From 936c485ab9034e11fdb49a9670d6fd4bc6a7b5cc Mon Sep 17 00:00:00 2001 From: WangRunji Date: Thu, 5 Apr 2018 22:36:39 +0800 Subject: [PATCH] Port MP & RSDP detecting --- Makefile | 2 +- src/arch/x86_64/driver/acpi/mod.rs | 18 ++++ src/arch/x86_64/driver/acpi/structs.rs | 76 +++++++++++++++++ src/arch/x86_64/driver/ioapic.rs | 76 +++++++++++++++++ src/arch/x86_64/driver/mod.rs | 5 +- src/arch/x86_64/driver/mp/mod.rs | 113 +++++++++++++++++++++++++ src/arch/x86_64/driver/mp/structs.rs | 74 ++++++++++++++++ src/lib.rs | 9 ++ src/util.rs | 20 +++++ 9 files changed, 391 insertions(+), 2 deletions(-) create mode 100644 src/arch/x86_64/driver/acpi/mod.rs create mode 100644 src/arch/x86_64/driver/acpi/structs.rs create mode 100644 src/arch/x86_64/driver/ioapic.rs create mode 100644 src/arch/x86_64/driver/mp/mod.rs create mode 100644 src/arch/x86_64/driver/mp/structs.rs create mode 100644 src/util.rs diff --git a/Makefile b/Makefile index 32170c0..75bc6f8 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ clean: @rm -r build run: $(iso) - @qemu-system-$(arch) -cdrom $(iso) + @qemu-system-$(arch) -cdrom $(iso) -smp 2 iso: $(iso) diff --git a/src/arch/x86_64/driver/acpi/mod.rs b/src/arch/x86_64/driver/acpi/mod.rs new file mode 100644 index 0000000..90381fe --- /dev/null +++ b/src/arch/x86_64/driver/acpi/mod.rs @@ -0,0 +1,18 @@ +// Migrate from xv6-x86_64 acpi.c + +mod structs; +use self::structs::*; + +/// See https://wiki.osdev.org/RSDP -- Detecting the RSDP +pub fn find_rdsp() -> Option<*const rsdp> { + use util::{Checkable, find_in_memory}; + let ebda = unsafe { *(0x40E as *const u16) as usize } << 4; + println!("EBDA at {:#x}", ebda); + if ebda != 0 { + if let Some(addr) = unsafe{ find_in_memory::(ebda as usize, 1024, 4) } { + return Some(addr as *const rsdp); + } + } + unsafe{ find_in_memory::(0xE0000, 0x20000, 4).map(|addr| addr as *const rsdp) } +} + diff --git a/src/arch/x86_64/driver/acpi/structs.rs b/src/arch/x86_64/driver/acpi/structs.rs new file mode 100644 index 0000000..96c55fb --- /dev/null +++ b/src/arch/x86_64/driver/acpi/structs.rs @@ -0,0 +1,76 @@ +// Reference: xv6-x86_64 acpi.h +// Copy from crate 'acpica-sys' + +use util::{Checkable, bytes_sum}; + +#[repr(C)] +#[derive(Debug)] +pub struct rsdp { + pub Signature: [u8; 8], + pub Checksum: u8, + pub OemId: [i8; 6], + pub Revision: u8, + pub RsdtPhysicalAddress: u32, + pub Length: u32, + pub XsdtPhysicalAddress: u64, + pub ExtendedChecksum: u8, + pub Reserved: [u8; 3], +} + +impl Checkable for rsdp { + fn check(&self) -> bool { + &self.Signature == b"RSD PTR " && bytes_sum(self) == 0 + } +} + +#[repr(C)] +#[derive(Debug)] +pub struct header { + pub Signature: [i8; 4], + pub Length: u32, + pub Revision: u8, + pub Checksum: u8, + pub OemId: [i8; 6], + pub OemTableId: [i8; 8], + pub OemRevision: u32, + pub AslCompilerId: [i8; 4], + pub AslCompilerRevision: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct rsdt { + pub Header: header, + pub TableOffsetEntry: [u32; 1], +} + +#[repr(C)] +#[derive(Debug)] +pub struct madt { + pub Header: header, + pub Address: u32, + pub Flags: u32, +} + +const MADT_SIGNATURE: [u8; 4] = *b"APIC"; + +#[repr(C)] +#[derive(Debug)] +pub struct madt_local_apic { + pub Type: u8, + pub Length: u8, + pub ProcessorId: u8, + pub Id: u8, + pub LapicFlags: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct madt_io_apic { + pub Type: u8, + pub Length: u8, + pub Id: u8, + pub Reserved: u8, + pub Address: u32, + pub GlobalIrqBase: u32, +} \ No newline at end of file diff --git a/src/arch/x86_64/driver/ioapic.rs b/src/arch/x86_64/driver/ioapic.rs new file mode 100644 index 0000000..992c53b --- /dev/null +++ b/src/arch/x86_64/driver/ioapic.rs @@ -0,0 +1,76 @@ +// Migrate from xv6 ioapic.c + +/// The I/O APIC manages hardware interrupts for an SMP system. +/// http://www.intel.com/design/chipsets/datashts/29056601.pdf +/// See also picirq.c. + +use core::ptr::{read_volatile, write_volatile}; + +pub fn init() { + +} + +const IOAPIC_ADDRESS : u32 = 0xFEC00000; // Default physical address of IO APIC + +const REG_ID : u32 = 0x00; // Register index: ID +const REG_VER : u32 = 0x01; // Register index: version +const REG_TABLE : u32 = 0x10; // Redirection table base + +// The redirection table starts at REG_TABLE and uses +// two registers to configure each interrupt. +// The first (low) register in a pair contains configuration bits. +// The second (high) register contains a bitmask telling which +// CPUs can serve that interrupt. +const INT_DISABLED : u32 = 0x00010000; // Interrupt disabled +const INT_LEVEL : u32 = 0x00008000; // Level-triggered (vs edge-) +const INT_ACTIVELOW : u32 = 0x00002000; // Active low (vs high) +const INT_LOGICAL : u32 = 0x00000800; // Destination is CPU id (vs APIC ID) + +// static IOAPIC: *mut IoApic = IOAPIC_ADDRESS as *mut _; + +const ioapicid: u32 = 0; // TODO fix +const T_IRQ0: u32 = 32; + +// IO APIC MMIO structure: write reg, then read or write data. +#[repr(C)] +struct IoApic { + reg: u32, + pad: [u32; 3], + data: u32, +} + +impl IoApic { + unsafe fn read(&mut self, reg: u32) -> u32 + { + write_volatile(&mut self.reg as *mut _, reg); + read_volatile(&self.data as *const _) + } + unsafe fn write(&mut self, reg: u32, data: u32) + { + write_volatile(&mut self.reg as *mut _, reg); + write_volatile(&mut self.data as *mut _, data); + } + unsafe fn init(&mut self) + { + let maxintr = (self.read(REG_VER) >> 16) & 0xFF; + let id = self.read(REG_ID) >> 24; + if id != ioapicid { + println!("ioapicinit: id isn't equal to ioapicid; not a MP"); + } + + // Mark all interrupts edge-triggered, active high, disabled, + // and not routed to any CPUs. + for i in 0 .. maxintr+1 { + self.write(REG_TABLE+2*i, INT_DISABLED | (T_IRQ0 + i)); + self.write(REG_TABLE+2*i+1, 0); + } + } + unsafe fn enable(&mut self, irq: u32, cpunum: u32) + { + // Mark interrupt edge-triggered, active high, + // enabled, and routed to the given cpunum, + // which happens to be that cpu's APIC ID. + self.write(REG_TABLE+2*irq, T_IRQ0 + irq); + self.write(REG_TABLE+2*irq+1, cpunum << 24); + } +} \ No newline at end of file diff --git a/src/arch/x86_64/driver/mod.rs b/src/arch/x86_64/driver/mod.rs index 9b00c35..1f1f8fb 100644 --- a/src/arch/x86_64/driver/mod.rs +++ b/src/arch/x86_64/driver/mod.rs @@ -1 +1,4 @@ -pub mod vga; \ No newline at end of file +pub mod vga; +pub mod acpi; +pub mod ioapic; +pub mod mp; \ No newline at end of file diff --git a/src/arch/x86_64/driver/mp/mod.rs b/src/arch/x86_64/driver/mp/mod.rs new file mode 100644 index 0000000..a6a587a --- /dev/null +++ b/src/arch/x86_64/driver/mp/mod.rs @@ -0,0 +1,113 @@ +// Migrate from xv6 mp.c + +// Multiprocessor support +// Search memory for MP description structures. +// http://developer.intel.com/design/pentium/datashts/24201606.pdf + +mod structs; +use self::structs::*; + +/// Search for the MP Floating Pointer Structure, which according to the +/// spec is in one of the following three locations: +/// 1) in the first KB of the EBDA; +/// 2) in the last KB of system base memory; +/// 3) in the BIOS ROM address space between 0F0000h and 0FFFFFh. +pub fn find_mp() -> Option<*const MP> +{ + use core::mem::size_of; + use util::find_in_memory; + let ebda = unsafe { *(0x40E as *const u16) as usize } << 4; + if ebda != 0 { + let res = unsafe{ find_in_memory::(ebda, 1024, size_of::()) }; + if let Some(addr) = res { + return Some(addr as *const MP); + } + } + let p = unsafe { *(0x413 as *const u16) as usize } << 10; + let res = unsafe{ find_in_memory::(p-1024, 1024, size_of::()) }; + if let Some(addr) = res { + return Some(addr as *const MP); + } + let res = unsafe{ find_in_memory::(0xF0000, 0x10000, size_of::()) }; + res.map(|addr| addr as *const MP) +} + +/* +struct cpu cpus[NCPU]; +int ncpu; +uchar ioapicid; + +// Search for an MP configuration table. For now, +// don't accept the default configurations (physaddr == 0). +// Check for correct signature, calculate the checksum and, +// if correct, check the version. +// To do: check extended table checksum. +static struct mpconf* +mpconfig(struct mp **pmp) +{ + struct mpconf *conf; + struct mp *mp; + + if((mp = mpsearch()) == 0 || mp->physaddr == 0) + return 0; + conf = (struct mpconf*) P2V((uint) mp->physaddr); + if(memcmp(conf, "PCMP", 4) != 0) + return 0; + if(conf->version != 1 && conf->version != 4) + return 0; + if(sum((uchar*)conf, conf->length) != 0) + return 0; + *pmp = mp; + return conf; +} + +void +mpinit(void) +{ + uchar *p, *e; + int ismp; + struct mp *mp; + struct mpconf *conf; + struct mpproc *proc; + struct mpioapic *ioapic; + + if((conf = mpconfig(&mp)) == 0) + panic("Expect to run on an SMP"); + ismp = 1; + lapic = (uint*)conf->lapicaddr; + for(p=(uchar*)(conf+1), e=(uchar*)conf+conf->length; papicid; // apicid may differ from ncpu + ncpu++; + } + p += sizeof(struct mpproc); + continue; + case MPIOAPIC: + ioapic = (struct mpioapic*)p; + ioapicid = ioapic->apicno; + p += sizeof(struct mpioapic); + continue; + case MPBUS: + case MPIOINTR: + case MPLINTR: + p += 8; + continue; + default: + ismp = 0; + break; + } + } + if(!ismp) + panic("Didn't find a suitable machine"); + + if(mp->imcrp){ + // Bochs doesn't support IMCR, so this doesn't run on Bochs. + // But it would on real hardware. + outb(0x22, 0x70); // Select IMCR + outb(0x23, inb(0x23) | 1); // Mask external interrupts. + } +} +*/ \ No newline at end of file diff --git a/src/arch/x86_64/driver/mp/structs.rs b/src/arch/x86_64/driver/mp/structs.rs new file mode 100644 index 0000000..a3acd68 --- /dev/null +++ b/src/arch/x86_64/driver/mp/structs.rs @@ -0,0 +1,74 @@ +// Migrate from xv6 mp.h + +// See MultiProcessor Specification Version 1.[14] + +use util::{Checkable, bytes_sum}; + +#[repr(C)] +#[derive(Debug)] +pub struct MP { // floating pointer + signature: [u8; 4], // "_MP_" + physaddr: u32, // phys addr of MP config table + length: u8, // 1 + specrev: u8, // [14] + checksum: u8, // all bytes must add up to 0 + type_: u8, // MP system config type + imcrp: u8, + reserved: [u8; 3], +} + +impl Checkable for MP { + fn check(&self) -> bool { + &self.signature == b"_MP_" && bytes_sum(self) == 0 + } +} + +/* + +#[repr(C)] +struct mpconf { // configuration table header + signature: [byte; 4]; // "PCMP" + length: u16, // total table length + version: u8, // [14] + checksum: u8, // all bytes must add up to 0 + product: [u8; 20], // product id + uint *oemtable, // OEM table pointer + ushort oemlength, // OEM table length + ushort entry, // entry count + uint *lapicaddr, // address of local APIC + ushort xlength, // extended table length + u8 xchecksum, // extended table checksum + u8 reserved, +} + +#[repr(C)] +struct mpproc { // processor table entry + u8 type; // entry type (0) + u8 apicid; // local APIC id + u8 version; // local APIC verison + u8 flags; // CPU flags + #define MPBOOT 0x02 // This proc is the bootstrap processor. + u8 signature[4]; // CPU signature + uint feature; // feature flags from CPUID instruction + u8 reserved[8]; +} + +struct mpioapic { // I/O APIC table entry + u8 type; // entry type (2) + u8 apicno; // I/O APIC id + u8 version; // I/O APIC version + u8 flags; // I/O APIC flags + uint *addr; // I/O APIC address +} + +// Table entry types +const MPPROC : u8 = 0x00; // One per processor +const MPBUS : u8 = 0x01; // One per bus +const MPIOAPIC : u8 = 0x02; // One per I/O APIC +const MPIOINTR : u8 = 0x03; // One per bus interrupt source +const MPLINTR : u8 = 0x04; // One per system interrupt source + +//PAGEBREAK! +// Blank page. + +*/ diff --git a/src/lib.rs b/src/lib.rs index 35e44c3..3139708 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ #![feature(allocator_api)] #![feature(global_allocator)] #![feature(abi_x86_interrupt)] +#![feature(iterator_step_by)] #![no_std] @@ -32,6 +33,8 @@ mod io; mod memory; mod interrupts; mod lang; +#[macro_use] +mod util; #[allow(dead_code)] #[cfg(target_arch = "x86_64")] @@ -42,6 +45,12 @@ mod arch; #[no_mangle] pub extern "C" fn rust_main(multiboot_information_address: usize) { // ATTENTION: we have a very small stack and no guard page + println!("MP = {:?}", arch::driver::mp::find_mp()); + println!("RDSP = {:?}", arch::driver::acpi::find_rdsp()); + loop { + + } + arch::driver::ioapic::init(); io::init(); println!("Hello World{}", "!"); diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..ed541f0 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,20 @@ +pub fn bytes_sum(p: &T) -> u8 { + use core::mem::size_of_val; + let len = size_of_val(p); + let p = p as *const T as *const u8; + (0..len).map(|i| unsafe { &*p.offset(i as isize) }) + .fold(0, |a, &b| a.overflowing_add(b).0) +} + +/// +pub trait Checkable { + fn check(&self) -> bool; +} + +/// Scan memory to find the struct +pub unsafe fn find_in_memory + (begin: usize, len: usize, step: usize) -> Option { + + (begin .. begin + len).step_by(step) + .find(|&addr| { (&*(addr as *const T)).check() }) +}