From 8a8aff8917cb39742169573468720c8901a979a7 Mon Sep 17 00:00:00 2001 From: WangRunji Date: Wed, 18 Apr 2018 01:56:54 +0800 Subject: [PATCH] Copy start_ap to lapic.c . Simple startothers(). Try to boot AP but failed. --- src/arch/x86_64/boot/entryother.asm | 20 +++++----- src/arch/x86_64/boot/linker.ld | 28 ++++++-------- src/arch/x86_64/driver/apic/lapic.c | 57 ++++++++++++++++++++++++++++ src/arch/x86_64/driver/apic/lapic.rs | 8 ++++ src/arch/x86_64/driver/apic/mod.rs | 2 +- src/arch/x86_64/driver/mod.rs | 3 +- src/arch/x86_64/interrupt/irq.rs | 4 +- src/arch/x86_64/mod.rs | 1 + src/arch/x86_64/smp.rs | 47 +++++++++++++++++++++++ src/lib.rs | 4 +- src/memory/mod.rs | 6 +++ 11 files changed, 150 insertions(+), 30 deletions(-) create mode 100644 src/arch/x86_64/smp.rs diff --git a/src/arch/x86_64/boot/entryother.asm b/src/arch/x86_64/boot/entryother.asm index 3eeb9fd..2b03e32 100644 --- a/src/arch/x86_64/boot/entryother.asm +++ b/src/arch/x86_64/boot/entryother.asm @@ -27,11 +27,14 @@ %define STA_R 0x2 ; Readable (executable segments) %define STA_A 0x1 ; Accessed -global otherstart +global entryother_start +global entryother_end + +entryother_start: section .text bits 16 -otherstart: +start: cli xor ax, ax @@ -62,9 +65,9 @@ start32: mov ebx, 1 ; Switch to the stack allocated by startothers() - mov esp, [otherstart-4] + mov esp, [start-4] ; Call mpenter() - call [otherstart-8] + call [start-8] mov ax, 0x8a00 mov dx, ax @@ -74,10 +77,7 @@ start32: spin: jmp spin -section .bss - resb 4096 - -section .rodata +; section .rodata align 4 gdt: ; NULL @@ -91,4 +91,6 @@ gdt: db 0, (0x90 | STA_W), 0xcf, 0 .desc: dw ($ - gdt - 1) - dq gdt \ No newline at end of file + dq gdt + +entryother_end: \ No newline at end of file diff --git a/src/arch/x86_64/boot/linker.ld b/src/arch/x86_64/boot/linker.ld index 3abbc38..13587d8 100644 --- a/src/arch/x86_64/boot/linker.ld +++ b/src/arch/x86_64/boot/linker.ld @@ -6,23 +6,6 @@ KERNEL_OFFSET = 0xffffff0000000000; SECTIONS { - . = OTHER_OFFSET - 0x1000; - .bss.other : - { - KEEP(*/entryother.o (.bss)) - . = ALIGN(4K); - } - .text.other : - { - KEEP(*/entryother.o (.text)) - . = ALIGN(4K); - } - .rodata.other : - { - KEEP(*/entryother.o (.rodata)) - . = ALIGN(4K); - } - . = BOOT_OFFSET; .rodata.32 : @@ -44,6 +27,17 @@ SECTIONS { . = ALIGN(4K); } + /* bootloader for other processors */ + + real = .; /* backup va */ + . = OTHER_OFFSET; + .text.other : AT(real) + { + KEEP(*/entryother.o (.text)) + . = ALIGN(4K); + } + . = . - OTHER_OFFSET + real; /* recover va */ + . += KERNEL_OFFSET; .rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) diff --git a/src/arch/x86_64/driver/apic/lapic.c b/src/arch/x86_64/driver/apic/lapic.c index f0eecfb..b01b1b8 100644 --- a/src/arch/x86_64/driver/apic/lapic.c +++ b/src/arch/x86_64/driver/apic/lapic.c @@ -2,6 +2,17 @@ // See Chapter 8 & Appendix C of Intel processor manual volume 3. typedef unsigned int uint; +typedef unsigned char uchar; +typedef unsigned short ushort; + +static inline void +outb(ushort port, uchar data) +{ + asm volatile("out %0,%1" : : "a" (data), "d" (port)); +} + +#define KERNBASE 0xFFFFFF0000000000 // First kernel virtual address +#define P2V(a) (((void *) (a)) + KERNBASE) #define T_IRQ0 32 // IRQ 0 corresponds to int T_IRQ @@ -105,4 +116,50 @@ lapiceoi(void) { if(lapic) lapicw(EOI, 0); +} + +// Spin for a given number of microseconds. +// On real hardware would want to tune this dynamically. +void +microdelay(int us) +{ +} + +#define CMOS_PORT 0x70 +#define CMOS_RETURN 0x71 + +// Start additional processor running entry code at addr. +// See Appendix B of MultiProcessor Specification. +void +lapicstartap(uchar apicid, uint addr) +{ + int i; + ushort *wrv; + + // "The BSP must initialize CMOS shutdown code to 0AH + // and the warm reset vector (DWORD based at 40:67) to point at + // the AP startup code prior to the [universal startup algorithm]." + outb(CMOS_PORT, 0xF); // offset 0xF is shutdown code + outb(CMOS_PORT+1, 0x0A); + wrv = (ushort*)P2V((0x40<<4 | 0x67)); // Warm reset vector + wrv[0] = 0; + wrv[1] = addr >> 4; + + // "Universal startup algorithm." + // Send INIT (level-triggered) interrupt to reset other CPU. + lapicw(ICRHI, apicid<<24); + lapicw(ICRLO, INIT | LEVEL | ASSERT); + microdelay(200); + lapicw(ICRLO, INIT | LEVEL); + microdelay(10000); + + // Send startup IPI (twice!) to enter code. + // Regular hardware is supposed to only accept a STARTUP + // when it is in the halted state due to an INIT. So the second + // should be ignored, but it is part of the official Intel algorithm. + for(i = 0; i < 2; i++){ + lapicw(ICRHI, apicid<<24); + lapicw(ICRLO, STARTUP | (addr>>12)); + microdelay(200); + } } \ No newline at end of file diff --git a/src/arch/x86_64/driver/apic/lapic.rs b/src/arch/x86_64/driver/apic/lapic.rs index b7b0ebb..4331028 100644 --- a/src/arch/x86_64/driver/apic/lapic.rs +++ b/src/arch/x86_64/driver/apic/lapic.rs @@ -2,6 +2,7 @@ extern { static mut lapic: *const (); fn lapicinit(); // must set `lapic` first fn lapiceoi(); // ack + fn lapicstartap(apicid: u8, addr: u32); } pub fn init(lapic_addr: *const ()) { @@ -17,4 +18,11 @@ pub fn ack(_irq: u8) { unsafe { lapiceoi(); } +} + +pub fn start_ap(apicid: u8, addr: u32) { + debug!("WARNING: lapic::start_ap use C lib"); + unsafe { + lapicstartap(apicid, addr); + } } \ No newline at end of file diff --git a/src/arch/x86_64/driver/apic/mod.rs b/src/arch/x86_64/driver/apic/mod.rs index 27b0f35..c03d30d 100644 --- a/src/arch/x86_64/driver/apic/mod.rs +++ b/src/arch/x86_64/driver/apic/mod.rs @@ -1,5 +1,5 @@ pub use self::ioapic::IOAPIC; -pub use self::lapic::ack; +pub use self::lapic::{ack, start_ap}; mod lapic; mod ioapic; diff --git a/src/arch/x86_64/driver/mod.rs b/src/arch/x86_64/driver/mod.rs index 5535ab8..a6cf151 100644 --- a/src/arch/x86_64/driver/mod.rs +++ b/src/arch/x86_64/driver/mod.rs @@ -7,7 +7,7 @@ pub mod pic; pub mod keyboard; pub mod pit; -pub fn init(mut page_map: F) +pub fn init(mut page_map: F) -> acpi::ACPI_Result where F: FnMut(usize) { assert_has_not_been_called!(); @@ -35,4 +35,5 @@ pub fn init(mut page_map: F) pit::init(); serial::init(); keyboard::init(); + acpi } \ No newline at end of file diff --git a/src/arch/x86_64/interrupt/irq.rs b/src/arch/x86_64/interrupt/irq.rs index 087e3d1..1b5523b 100644 --- a/src/arch/x86_64/interrupt/irq.rs +++ b/src/arch/x86_64/interrupt/irq.rs @@ -16,7 +16,9 @@ pub extern "x86-interrupt" fn double_fault_handler( pub extern "x86-interrupt" fn page_fault_handler( stack_frame: &mut ExceptionStackFrame, error_code: PageFaultErrorCode) { - println!("\nEXCEPTION: PAGE FAULT\n{:#?}\n{:#?}", stack_frame, error_code); + use x86_64::registers::control_regs::cr2; + println!("\nEXCEPTION: PAGE FAULT\n{:#?}\nErrorCode: {:#?}\nAddress: {:#x}", + stack_frame, error_code, cr2()); loop {} } diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index b110f0f..d1200ba 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -4,6 +4,7 @@ pub mod interrupt; pub mod paging; pub mod gdt; pub mod idt; +pub mod smp; pub fn init() { cpu::enable_nxe_bit(); diff --git a/src/arch/x86_64/smp.rs b/src/arch/x86_64/smp.rs new file mode 100644 index 0000000..0873f3d --- /dev/null +++ b/src/arch/x86_64/smp.rs @@ -0,0 +1,47 @@ +use arch::driver::{acpi::ACPI_Result, apic::start_ap}; +use memory::{MemoryController, PhysicalAddress}; + +extern { + fn entryother_start(); + fn entryother_end(); +} + +const ENTRYOTHER_ADDR: u32 = 0x7000; + +pub fn start_other_cores(acpi: &ACPI_Result, mc: &mut MemoryController) { + mc.map_page_identity(ENTRYOTHER_ADDR as usize - 1); + mc.map_page_identity(ENTRYOTHER_ADDR as usize); + mc.map_page_p2v(PhysicalAddress(0)); + copy_entryother(); + let args = unsafe{ &mut *(ENTRYOTHER_ADDR as *mut EntryArgs).offset(-1) }; + for i in 1 .. acpi.cpu_num { + let apic_id = acpi.cpu_acpi_ids[i as usize]; + *args = EntryArgs { + kstack: mc.alloc_stack(1).unwrap().top() as u64, + next_code: 0, + stack: 0x8000, // just enough stack to get us to entry64mp + }; + start_ap(apic_id, ENTRYOTHER_ADDR); + } + +} + +fn hello_world() { + println!("Hello world!"); +} + +fn copy_entryother() { + use rlibc::memmove; + let entryother_start = entryother_start as usize; + let entryother_end = entryother_end as usize; + let size = entryother_end - entryother_start; + unsafe{ memmove(ENTRYOTHER_ADDR as *mut u8, entryother_start as *mut u8, size); } + debug!("smp: copied entryother code to 0x7000"); +} + +#[repr(C)] +struct EntryArgs { + kstack: u64, + next_code: u32, + stack: u32, +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index cec60c3..b1e18ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,8 +69,10 @@ pub extern "C" fn rust_main(multiboot_information_address: usize) { test!(guard_page); test!(find_mp); - arch::driver::init(|addr: usize| memory_controller.map_page_identity(addr)); + let acpi = arch::driver::init( + |addr: usize| memory_controller.map_page_identity(addr)); // memory_controller.print_page_table(); + arch::smp::start_other_cores(&acpi, &mut memory_controller); unsafe{ arch::interrupt::enable(); } loop{} diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 03d16af..517ec9d 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -164,6 +164,12 @@ impl MemoryController { let flags = EntryFlags::WRITABLE; self.active_table.identity_map(frame, flags, &mut self.frame_allocator); } + pub fn map_page_p2v(&mut self, addr: PhysicalAddress) { + let page = Page::containing_address(addr.to_kernel_virtual()); + let frame = Frame::containing_address(addr.get()); + let flags = EntryFlags::WRITABLE; + self.active_table.map_to(page, frame, flags, &mut self.frame_allocator); + } pub fn print_page_table(&self) { debug!("{:?}", self.active_table); }