From 743ce2dd1c12a5928b5307d9a23570da0f135f2e Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 01:35:42 -0800 Subject: [PATCH] Use x86_64 and AtomicRefCell creates - The `spin` crate is unmaintained - Replace it with `atomic_refcell` - We don't need spin locks, just a checked refcount - Use `x86_64` instead of `cpuio` - It has better abstractions - It has more functionality that we can user later - Fixup existing port usage Signed-off-by: Joe Richey --- Cargo.lock | 32 +++++++--- Cargo.toml | 4 +- src/efi/block.rs | 2 +- src/efi/console.rs | 4 +- src/efi/file.rs | 4 +- src/efi/mod.rs | 26 ++++----- src/main.rs | 2 +- src/pci.rs | 109 ++++++++++++++++++++--------------- src/{logger.rs => serial.rs} | 33 +++++------ 9 files changed, 122 insertions(+), 94 deletions(-) rename src/{logger.rs => serial.rs} (66%) diff --git a/Cargo.lock b/Cargo.lock index d837c19d..c02c0022 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,17 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] -name = "cpuio" -version = "0.2.0" +name = "atomic_refcell" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bit_field" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bitflags" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "hypervisor-fw" version = "0.1.0" dependencies = [ - "cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "atomic_refcell 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "r-efi 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "x86_64 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -20,11 +30,17 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "spin" -version = "0.5.0" +name = "x86_64" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [metadata] -"checksum cpuio 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa" +"checksum atomic_refcell 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3bc31dce067eab974c815a9deb95f6217806de7b53685d7fc31f8ccf3fb2539f" +"checksum bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8765909f9009617974ab6b7d332625b320b33c326b1e9321382ef1999b5d56" +"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" "checksum r-efi 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f2c7f9e57367053a4c9d2f235e715b7a4fa8b774b78eb3e477b2345dc2bb33d" -"checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" +"checksum x86_64 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4e6a3f047ad0844d3b4794f34d1095aefb918241064663d4163db8c7d4a76edf" diff --git a/Cargo.toml b/Cargo.toml index e800f69c..8150a06c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,7 @@ log-serial = [] log-panic = ["log-serial"] [dependencies] -cpuio = "*" -spin = "0.5" +x86_64 = "0.9" +atomic_refcell = "0.1" r-efi = "2.1.0" diff --git a/src/efi/block.rs b/src/efi/block.rs index dea82878..4c55a4cc 100644 --- a/src/efi/block.rs +++ b/src/efi/block.rs @@ -186,7 +186,7 @@ impl<'a> BlockWrapper<'a> { let last_block = unsafe { (*block).get_capacity() } - 1; let size = core::mem::size_of::(); - let (_status, new_address) = super::ALLOCATOR.lock().allocate_pages( + let (_status, new_address) = super::ALLOCATOR.borrow_mut().allocate_pages( AllocateType::AllocateAnyPages, MemoryType::LoaderData, ((size + super::PAGE_SIZE as usize - 1) / super::PAGE_SIZE as usize) as u64, diff --git a/src/efi/console.rs b/src/efi/console.rs index 6b867d70..72a9a8c8 100644 --- a/src/efi/console.rs +++ b/src/efi/console.rs @@ -52,7 +52,7 @@ pub extern "win64" fn stdout_output_string( message: *mut Char16, ) -> Status { use core::fmt::Write; - let mut logger = crate::logger::LOGGER.lock(); + let mut serial = crate::serial::SERIAL.borrow_mut(); let mut string_end = false; loop { @@ -67,7 +67,7 @@ pub extern "win64" fn stdout_output_string( i += 1; } let s = unsafe { core::str::from_utf8_unchecked(&output) }; - logger.write_str(s).unwrap(); + serial.write_str(s).unwrap(); if string_end { break; } diff --git a/src/efi/file.rs b/src/efi/file.rs index 63e8fd81..43a3c285 100644 --- a/src/efi/file.rs +++ b/src/efi/file.rs @@ -84,7 +84,7 @@ pub extern "win64" fn open( pub extern "win64" fn close(proto: *mut FileProtocol) -> Status { let wrapper = container_of!(proto, FileWrapper, proto); super::ALLOCATOR - .lock() + .borrow_mut() .free_pages(&wrapper as *const _ as u64) } @@ -208,7 +208,7 @@ pub struct FileSystemWrapper<'a> { impl<'a> FileSystemWrapper<'a> { fn create_file(&self, root: bool) -> Option<*mut FileWrapper> { let size = core::mem::size_of::(); - let (status, new_address) = super::ALLOCATOR.lock().allocate_pages( + let (status, new_address) = super::ALLOCATOR.borrow_mut().allocate_pages( AllocateType::AllocateAnyPages, MemoryType::LoaderData, ((size + super::PAGE_SIZE as usize - 1) / super::PAGE_SIZE as usize) as u64, diff --git a/src/efi/mod.rs b/src/efi/mod.rs index 230a5a9c..b1dedd82 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -14,6 +14,7 @@ use core::ffi::c_void; +use atomic_refcell::AtomicRefCell; use r_efi::{ efi::{ self, AllocateType, Boolean, CapsuleHeader, Char16, Event, EventNotify, Guid, Handle, @@ -25,7 +26,6 @@ use r_efi::{ device_path::Protocol as DevicePathProtocol, loaded_image::Protocol as LoadedImageProtocol, }, }; -use spin::Mutex; mod alloc; mod block; @@ -48,7 +48,7 @@ struct HandleWrapper { handle_type: HandleType, } -pub static ALLOCATOR: Mutex = Mutex::new(Allocator::new()); +pub static ALLOCATOR: AtomicRefCell = AtomicRefCell::new(Allocator::new()); static mut BLOCK_WRAPPERS: block::BlockWrappers = block::BlockWrappers { wrappers: [core::ptr::null_mut(); 16], @@ -87,7 +87,7 @@ pub extern "win64" fn set_virtual_address_map( core::slice::from_raw_parts_mut(descriptors as *mut alloc::MemoryDescriptor, count) }; - ALLOCATOR.lock().update_virtual_addresses(descriptors) + ALLOCATOR.borrow_mut().update_virtual_addresses(descriptors) } pub extern "win64" fn convert_pointer(_: usize, _: *mut *mut c_void) -> Status { @@ -175,7 +175,7 @@ pub extern "win64" fn allocate_pages( ) -> Status { let (status, new_address) = ALLOCATOR - .lock() + .borrow_mut() .allocate_pages( allocate_type, memory_type, @@ -191,7 +191,7 @@ pub extern "win64" fn allocate_pages( } pub extern "win64" fn free_pages(address: PhysicalAddress, _: usize) -> Status { - ALLOCATOR.lock().free_pages(address) + ALLOCATOR.borrow_mut().free_pages(address) } pub extern "win64" fn get_memory_map( @@ -201,7 +201,7 @@ pub extern "win64" fn get_memory_map( descriptor_size: *mut usize, descriptor_version: *mut u32, ) -> Status { - let count = ALLOCATOR.lock().get_descriptor_count(); + let count = ALLOCATOR.borrow().get_descriptor_count(); let map_size = core::mem::size_of::() * count; if unsafe { *memory_map_size } < map_size { unsafe { @@ -212,13 +212,13 @@ pub extern "win64" fn get_memory_map( let out = unsafe { core::slice::from_raw_parts_mut(out as *mut alloc::MemoryDescriptor, count) }; - let count = ALLOCATOR.lock().get_descriptors(out); + let count = ALLOCATOR.borrow().get_descriptors(out); let map_size = core::mem::size_of::() * count; unsafe { *memory_map_size = map_size; *descriptor_version = efi::MEMORY_DESCRIPTOR_VERSION; *descriptor_size = core::mem::size_of::(); - *key = ALLOCATOR.lock().get_map_key(); + *key = ALLOCATOR.borrow().get_map_key(); } Status::SUCCESS @@ -229,7 +229,7 @@ pub extern "win64" fn allocate_pool( size: usize, address: *mut *mut c_void, ) -> Status { - let (status, new_address) = ALLOCATOR.lock().allocate_pages( + let (status, new_address) = ALLOCATOR.borrow_mut().allocate_pages( AllocateType::AllocateAnyPages, memory_type, ((size + PAGE_SIZE as usize - 1) / PAGE_SIZE as usize) as u64, @@ -246,7 +246,7 @@ pub extern "win64" fn allocate_pool( } pub extern "win64" fn free_pool(ptr: *mut c_void) -> Status { - ALLOCATOR.lock().free_pages(ptr as u64) + ALLOCATOR.borrow_mut().free_pages(ptr as u64) } pub extern "win64" fn create_event( @@ -597,7 +597,7 @@ fn populate_allocator(image_address: u64, image_size: u64) { for entry in e820_table { if entry.entry_type == E820_RAM { - ALLOCATOR.lock().add_initial_allocation( + ALLOCATOR.borrow_mut().add_initial_allocation( MemoryType::ConventionalMemory, entry.size / PAGE_SIZE, entry.addr, @@ -607,7 +607,7 @@ fn populate_allocator(image_address: u64, image_size: u64) { } // Add ourselves - ALLOCATOR.lock().allocate_pages( + ALLOCATOR.borrow_mut().allocate_pages( AllocateType::AllocateAddress, MemoryType::RuntimeServicesCode, 1024 * 1024 / PAGE_SIZE, @@ -615,7 +615,7 @@ fn populate_allocator(image_address: u64, image_size: u64) { ); // Add the loaded binary - ALLOCATOR.lock().allocate_pages( + ALLOCATOR.borrow_mut().allocate_pages( AllocateType::AllocateAddress, MemoryType::LoaderCode, image_size / PAGE_SIZE, diff --git a/src/main.rs b/src/main.rs index 02ffdbda..2b267b83 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ use core::panic::PanicInfo; #[macro_use] -mod logger; +mod serial; #[macro_use] mod common; diff --git a/src/pci.rs b/src/pci.rs index a71acac8..2737a291 100644 --- a/src/pci.rs +++ b/src/pci.rs @@ -12,54 +12,58 @@ // See the License for the specific language governing permissions and // limitations under the License. -use cpuio::Port; +use atomic_refcell::AtomicRefCell; +use x86_64::instructions::port::{PortReadOnly, PortWriteOnly}; use crate::{ mem, virtio::{Error as VirtioError, VirtioTransport}, }; -const CONFIG_ADDRESS: u16 = 0xcf8; -const CONFIG_DATA: u16 = 0xcfc; - const MAX_DEVICES: u8 = 32; const MAX_FUNCTIONS: u8 = 8; const INVALID_VENDOR_ID: u16 = 0xffff; -fn pci_config_read_u32(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 - let addr = addr | 1u32 << 31; // enable bit 31 - - let mut config_address_port: Port = unsafe { Port::new(CONFIG_ADDRESS) }; - config_address_port.write(addr); +static PCI_CONFIG: AtomicRefCell = AtomicRefCell::new(PciConfig::new()); - let mut config_data_port: Port = unsafe { Port::new(CONFIG_DATA) }; - - config_data_port.read() +struct PciConfig { + address_port: PortWriteOnly, + data_port: PortReadOnly, } -fn pci_config_read_u8(bus: u8, device: u8, func: u8, offset: u8) -> u8 { - (pci_config_read_u32(bus, device, func, offset & !3) >> ((offset % 4) * 8)) as u8 -} +impl PciConfig { + const fn new() -> Self { + // We use the legacy, port-based Configuration Access Mechanism (CAM). + Self { + address_port: PortWriteOnly::new(0xcf8), + data_port: PortReadOnly::new(0xcfc), + } + } -fn pci_config_read_u16(bus: u8, device: u8, func: u8, offset: u8) -> u16 { - assert_eq!(offset % 2, 0); - (pci_config_read_u32(bus, device, func, offset & !3) >> ((offset % 4) * 8)) as u16 + 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 + let addr = addr | 1u32 << 31; // enable bit 31 + + // SAFETY: We have exclusive access to the ports, so the data read will + // correspond to the address written. + unsafe { + self.address_port.write(addr); + self.data_port.read() + } + } } fn get_device_details(bus: u8, device: u8, func: u8) -> (u16, u16) { - ( - pci_config_read_u16(bus, device, func, 0), - pci_config_read_u16(bus, device, func, 2), - ) + let data = PCI_CONFIG.borrow_mut().read(bus, device, func, 0); + ((data & 0xffff) as u16, (data >> 16) as u16) } pub fn print_bus() { @@ -132,16 +136,27 @@ impl PciDevice { } } - fn config_read_u8(&self, offset: u8) -> u8 { - pci_config_read_u8(self.bus, self.device, self.func, offset) + fn read_u8(&self, offset: u8) -> u8 { + let offset32 = offset & 0b11111100; + let shift32 = offset & 0b00000011; + + let data = self.read_u32(offset32); + (data >> (shift32 * 8)) as u8 } - fn config_read_u16(&self, offset: u8) -> u16 { - pci_config_read_u16(self.bus, self.device, self.func, offset) + fn read_u16(&self, offset: u8) -> u16 { + assert_eq!(offset % 2, 0); + let offset32 = offset & 0b11111100; + let shift32 = offset & 0b00000011; + + let data = self.read_u32(offset32); + (data >> (shift32 * 8)) as u16 } - fn config_read_u32(&self, offset: u8) -> u32 { - pci_config_read_u32(self.bus, self.device, self.func, offset) + fn read_u32(&self, offset: u8) -> u32 { + PCI_CONFIG + .borrow_mut() + .read(self.bus, self.device, self.func, offset) } fn init(&mut self) { @@ -165,7 +180,7 @@ impl PciDevice { //0x24 offset is last bar while current_bar_offset < 0x24 { #[allow(clippy::blacklisted_name)] - let bar = self.config_read_u32(current_bar_offset); + let bar = self.read_u32(current_bar_offset); // lsb is 1 for I/O space bars if bar & 1 == 1 { @@ -184,7 +199,7 @@ impl PciDevice { current_bar_offset += 4; #[allow(clippy::blacklisted_name)] - let bar = self.config_read_u32(current_bar_offset); + let bar = self.read_u32(current_bar_offset); self.bars[current_bar].address += u64::from(bar) << 32; } _ => panic!("Unsupported BAR type"), @@ -254,7 +269,7 @@ impl VirtioTransport for VirtioPciTransport { self.device.init(); // Read status register - let status = self.device.config_read_u16(0x06); + let status = self.device.read_u16(0x06); // bit 4 of status is capability bit if status & 1 << 4 == 0 { @@ -263,11 +278,11 @@ impl VirtioTransport for VirtioPciTransport { } // capabilities list offset is at 0x34 - let mut cap_next = self.device.config_read_u8(0x34); + let mut cap_next = self.device.read_u8(0x34); while cap_next < 0xff && cap_next > 0 { // vendor specific capability - if self.device.config_read_u8(cap_next) == 0x09 { + if self.device.read_u8(cap_next) == 0x09 { // These offsets are into the following structure: // struct virtio_pci_cap { // u8 cap_vndr; /* Generic PCI field: PCI_CAP_ID_VNDR */ @@ -279,11 +294,11 @@ impl VirtioTransport for VirtioPciTransport { // le32 offset; /* Offset within bar. */ // le32 length; /* Length of the structure, in bytes. */ // }; - let cfg_type = self.device.config_read_u8(cap_next + 3); + let cfg_type = self.device.read_u8(cap_next + 3); #[allow(clippy::blacklisted_name)] - let bar = self.device.config_read_u8(cap_next + 4); - let offset = self.device.config_read_u32(cap_next + 8); - let length = self.device.config_read_u32(cap_next + 12); + let bar = self.device.read_u8(cap_next + 4); + let offset = self.device.read_u32(cap_next + 8); + let length = self.device.read_u32(cap_next + 12); if cfg_type == VirtioPciCapabilityType::CommonConfig as u8 { self.region = mem::MemoryRegion::new( @@ -302,7 +317,7 @@ impl VirtioTransport for VirtioPciTransport { // struct virtio_pci_cap cap; // le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */ // }; - self.notify_off_multiplier = self.device.config_read_u32(cap_next + 16); + self.notify_off_multiplier = self.device.read_u32(cap_next + 16); } if cfg_type == VirtioPciCapabilityType::DeviceConfig as u8 { @@ -312,7 +327,7 @@ impl VirtioTransport for VirtioPciTransport { ); } } - cap_next = self.device.config_read_u8(cap_next + 1) + cap_next = self.device.read_u8(cap_next + 1) } Ok(()) diff --git a/src/logger.rs b/src/serial.rs similarity index 66% rename from src/logger.rs rename to src/serial.rs index 871d4825..d74545e9 100644 --- a/src/logger.rs +++ b/src/serial.rs @@ -17,32 +17,29 @@ use core::fmt; -use cpuio::Port; -use spin::Mutex; +use atomic_refcell::AtomicRefCell; +use x86_64::instructions::port::PortWriteOnly; -pub static LOGGER: Mutex = Mutex::new(Logger { - port: unsafe { Port::new(0x3f8) }, -}); +pub static SERIAL: AtomicRefCell = AtomicRefCell::new(Serial::new()); -pub struct Logger { - port: Port, +pub struct Serial { + port: PortWriteOnly, } -impl Logger { - pub fn write_byte(&mut self, byte: u8) { - self.port.write(byte) - } - - pub fn write_string(&mut self, s: &str) { - for c in s.chars() { - self.write_byte(c as u8); +impl Serial { + pub const fn new() -> Self { + // We use COM1 as it is the standard first serial port. + Self { + port: PortWriteOnly::new(0x3f8), } } } -impl fmt::Write for Logger { +impl fmt::Write for Serial { fn write_str(&mut self, s: &str) -> fmt::Result { - self.write_string(s); + for b in s.bytes() { + unsafe { self.port.write(b) } + } Ok(()) } } @@ -52,7 +49,7 @@ macro_rules! log { ($($arg:tt)*) => {{ use core::fmt::Write; #[cfg(all(feature = "log-serial", not(test)))] - writeln!(&mut crate::logger::LOGGER.lock(), $($arg)*).unwrap(); + writeln!(crate::serial::SERIAL.borrow_mut(), $($arg)*).unwrap(); #[cfg(all(feature = "log-serial", test))] println!($($arg)*); }};