From 952ca6efb6ff45a6b821ebb1435003f6955e3a03 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Fri, 30 Sep 2022 22:07:02 +0900 Subject: [PATCH 1/6] build: Move x86_64 specific code to arch mod This commit introduces `arch` mod for architecture specific code, and moves x86_64 related code under arch/x86_64. This change is to support multiple architecture. Signed-off-by: Akira Moroo --- src/arch/mod.rs | 5 +++++ src/{asm/mod.rs => arch/x86_64/asm.rs} | 0 src/{ => arch/x86_64}/gdt.rs | 0 src/arch/x86_64/mod.rs | 8 +++++++ src/{ => arch/x86_64}/paging.rs | 0 src/{asm => arch/x86_64}/ram32.s | 0 src/arch/x86_64/sse.rs | 13 +++++++++++ src/main.rs | 30 ++++++-------------------- 8 files changed, 32 insertions(+), 24 deletions(-) create mode 100644 src/arch/mod.rs rename src/{asm/mod.rs => arch/x86_64/asm.rs} (100%) rename src/{ => arch/x86_64}/gdt.rs (100%) create mode 100644 src/arch/x86_64/mod.rs rename src/{ => arch/x86_64}/paging.rs (100%) rename src/{asm => arch/x86_64}/ram32.s (100%) create mode 100644 src/arch/x86_64/sse.rs diff --git a/src/arch/mod.rs b/src/arch/mod.rs new file mode 100644 index 00000000..080a70d8 --- /dev/null +++ b/src/arch/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo + +#[cfg(target_arch = "x86_64")] +pub mod x86_64; diff --git a/src/asm/mod.rs b/src/arch/x86_64/asm.rs similarity index 100% rename from src/asm/mod.rs rename to src/arch/x86_64/asm.rs diff --git a/src/gdt.rs b/src/arch/x86_64/gdt.rs similarity index 100% rename from src/gdt.rs rename to src/arch/x86_64/gdt.rs diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs new file mode 100644 index 00000000..f7e549f3 --- /dev/null +++ b/src/arch/x86_64/mod.rs @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2022 Akira Moroo + +#[cfg(not(test))] +pub mod asm; +pub mod gdt; +pub mod paging; +pub mod sse; diff --git a/src/paging.rs b/src/arch/x86_64/paging.rs similarity index 100% rename from src/paging.rs rename to src/arch/x86_64/paging.rs diff --git a/src/asm/ram32.s b/src/arch/x86_64/ram32.s similarity index 100% rename from src/asm/ram32.s rename to src/arch/x86_64/ram32.s diff --git a/src/arch/x86_64/sse.rs b/src/arch/x86_64/sse.rs new file mode 100644 index 00000000..a2d5f12c --- /dev/null +++ b/src/arch/x86_64/sse.rs @@ -0,0 +1,13 @@ +use x86_64::registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}; + +// Enable SSE2 for XMM registers (needed for EFI calling) +pub fn enable_sse() { + let mut cr0 = Cr0::read(); + cr0.remove(Cr0Flags::EMULATE_COPROCESSOR); + cr0.insert(Cr0Flags::MONITOR_COPROCESSOR); + unsafe { Cr0::write(cr0) }; + let mut cr4 = Cr4::read(); + cr4.insert(Cr4Flags::OSFXSR); + cr4.insert(Cr4Flags::OSXMMEXCPT_ENABLE); + unsafe { Cr4::write(cr4) }; +} diff --git a/src/main.rs b/src/main.rs index 93cce46b..a3458b0e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,10 +22,7 @@ use core::panic::PanicInfo; -use x86_64::{ - instructions::hlt, - registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}, -}; +use x86_64::instructions::hlt; #[macro_use] mod serial; @@ -33,8 +30,7 @@ mod serial; #[macro_use] mod common; -#[cfg(not(test))] -mod asm; +mod arch; mod block; mod boot; mod bzimage; @@ -42,12 +38,10 @@ mod coreboot; mod delay; mod efi; mod fat; -mod gdt; #[cfg(all(test, feature = "integration_tests"))] mod integration; mod loader; mod mem; -mod paging; mod part; mod pci; mod pe; @@ -70,18 +64,6 @@ fn panic(_: &PanicInfo) -> ! { loop {} } -// Enable SSE2 for XMM registers (needed for EFI calling) -fn enable_sse() { - let mut cr0 = Cr0::read(); - cr0.remove(Cr0Flags::EMULATE_COPROCESSOR); - cr0.insert(Cr0Flags::MONITOR_COPROCESSOR); - unsafe { Cr0::write(cr0) }; - let mut cr4 = Cr4::read(); - cr4.insert(Cr4Flags::OSFXSR); - cr4.insert(Cr4Flags::OSXMMEXCPT_ENABLE); - unsafe { Cr4::write(cr4) }; -} - const VIRTIO_PCI_VENDOR_ID: u16 = 0x1af4; const VIRTIO_PCI_BLOCK_DEVICE_ID: u16 = 0x1042; @@ -150,8 +132,8 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice, info: &dyn boot::Info pub extern "C" fn rust64_start(rdi: &pvh::StartInfo) -> ! { serial::PORT.borrow_mut().init(); - enable_sse(); - paging::setup(); + arch::x86_64::sse::enable_sse(); + arch::x86_64::paging::setup(); main(rdi) } @@ -161,8 +143,8 @@ pub extern "C" fn rust64_start(rdi: &pvh::StartInfo) -> ! { pub extern "C" fn rust64_start() -> ! { serial::PORT.borrow_mut().init(); - enable_sse(); - paging::setup(); + arch::x86_64::sse::enable_sse(); + arch::x86_64::paging::setup(); let info = coreboot::StartInfo::default(); From 69a2a8b7c884fc7fdb550436b7269fbab6cea3b8 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sat, 22 Oct 2022 15:05:55 +0900 Subject: [PATCH 2/6] build: Merge `rust64_start()` for coreboot and pvh This commit merges `rust64_start()` for coreboot and pvh so that the entry function is implemented for each architecture. This commit includes suggested changes in [1]. [1] https://github.com/cloud-hypervisor/rust-hypervisor-firmware/pull/203#discussion_r1008665312 Signed-off-by: Akira Moroo --- src/main.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index a3458b0e..10a24b98 100644 --- a/src/main.rs +++ b/src/main.rs @@ -128,27 +128,19 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice, info: &dyn boot::Info } #[no_mangle] -#[cfg(not(feature = "coreboot"))] -pub extern "C" fn rust64_start(rdi: &pvh::StartInfo) -> ! { +pub extern "C" fn rust64_start(#[cfg(not(feature = "coreboot"))] pvh_info: &pvh::StartInfo) -> ! { serial::PORT.borrow_mut().init(); arch::x86_64::sse::enable_sse(); arch::x86_64::paging::setup(); - main(rdi) -} - -#[no_mangle] -#[cfg(feature = "coreboot")] -pub extern "C" fn rust64_start() -> ! { - serial::PORT.borrow_mut().init(); - - arch::x86_64::sse::enable_sse(); - arch::x86_64::paging::setup(); + #[cfg(feature = "coreboot")] + let info = &coreboot::StartInfo::default(); - let info = coreboot::StartInfo::default(); + #[cfg(not(feature = "coreboot"))] + let info = pvh_info; - main(&info) + main(info) } fn main(info: &dyn boot::Info) -> ! { From b543cd5f3d1561ad1c0b6f49011ca5458b3ea556 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Mon, 10 Oct 2022 18:29:40 +0900 Subject: [PATCH 3/6] build: Rename x86_64 CMOS RTC driver to `cmos` Signed-off-by: Akira Moroo --- src/cmos.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ src/rtc.rs | 94 ++--------------------------------------------------- 3 files changed, 98 insertions(+), 91 deletions(-) create mode 100644 src/cmos.rs diff --git a/src/cmos.rs b/src/cmos.rs new file mode 100644 index 00000000..9d8fca7b --- /dev/null +++ b/src/cmos.rs @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright (C) 2021 Akira Moroo + +use atomic_refcell::AtomicRefCell; +use x86_64::instructions::port::{Port, PortWriteOnly}; + +static CMOS: AtomicRefCell = AtomicRefCell::new(Cmos::new()); + +struct Cmos { + address_port: PortWriteOnly, + data_port: Port, + reg_b: Option, +} + +impl Cmos { + const fn new() -> Self { + Self { + address_port: PortWriteOnly::new(0x70), + data_port: Port::new(0x71), + reg_b: None, + } + } + + fn read_cmos(&mut self, addr: u8) -> u8 { + assert!(addr < 128); + unsafe { + self.address_port.write(addr); + self.data_port.read() + } + } + + fn get_update_status(&mut self) -> bool { + self.read_cmos(0x0a) & 0x80 != 0 + } + + fn read(&mut self, offset: u8) -> Result { + if crate::delay::wait_while(1, || self.get_update_status()) { + return Err(()); + } + Ok(self.read_cmos(offset)) + } + + fn get_reg_b(&mut self) -> u8 { + if self.reg_b.is_none() { + self.reg_b = Some(self.read_cmos(0x0b)); + } + self.reg_b.unwrap() + } + + fn read_date(&mut self) -> Result<(u8, u8, u8), ()> { + let mut year = self.read(0x09)?; + let mut month = self.read(0x08)?; + let mut day = self.read(0x07)?; + + if (self.get_reg_b() & 0x04) == 0 { + year = bcd2dec(year); + month = bcd2dec(month); + day = bcd2dec(day); + } + + Ok((year, month, day)) + } + + fn read_time(&mut self) -> Result<(u8, u8, u8), ()> { + let mut hour = self.read(0x04)?; + let mut minute = self.read(0x02)?; + let mut second = self.read(0x00)?; + + if (self.get_reg_b() & 0x04) == 0 { + hour = bcd2dec(hour); + minute = bcd2dec(minute); + second = bcd2dec(second); + } + + if ((self.get_reg_b() & 0x02) == 0) && ((hour & 0x80) != 0) { + hour = ((hour & 0x7f) + 12) % 24; + } + + Ok((hour, minute, second)) + } +} + +fn bcd2dec(b: u8) -> u8 { + ((b >> 4) & 0x0f) * 10 + (b & 0x0f) +} + +pub fn read_date() -> Result<(u8, u8, u8), ()> { + CMOS.borrow_mut().read_date() +} + +pub fn read_time() -> Result<(u8, u8, u8), ()> { + CMOS.borrow_mut().read_time() +} diff --git a/src/main.rs b/src/main.rs index 10a24b98..b302775e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -34,6 +34,8 @@ mod arch; mod block; mod boot; mod bzimage; +#[cfg(target_arch = "x86_64")] +mod cmos; mod coreboot; mod delay; mod efi; diff --git a/src/rtc.rs b/src/rtc.rs index b65ae938..5074c2ae 100644 --- a/src/rtc.rs +++ b/src/rtc.rs @@ -1,93 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright (C) 2021 Akira Moroo +// Copyright (C) 2022 Akira Moroo -use atomic_refcell::AtomicRefCell; -use x86_64::instructions::port::{Port, PortWriteOnly}; - -static RTC: AtomicRefCell = AtomicRefCell::new(Rtc::new()); - -struct Rtc { - address_port: PortWriteOnly, - data_port: Port, - reg_b: Option, -} - -impl Rtc { - const fn new() -> Self { - Self { - address_port: PortWriteOnly::new(0x70), - data_port: Port::new(0x71), - reg_b: None, - } - } - - fn read_cmos(&mut self, addr: u8) -> u8 { - assert!(addr < 128); - unsafe { - self.address_port.write(addr); - self.data_port.read() - } - } - - fn get_update_status(&mut self) -> bool { - self.read_cmos(0x0a) & 0x80 != 0 - } - - fn read(&mut self, offset: u8) -> Result { - if crate::delay::wait_while(1, || self.get_update_status()) { - return Err(()); - } - Ok(self.read_cmos(offset)) - } - - fn get_reg_b(&mut self) -> u8 { - if self.reg_b.is_none() { - self.reg_b = Some(self.read_cmos(0x0b)); - } - self.reg_b.unwrap() - } - - fn read_date(&mut self) -> Result<(u8, u8, u8), ()> { - let mut year = self.read(0x09)?; - let mut month = self.read(0x08)?; - let mut day = self.read(0x07)?; - - if (self.get_reg_b() & 0x04) == 0 { - year = bcd2dec(year); - month = bcd2dec(month); - day = bcd2dec(day); - } - - Ok((year, month, day)) - } - - fn read_time(&mut self) -> Result<(u8, u8, u8), ()> { - let mut hour = self.read(0x04)?; - let mut minute = self.read(0x02)?; - let mut second = self.read(0x00)?; - - if (self.get_reg_b() & 0x04) == 0 { - hour = bcd2dec(hour); - minute = bcd2dec(minute); - second = bcd2dec(second); - } - - if ((self.get_reg_b() & 0x02) == 0) && ((hour & 0x80) != 0) { - hour = ((hour & 0x7f) + 12) % 24; - } - - Ok((hour, minute, second)) - } -} - -fn bcd2dec(b: u8) -> u8 { - ((b >> 4) & 0x0f) * 10 + (b & 0x0f) -} - -pub fn read_date() -> Result<(u8, u8, u8), ()> { - RTC.borrow_mut().read_date() -} - -pub fn read_time() -> Result<(u8, u8, u8), ()> { - RTC.borrow_mut().read_time() -} +#[cfg(target_arch = "x86_64")] +pub use crate::cmos::{read_date, read_time}; From 9136a438dfcf539dcfc7cd9febb1203f7af0fc50 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sat, 22 Oct 2022 15:44:13 +0900 Subject: [PATCH 4/6] build: Annotate x86_64 specific code THis commit adds annotations for x86_64 specific code to prepare for supporting multiple architectures. NOTE: pci is marked as x86_64 specific code at the moment. This limitation will be removed later. Signed-off-by: Akira Moroo --- src/main.rs | 6 ++++++ src/pci.rs | 31 ++++++++++++++++++++++--------- src/serial.rs | 4 ++++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/src/main.rs b/src/main.rs index b302775e..ec171830 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,6 +22,7 @@ use core::panic::PanicInfo; +#[cfg(target_arch = "x86_64")] use x86_64::instructions::hlt; #[macro_use] @@ -45,8 +46,10 @@ mod integration; mod loader; mod mem; mod part; +#[cfg(target_arch = "x86_64")] mod pci; mod pe; +#[cfg(target_arch = "x86_64")] mod pvh; mod rtc; mod virtio; @@ -56,6 +59,7 @@ mod virtio; fn panic(info: &PanicInfo) -> ! { log!("PANIC: {}", info); loop { + #[cfg(target_arch = "x86_64")] hlt() } } @@ -129,6 +133,7 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice, info: &dyn boot::Info true } +#[cfg(target_arch = "x86_64")] #[no_mangle] pub extern "C" fn rust64_start(#[cfg(not(feature = "coreboot"))] pvh_info: &pvh::StartInfo) -> ! { serial::PORT.borrow_mut().init(); @@ -145,6 +150,7 @@ pub extern "C" fn rust64_start(#[cfg(not(feature = "coreboot"))] pvh_info: &pvh: main(info) } +#[cfg(target_arch = "x86_64")] fn main(info: &dyn boot::Info) -> ! { log!("\nBooting with {}", info.name()); diff --git a/src/pci.rs b/src/pci.rs index 3a525835..ea5a6690 100644 --- a/src/pci.rs +++ b/src/pci.rs @@ -13,6 +13,8 @@ // limitations under the License. use atomic_refcell::AtomicRefCell; + +#[cfg(target_arch = "x86_64")] use x86_64::instructions::port::{PortReadOnly, PortWriteOnly}; use crate::{ @@ -20,6 +22,7 @@ use crate::{ virtio::{Error as VirtioError, VirtioTransport}, }; +const MAX_BUSES: u8 = 8; const MAX_DEVICES: u8 = 32; const MAX_FUNCTIONS: u8 = 8; @@ -27,12 +30,14 @@ const INVALID_VENDOR_ID: u16 = 0xffff; static PCI_CONFIG: AtomicRefCell = AtomicRefCell::new(PciConfig::new()); +#[cfg(target_arch = "x86_64")] struct PciConfig { address_port: PortWriteOnly, data_port: PortReadOnly, } impl PciConfig { + #[cfg(target_arch = "x86_64")] const fn new() -> Self { // We use the legacy, port-based Configuration Access Mechanism (CAM). Self { @@ -41,15 +46,8 @@ impl PciConfig { } } - fn read(&mut self, bus: u8, device: u8, func: u8, offset: u8) -> u32 { - assert_eq!(offset % 4, 0); - assert!(device < MAX_DEVICES); - assert!(func < MAX_FUNCTIONS); - - let addr = u32::from(bus) << 16; // bus bits 23-16 - let addr = addr | u32::from(device) << 11; // slot/device bits 15-11 - let addr = addr | u32::from(func) << 8; // function bits 10-8 - let addr = addr | u32::from(offset & 0xfc); // register 7-0 + #[cfg(target_arch = "x86_64")] + fn read_at(&mut self, addr: u32) -> u32 { let addr = addr | 1u32 << 31; // enable bit 31 // SAFETY: We have exclusive access to the ports, so the data read will @@ -59,6 +57,21 @@ impl PciConfig { self.data_port.read() } } + + fn read(&mut self, bus: u8, device: u8, func: u8, offset: u8) -> u32 { + assert_eq!(offset % 4, 0); + assert!(bus < MAX_BUSES); + assert!(device < MAX_DEVICES); + assert!(func < MAX_FUNCTIONS); + + let mut addr = 0; + addr |= u32::from(bus) << 16; // bus bits 23-16 + addr |= u32::from(device) << 11; // slot/device bits 15-11 + addr |= u32::from(func) << 8; // function bits 10-8 + addr |= u32::from(offset & 0xfc); // register 7-0 + + self.read_at(addr) + } } fn get_device_details(bus: u8, device: u8, func: u8) -> (u16, u16) { diff --git a/src/serial.rs b/src/serial.rs index 91c18092..2491765e 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -18,13 +18,17 @@ use core::fmt; use atomic_refcell::AtomicRefCell; + +#[cfg(target_arch = "x86_64")] use uart_16550::SerialPort; // We use COM1 as it is the standard first serial port. +#[cfg(target_arch = "x86_64")] pub static PORT: AtomicRefCell = AtomicRefCell::new(unsafe { SerialPort::new(0x3f8) }); pub struct Serial; impl fmt::Write for Serial { + #[cfg(target_arch = "x86_64")] fn write_str(&mut self, s: &str) -> fmt::Result { PORT.borrow_mut().write_str(s) } From 723b00ec0ad1805594380b325328a5b20bf773fd Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sat, 22 Oct 2022 15:47:04 +0900 Subject: [PATCH 5/6] build: Split x86_64 specific dependencies This change is to prepare for multiple architecture support. Signed-off-by: Akira Moroo --- Cargo.toml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f245ab51..5cadf23b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,12 +27,14 @@ efi-var = [] [dependencies] bitflags = "1.3.2" -x86_64 = "0.14.10" atomic_refcell = "0.1.8" r-efi = "4.1.0" -uart_16550 = "0.2.18" linked_list_allocator = "0.10.4" +[target.'cfg(target_arch = "x86_64")'.dependencies] +uart_16550 = "0.2.18" +x86_64 = "0.14.10" + [dev-dependencies] dirs = "4.0.0" rand = "0.8.5" From 46af1e766d0c6d52c6860ecf00059f1a775b8c42 Mon Sep 17 00:00:00 2001 From: Akira Moroo Date: Sat, 22 Oct 2022 15:53:03 +0900 Subject: [PATCH 6/6] delay: Abstract architecture-dependent functions This commit introduces `rdtsc()` and `pause()` to abstract architecture-specific instruction calls. Signed-off-by: Akira Moroo --- src/delay.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/delay.rs b/src/delay.rs index 2ff61788..acb2daec 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -10,18 +10,30 @@ const NSECS_PER_SEC: u64 = 1000000000; const CPU_KHZ_DEFAULT: u64 = 200; const PAUSE_THRESHOLD_TICKS: u64 = 150; +#[cfg(target_arch = "x86_64")] +#[inline] +unsafe fn rdtsc() -> u64 { + _rdtsc() +} + +#[cfg(target_arch = "x86_64")] +#[inline] +unsafe fn pause() { + asm!("pause"); +} + pub fn ndelay(ns: u64) { let delta = ns * CPU_KHZ_DEFAULT / NSECS_PER_SEC; let mut pause_delta = 0; unsafe { - let start = _rdtsc(); + let start = rdtsc(); if delta > PAUSE_THRESHOLD_TICKS { pause_delta = delta - PAUSE_THRESHOLD_TICKS; } - while _rdtsc() - start < pause_delta { - asm!("pause"); + while rdtsc() - start < pause_delta { + pause(); } - while _rdtsc() - start < delta {} + while rdtsc() - start < delta {} } }