hardware 0.0.9

A no_std bare-metal hardware abstraction layer — all port I/O, memory and swap allocations are guarded at runtime. Do not consider this dependency stable before x.1.x
Documentation
use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering};

static SMBIOS_BASE: AtomicUsize = AtomicUsize::new(0);
static SMBIOS_VERSION_MAJOR: AtomicU8 = AtomicU8::new(0);
static SMBIOS_VERSION_MINOR: AtomicU8 = AtomicU8::new(0);
static SMBIOS_TABLE_COUNT: AtomicUsize = AtomicUsize::new(0);
static SMBIOS_TABLE_ADDR: AtomicUsize = AtomicUsize::new(0);
static SMBIOS_TABLE_LEN: AtomicUsize = AtomicUsize::new(0);

#[derive(Copy, Clone)]
pub struct SmbiosHeader {
    pub entry_type: u8,
    pub length: u8,
    pub handle: u16,
}

#[derive(Copy, Clone)]
pub struct SmbiosInfo {
    pub base: usize,
    pub version_major: u8,
    pub version_minor: u8,
    pub table_count: usize,
}

#[derive(Copy, Clone)]
pub struct BiosInfo {
    pub vendor_str_idx: u8,
    pub version_str_idx: u8,
    pub release_date_str_idx: u8,
    pub rom_size_64k: u8,
    pub major_release: u8,
    pub minor_release: u8,
}

#[derive(Copy, Clone)]
pub struct CpuInfo {
    pub socket_str_idx: u8,
    pub processor_type: u8,
    pub processor_family: u8,
    pub processor_id: u64,
    pub max_speed_mhz: u16,
    pub current_speed_mhz: u16,
    pub core_count: u8,
    pub thread_count: u8,
    pub voltage: u8,
}

#[derive(Copy, Clone)]
pub struct MemModuleInfo {
    pub locator_str_idx: u8,
    pub size_mb: u16,
    pub speed_mhz: u16,
    pub memory_type: u8,
    pub form_factor: u8,
    pub data_width: u16,
    pub rank: u8,
}

fn smbios_read_u8(blob: &[u8], offset: usize) -> u8 {
    if offset < blob.len() {
        blob[offset]
    } else {
        0
    }
}

fn smbios_read_u16(blob: &[u8], offset: usize) -> u16 {
    if offset + 1 < blob.len() {
        u16::from_le_bytes([blob[offset], blob[offset + 1]])
    } else {
        0
    }
}

fn smbios_read_u64(blob: &[u8], offset: usize) -> u64 {
    if offset + 7 < blob.len() {
        u64::from_le_bytes([
            blob[offset],
            blob[offset + 1],
            blob[offset + 2],
            blob[offset + 3],
            blob[offset + 4],
            blob[offset + 5],
            blob[offset + 6],
            blob[offset + 7],
        ])
    } else {
        0
    }
}

fn skip_structure(blob: &[u8], offset: usize, header_len: usize) -> usize {
    let mut pos = offset + header_len;
    loop {
        if pos + 1 >= blob.len() {
            return blob.len();
        }
        if blob[pos] == 0 && blob[pos + 1] == 0 {
            return pos + 2;
        }
        pos += 1;
    }
}

pub fn parse_smbios() {
    let scan_regions: [(usize, usize); 2] =
        [(0x000F_0000, 0x000F_FFFF), (0x000E_0000, 0x000E_FFFF)];
    for (start, end) in scan_regions {
        let mut addr = start;
        while addr < end {
            let sig = crate::hardware_access::mmio_read32(addr).unwrap_or(0);
            if sig == 0x5F4D535F {
                SMBIOS_BASE.store(addr, Ordering::Release);
                let version_byte = crate::hardware_access::mmio_read32(addr + 6).unwrap_or(0);
                SMBIOS_VERSION_MAJOR.store((version_byte & 0xFF) as u8, Ordering::Release);
                SMBIOS_VERSION_MINOR.store(((version_byte >> 8) & 0xFF) as u8, Ordering::Release);
                let table_addr_raw = crate::hardware_access::mmio_read32(addr + 24).unwrap_or(0);
                let table_len =
                    crate::hardware_access::mmio_read32(addr + 22).unwrap_or(0) & 0xFFFF;
                SMBIOS_TABLE_ADDR.store(table_addr_raw as usize, Ordering::Release);
                SMBIOS_TABLE_LEN.store(table_len as usize, Ordering::Release);
                if table_addr_raw != 0 {
                    let mut offset: usize = 0;
                    let mut count: usize = 0;
                    while offset < table_len as usize {
                        let hdr_raw =
                            crate::hardware_access::mmio_read32(table_addr_raw as usize + offset)
                                .unwrap_or(0);
                        let length = ((hdr_raw >> 8) & 0xFF) as usize;
                        if length < 4 {
                            break;
                        }
                        offset += length;
                        loop {
                            let pair = crate::hardware_access::mmio_read32(
                                table_addr_raw as usize + offset,
                            )
                            .unwrap_or(0)
                                & 0xFFFF;
                            offset += 1;
                            if pair == 0 {
                                offset += 1;
                                break;
                            }
                        }
                        count += 1;
                    }
                    SMBIOS_TABLE_COUNT.store(count, Ordering::Release);
                }
                return;
            }
            addr += 16;
        }
    }
}

const SMBIOS_BLOB_MAX: usize = 16384;

fn read_table_blob(out: &mut [u8; SMBIOS_BLOB_MAX]) -> usize {
    let addr = SMBIOS_TABLE_ADDR.load(Ordering::Acquire);
    let len = SMBIOS_TABLE_LEN.load(Ordering::Acquire);
    if addr == 0 || len == 0 {
        return 0;
    }
    let n = if len > SMBIOS_BLOB_MAX {
        SMBIOS_BLOB_MAX
    } else {
        len
    };
    let mut i = 0;
    while i + 3 < n {
        let val = crate::hardware_access::mmio_read32(addr + i).unwrap_or(0);
        let bytes = val.to_le_bytes();
        out[i] = bytes[0];
        out[i + 1] = bytes[1];
        out[i + 2] = bytes[2];
        out[i + 3] = bytes[3];
        i += 4;
    }
    while i < n {
        let val = crate::hardware_access::mmio_read32(addr + i).unwrap_or(0);
        out[i] = val as u8;
        i += 1;
    }
    n
}

pub fn find_bios_info() -> Option<BiosInfo> {
    let mut blob = [0u8; SMBIOS_BLOB_MAX];
    let n = read_table_blob(&mut blob);
    if n < 4 {
        return None;
    }
    let mut off = 0;
    while off + 4 <= n {
        let entry_type = blob[off];
        let length = blob[off + 1] as usize;
        if length < 4 {
            break;
        }
        if entry_type == 0 && length >= 18 {
            let info = BiosInfo {
                vendor_str_idx: smbios_read_u8(&blob, off + 4),
                version_str_idx: smbios_read_u8(&blob, off + 5),
                release_date_str_idx: smbios_read_u8(&blob, off + 8),
                rom_size_64k: smbios_read_u8(&blob, off + 9),
                major_release: if length >= 22 {
                    smbios_read_u8(&blob, off + 20)
                } else {
                    0
                },
                minor_release: if length >= 23 {
                    smbios_read_u8(&blob, off + 21)
                } else {
                    0
                },
            };
            return Some(info);
        }
        off = skip_structure(&blob, off, length);
    }
    None
}

const MAX_CPU_ENTRIES: usize = 8;

pub fn find_cpu_info(out: &mut [CpuInfo; MAX_CPU_ENTRIES]) -> usize {
    let mut blob = [0u8; SMBIOS_BLOB_MAX];
    let n = read_table_blob(&mut blob);
    if n < 4 {
        return 0;
    }
    let mut off = 0;
    let mut count = 0;
    while off + 4 <= n && count < MAX_CPU_ENTRIES {
        let entry_type = blob[off];
        let length = blob[off + 1] as usize;
        if length < 4 {
            break;
        }
        if entry_type == 4 && length >= 26 {
            out[count] = CpuInfo {
                socket_str_idx: smbios_read_u8(&blob, off + 4),
                processor_type: smbios_read_u8(&blob, off + 5),
                processor_family: smbios_read_u8(&blob, off + 6),
                processor_id: smbios_read_u64(&blob, off + 8),
                max_speed_mhz: smbios_read_u16(&blob, off + 20),
                current_speed_mhz: smbios_read_u16(&blob, off + 22),
                core_count: if length >= 36 {
                    smbios_read_u8(&blob, off + 35)
                } else {
                    1
                },
                thread_count: if length >= 38 {
                    smbios_read_u8(&blob, off + 37)
                } else {
                    1
                },
                voltage: smbios_read_u8(&blob, off + 17),
            };
            count += 1;
        }
        off = skip_structure(&blob, off, length);
    }
    count
}

const MAX_MEM_MODULES: usize = 32;

pub fn find_memory_modules(out: &mut [MemModuleInfo; MAX_MEM_MODULES]) -> usize {
    let mut blob = [0u8; SMBIOS_BLOB_MAX];
    let n = read_table_blob(&mut blob);
    if n < 4 {
        return 0;
    }
    let mut off = 0;
    let mut count = 0;
    while off + 4 <= n && count < MAX_MEM_MODULES {
        let entry_type = blob[off];
        let length = blob[off + 1] as usize;
        if length < 4 {
            break;
        }
        if entry_type == 17 && length >= 21 {
            let raw_size = smbios_read_u16(&blob, off + 12);
            let size_mb = if raw_size == 0 || raw_size == 0xFFFF {
                0
            } else if raw_size & 0x8000 != 0 {
                (raw_size & 0x7FFF) / 1024
            } else {
                raw_size
            };
            out[count] = MemModuleInfo {
                locator_str_idx: smbios_read_u8(&blob, off + 16),
                size_mb,
                speed_mhz: if length >= 23 {
                    smbios_read_u16(&blob, off + 21)
                } else {
                    0
                },
                memory_type: if length >= 19 {
                    smbios_read_u8(&blob, off + 18)
                } else {
                    0
                },
                form_factor: smbios_read_u8(&blob, off + 14),
                data_width: smbios_read_u16(&blob, off + 10),
                rank: if length >= 28 {
                    smbios_read_u8(&blob, off + 27)
                } else {
                    0
                },
            };
            if size_mb > 0 {
                count += 1;
            }
        }
        off = skip_structure(&blob, off, length);
    }
    count
}

pub fn total_installed_ram_mb() -> u64 {
    let mut modules = [MemModuleInfo {
        locator_str_idx: 0,
        size_mb: 0,
        speed_mhz: 0,
        memory_type: 0,
        form_factor: 0,
        data_width: 0,
        rank: 0,
    }; MAX_MEM_MODULES];
    let n = find_memory_modules(&mut modules);
    let mut total: u64 = 0;
    let mut i = 0;
    while i < n {
        total += modules[i].size_mb as u64;
        i += 1;
    }
    total
}

pub fn smbios_info() -> SmbiosInfo {
    SmbiosInfo {
        base: SMBIOS_BASE.load(Ordering::Acquire),
        version_major: SMBIOS_VERSION_MAJOR.load(Ordering::Acquire),
        version_minor: SMBIOS_VERSION_MINOR.load(Ordering::Acquire),
        table_count: SMBIOS_TABLE_COUNT.load(Ordering::Acquire),
    }
}

pub fn is_present() -> bool {
    if SMBIOS_BASE.load(Ordering::Acquire) != 0 {
        return true;
    }
    parse_smbios();
    SMBIOS_BASE.load(Ordering::Acquire) != 0
}