use super::{ArchInfo, tables::arm_uarch};
pub struct Midr {
pub implementer: u8,
pub part: u32,
pub variant: Option<u32>,
pub revision: Option<u32>,
}
pub fn decode_midr(midr: u32) -> Midr {
Midr {
implementer: ((midr >> 24) & 0xFF) as u8,
part: (midr >> 4) & 0xFFF,
variant: Some((midr >> 20) & 0xF),
revision: Some(midr & 0xF),
}
}
fn implementer_vendor(implementer: u8) -> &'static str {
match implementer {
b'A' => "ARM",
b'B' => "Broadcom",
b'C' => "Cavium",
b'H' => "HiSilicon",
b'N' => "Nvidia",
b'P' => "APM",
b'Q' => "Qualcomm",
b'S' => "Samsung",
b'V' => "Marvell",
b'a' => "Apple",
b'F' => "Fujitsu",
b'M' => "Ampere",
_ => "Unknown",
}
}
fn lookup_uarch(midr: &Midr) -> Option<(&'static str, &'static str)> {
arm_uarch::ARM_UARCH
.iter()
.find_map(|&(im, part, variant, revision, name, vendor)| {
let ok = im == midr.implementer
&& part == midr.part
&& variant.is_none_or(|v| Some(v) == midr.variant)
&& revision.is_none_or(|v| Some(v) == midr.revision);
ok.then_some((name, vendor))
})
}
pub fn detect_from_midr(midr_raw: u32) -> ArchInfo {
let midr = decode_midr(midr_raw);
match lookup_uarch(&midr) {
Some((name, vendor)) => ArchInfo {
vendor: Some(vendor.to_string()),
uarch: Some(name.to_string()),
soc: None,
process_nm: None,
features: Vec::new(),
},
None => ArchInfo {
vendor: Some(implementer_vendor(midr.implementer).to_string()),
uarch: None,
soc: None,
process_nm: None,
features: Vec::new(),
},
}
}
pub static SOC_HINTS: &[(&str, &str)] = &[
("bcm2712", "Broadcom BCM2712 (Raspberry Pi 5)"),
("bcm2711", "Broadcom BCM2711 (Raspberry Pi 4)"),
("bcm2837", "Broadcom BCM2837 (Raspberry Pi 3)"),
("bcm2835", "Broadcom BCM2835 (Raspberry Pi 1/Zero)"),
("sm8650", "Qualcomm Snapdragon 8 Gen 3"),
("sm8550", "Qualcomm Snapdragon 8 Gen 2"),
("sm8450", "Qualcomm Snapdragon 8 Gen 1"),
("exynos2400", "Samsung Exynos 2400"),
("exynos990", "Samsung Exynos 990"),
("mt6989", "MediaTek Dimensity 9300"),
("mt6985", "MediaTek Dimensity 9200"),
("rk3588", "Rockchip RK3588"),
("rk3399", "Rockchip RK3399"),
("ampereone", "Ampere AmpereOne"),
("ampere altra", "Ampere Altra"),
("thunderx", "Marvell/Cavium ThunderX"),
("kunpeng", "HiSilicon Kunpeng"),
];
pub fn soc_from_hint(haystack: &str) -> Option<&'static str> {
let lower = haystack.to_ascii_lowercase();
SOC_HINTS
.iter()
.find(|(needle, _)| lower.contains(needle))
.map(|(_, name)| *name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decodes_cortex_a76_midr() {
let midr_raw: u32 = (0x41 << 24) | (0xD0B << 4) | 0x1;
let info = detect_from_midr(midr_raw);
assert_eq!(info.uarch.as_deref(), Some("Cortex-A76"));
assert_eq!(info.vendor.as_deref(), Some("ARM"));
}
#[test]
fn unknown_part_falls_back_to_implementer_vendor_only() {
let midr_raw: u32 = (0x51 << 24) | (0xFFF << 4); let info = detect_from_midr(midr_raw);
assert_eq!(info.uarch, None);
assert_eq!(info.vendor.as_deref(), Some("Qualcomm"));
}
#[test]
fn soc_hint_matches_case_insensitively() {
assert_eq!(
soc_from_hint("Raspberry Pi 5 (BCM2712)"),
Some("Broadcom BCM2712 (Raspberry Pi 5)")
);
assert_eq!(soc_from_hint("totally unknown board"), None);
}
}