/* SPDX-License-Identifier: MIT */ #include "firmware.h" #include "adt.h" #include "stdint.h" #include "string.h" #include "types.h" #include "utils.h" #include "libfdt/libfdt.h" #include "libfdt/libfdt_env.h" struct fw_version_info os_firmware; struct fw_version_info system_firmware; #define bail(...) \ do { \ printf(__VA_ARGS__); \ return -1; \ } while (0) const struct fw_version_info fw_versions[NUM_FW_VERSIONS] = { // clang-format off [V_UNKNOWN] = {V_UNKNOWN, "unknown", {0}, 1, "unknown"}, [V12_1] = {V12_1, "12.1", {12, 1, 0}, 3, "iBoot-7429.61.2"}, [V12_2] = {V12_2, "12.2", {12, 2, 0}, 3, "iBoot-7429.81.3"}, [V12_3] = {V12_3, "12.3", {12, 3, 0}, 3, "iBoot-7459.101.2"}, [V12_3_1] = {V12_3_1, "12.3.1", {12, 3, 1}, 3, "iBoot-7459.101.3"}, [V12_4] = {V12_4, "12.4", {12, 4, 0}, 3, "iBoot-7459.121.3"}, [V12_5] = {V12_5, "12.5", {12, 5, 0}, 3, "iBoot-7459.141.1"}, // Same as 12.5 // {V12_6, "12.6", {12, 6, 0}, 3, "iBoot-7459.141.1"}, [V13_0B4] = {V13_0B4, "13.0 beta4", {12, 99, 4}, 3, "iBoot-8419.0.151.0.1"}, [V13_0] = {V13_0, "13.0", {13, 0, 0}, 3, "iBoot-8419.41.10"}, [V13_1] = {V13_1, "13.1", {13, 1, 0}, 3, "iBoot-8419.60.44"}, [V13_2] = {V13_2, "13.2", {13, 2, 0}, 3, "iBoot-8419.80.7"}, [V13_3] = {V13_3, "13.3", {13, 3, 0}, 3, "iBoot-8422.100.650"}, [V13_5B4] = {V13_5B4, "13.5 beta4", {13, 4, 99, 4}, 4, "iBoot-8422.140.50.0.2"}, [V13_5] = {V13_5, "13.5", {13, 5, 0}, 3, "iBoot-8422.141.2"}, [V13_6_2] = {V13_6_2, "13.6.2", {13, 6, 2}, 3, "iBoot-8422.141.2.700.1"}, [V14_1_1] = {V14_1_1, "14.1.1", {14, 1, 1}, 3, "iBoot-10151.41.12"}, [V15_0B1] = {V15_0B1, "15.0 beta", {14, 99, 1}, 3, "iBoot-11881.0.80.0.2"}, [V15_0] = {V15_0, "15.0", {15, 0, 0}, 3, "iBoot-11881.1.1"}, // clang-format on }; int firmware_set_fdt(void *fdt, int node, const char *prop, const struct fw_version_info *ver) { fdt32_t data[ARRAY_SIZE(ver->num)]; for (size_t i = 0; i < ver->num_length; i++) { data[i] = cpu_to_fdt32(ver->num[i]); } if (fdt_setprop(fdt, node, prop, data, ver->num_length * sizeof(u32))) bail("FDT: couldn't set %s property to firmware info", prop); return 0; } static void parse_version(const char *s, u32 *out) { memset(out, 0, sizeof(*out) * IBOOT_VER_COMP); for (int i = 0; i < IBOOT_VER_COMP; i++) { while (*s && !(*s >= '0' && *s <= '9')) s++; if (!*s) break; out[i] = atol(s); while (*s >= '0' && *s <= '9') s++; } } static void detect_firmware(struct fw_version_info *info, const char *ver) { for (size_t i = 0; i < ARRAY_SIZE(fw_versions); i++) { if (!strcmp(fw_versions[i].iboot, ver)) { *info = fw_versions[i]; return; } } *info = fw_versions[V_UNKNOWN]; info->iboot = ver; } // Note: semi-open range bool firmware_sfw_in_range(enum fw_version lower_bound, enum fw_version upper_bound) { u32 min[IBOOT_VER_COMP] = {0}; u32 max[IBOOT_VER_COMP] = {UINT32_MAX}; u32 this[IBOOT_VER_COMP] = {0}; if (lower_bound > V_UNKNOWN && lower_bound < NUM_FW_VERSIONS) parse_version(fw_versions[lower_bound].iboot, min); if (upper_bound > V_UNKNOWN && upper_bound < NUM_FW_VERSIONS) parse_version(fw_versions[upper_bound].iboot, max); parse_version(system_firmware.iboot, this); int i; for (i = 0; i < IBOOT_VER_COMP; i++) if (this[i] != min[i]) break; if (this[i] < min[i]) return false; for (i = 0; i < IBOOT_VER_COMP; i++) if (this[i] != max[i]) break; return this[i] < max[i]; } int firmware_init(void) { int node = adt_path_offset(adt, "/chosen"); if (node < 0) { printf("ADT: no /chosen found\n"); return -1; } u32 len; const char *p = adt_getprop(adt, node, "firmware-version", &len); if (p && len && p[len - 1] == 0) { detect_firmware(&os_firmware, p); printf("OS FW version: %s (%s)\n", os_firmware.string, os_firmware.iboot); } else { printf("ADT: failed to find firmware-version\n"); return -1; } p = adt_getprop(adt, node, "system-firmware-version", &len); if (p && len && p[len - 1] == 0) { detect_firmware(&system_firmware, p); printf("System FW version: %s (%s)\n", system_firmware.string, system_firmware.iboot); } else { printf("ADT: failed to find system-firmware-version\n"); return -1; } return 0; }