From c7964a7d1564e7b3a3f6bb23247e0b35fa27b77c Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Wed, 26 Feb 2020 21:43:19 -0800 Subject: [PATCH 1/9] Update pinned rust toolchain I confirmed that this toolchain has all the necessary components. See: https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu.html Signed-off-by: Joe Richey --- .travis.yml | 2 +- rust-toolchain | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 30d5aeb3..7f17a878 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ dist: xenial language: rust rust: - - nightly-2019-11-05 + - nightly-2020-03-01 env: - RUSTFLAGS="-D warnings" diff --git a/rust-toolchain b/rust-toolchain index 5571d1ad..b6daee9c 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-11-05 +nightly-2020-03-01 From 58a34cde2641e705eafce53e8e10cb238330a110 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 01:02:51 -0800 Subject: [PATCH 2/9] Cleanup include order Make sure that we consistently use the tree import syntax. Also, consistently order code like: - Global attributes/comments - `use` from `core` - `use` from external crates - `mod` declarations (if applicable) - `use` from current crate - Rest of code This makes the code easier to read/edit. Signed-off-by: Joe Richey --- src/block.rs | 3 +-- src/bzimage.rs | 3 +-- src/efi/alloc.rs | 4 ++-- src/efi/block.rs | 8 +++++--- src/efi/console.rs | 12 +++++++----- src/efi/file.rs | 11 +++++++---- src/efi/mod.rs | 29 +++++++++++++++-------------- src/loader.rs | 7 ++++--- src/logger.rs | 2 +- src/main.rs | 4 ++-- src/pci.rs | 8 ++++---- 11 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/block.rs b/src/block.rs index a0de79f0..856bc49f 100644 --- a/src/block.rs +++ b/src/block.rs @@ -14,8 +14,7 @@ use core::cell::RefCell; -use crate::virtio::Error as VirtioError; -use crate::virtio::VirtioTransport; +use crate::virtio::{Error as VirtioError, VirtioTransport}; const QUEUE_SIZE: usize = 16; diff --git a/src/bzimage.rs b/src/bzimage.rs index 6ba81c5d..330f13e9 100644 --- a/src/bzimage.rs +++ b/src/bzimage.rs @@ -12,8 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::fat; -use fat::Read; +use crate::fat::{self, Read}; pub enum Error { FileError, diff --git a/src/efi/alloc.rs b/src/efi/alloc.rs index c6abe4b0..4cc9a7e0 100644 --- a/src/efi/alloc.rs +++ b/src/efi/alloc.rs @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -const PAGE_SIZE: u64 = 4096; - use r_efi::efi::{AllocateType, MemoryType, PhysicalAddress, Status, VirtualAddress}; +const PAGE_SIZE: u64 = 4096; + // Copied from r_efi so we can do Default on it #[repr(C)] #[derive(Debug, Copy, Clone)] diff --git a/src/efi/block.rs b/src/efi/block.rs index 664a39dd..dea82878 100644 --- a/src/efi/block.rs +++ b/src/efi/block.rs @@ -14,9 +14,11 @@ use core::ffi::c_void; -use r_efi::efi::{AllocateType, Guid, MemoryType, Status}; -use r_efi::protocols::device_path::Protocol as DevicePathProtocol; -use r_efi::{eficall, eficall_abi}; +use r_efi::{ + efi::{AllocateType, Guid, MemoryType, Status}, + eficall, eficall_abi, + protocols::device_path::Protocol as DevicePathProtocol, +}; pub const PROTOCOL_GUID: Guid = Guid::from_fields( 0x964e_5b21, diff --git a/src/efi/console.rs b/src/efi/console.rs index e7522ecd..6b867d70 100644 --- a/src/efi/console.rs +++ b/src/efi/console.rs @@ -12,11 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use r_efi::efi::{Boolean, Char16, Event, Handle, Status}; -use r_efi::protocols::simple_text_input::InputKey; -use r_efi::protocols::simple_text_input::Protocol as SimpleTextInputProtocol; -use r_efi::protocols::simple_text_output::Mode as SimpleTextOutputMode; -use r_efi::protocols::simple_text_output::Protocol as SimpleTextOutputProtocol; +use r_efi::{ + efi::{Boolean, Char16, Event, Handle, Status}, + protocols::{ + simple_text_input::{InputKey, Protocol as SimpleTextInputProtocol}, + simple_text_output::{Mode as SimpleTextOutputMode, Protocol as SimpleTextOutputProtocol}, + }, +}; use super::{HandleType, HandleWrapper}; diff --git a/src/efi/file.rs b/src/efi/file.rs index c0e39021..63e8fd81 100644 --- a/src/efi/file.rs +++ b/src/efi/file.rs @@ -14,10 +14,13 @@ use core::ffi::c_void; -use r_efi::efi::{AllocateType, Char16, Guid, MemoryType, Status}; -use r_efi::protocols::device_path::Protocol as DevicePathProtocol; -use r_efi::protocols::file::Protocol as FileProtocol; -use r_efi::protocols::simple_file_system::Protocol as SimpleFileSystemProtocol; +use r_efi::{ + efi::{AllocateType, Char16, Guid, MemoryType, Status}, + protocols::{ + device_path::Protocol as DevicePathProtocol, file::Protocol as FileProtocol, + simple_file_system::Protocol as SimpleFileSystemProtocol, + }, +}; #[repr(C)] pub struct FileDevicePathProtocol { diff --git a/src/efi/mod.rs b/src/efi/mod.rs index c4ee2813..230a5a9c 100644 --- a/src/efi/mod.rs +++ b/src/efi/mod.rs @@ -12,25 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::ffi::c_void; + +use r_efi::{ + efi::{ + self, AllocateType, Boolean, CapsuleHeader, Char16, Event, EventNotify, Guid, Handle, + InterfaceType, LocateSearchType, MemoryDescriptor, MemoryType, + OpenProtocolInformationEntry, PhysicalAddress, ResetType, Status, Time, TimeCapabilities, + TimerDelay, Tpl, + }, + protocols::{ + device_path::Protocol as DevicePathProtocol, loaded_image::Protocol as LoadedImageProtocol, + }, +}; +use spin::Mutex; + mod alloc; mod block; mod console; mod file; -use spin::Mutex; - -use r_efi::efi; -use r_efi::efi::{ - AllocateType, Boolean, CapsuleHeader, Char16, Event, EventNotify, Guid, Handle, InterfaceType, - LocateSearchType, MemoryDescriptor, MemoryType, OpenProtocolInformationEntry, PhysicalAddress, - ResetType, Status, Time, TimeCapabilities, TimerDelay, Tpl, -}; - -use r_efi::protocols::device_path::Protocol as DevicePathProtocol; -use r_efi::protocols::loaded_image::Protocol as LoadedImageProtocol; - -use core::ffi::c_void; - use alloc::Allocator; #[derive(Copy, Clone, PartialEq)] diff --git a/src/loader.rs b/src/loader.rs index a587a083..92b45bcb 100644 --- a/src/loader.rs +++ b/src/loader.rs @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::bzimage; -use crate::fat; -use fat::Read; +use crate::{ + bzimage, + fat::{self, Read}, +}; pub struct LoaderConfig { pub bzimage_path: [u8; 260], diff --git a/src/logger.rs b/src/logger.rs index d4c39b42..871d4825 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -16,9 +16,9 @@ // from Philipp Oppermann use core::fmt; -use spin::Mutex; use cpuio::Port; +use spin::Mutex; pub static LOGGER: Mutex = Mutex::new(Logger { port: unsafe { Port::new(0x3f8) }, diff --git a/src/main.rs b/src/main.rs index 816c179b..02ffdbda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,14 +18,14 @@ #![cfg_attr(test, allow(unused_imports, dead_code))] #![cfg_attr(not(feature = "log-serial"), allow(unused_variables, unused_imports))] +use core::panic::PanicInfo; + #[macro_use] mod logger; #[macro_use] mod common; -use core::panic::PanicInfo; - mod block; mod bzimage; mod efi; diff --git a/src/pci.rs b/src/pci.rs index dd5be5f1..a71acac8 100644 --- a/src/pci.rs +++ b/src/pci.rs @@ -14,10 +14,10 @@ use cpuio::Port; -use crate::virtio::Error as VirtioError; -use crate::virtio::VirtioTransport; - -use crate::mem; +use crate::{ + mem, + virtio::{Error as VirtioError, VirtioTransport}, +}; const CONFIG_ADDRESS: u16 = 0xcf8; const CONFIG_DATA: u16 = 0xcfc; From 379762d6bb0aad0cdeb518c7b3fbaf995682d790 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 01:35:42 -0800 Subject: [PATCH 3/9] 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..85e2e382 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 & 0b1111_1100; + let shift32 = offset & 0b0000_0011; + + 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 & 0b1111_1100; + let shift32 = offset & 0b0000_0011; + + 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)*); }}; From 027f98fad1401d445fa34d7889f60242fe7c9d67 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 01:52:34 -0800 Subject: [PATCH 4/9] x86_64: Use hlt() instead of raw ASM Signed-off-by: Joe Richey --- src/asm/ram64.s | 4 ---- src/main.rs | 13 ++++++------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/asm/ram64.s b/src/asm/ram64.s index 4f9ca392..0dc81b46 100644 --- a/src/asm/ram64.s +++ b/src/asm/ram64.s @@ -23,7 +23,3 @@ ram64_start: movq $ram_max, %rsp jmp rust64_start - -halt_loop: - hlt - jmp halt_loop \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2b267b83..78f7a86c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,8 @@ use core::panic::PanicInfo; +use x86_64::instructions::hlt; + #[macro_use] mod serial; @@ -40,15 +42,13 @@ mod virtio; #[cfg(not(test))] global_asm!(include_str!("asm/ram64.s")); -extern "C" { - fn halt_loop() -> !; -} - #[cfg(all(not(test), feature = "log-panic"))] #[panic_handler] fn panic(info: &PanicInfo) -> ! { log!("PANIC: {}", info); - unsafe { halt_loop() } + loop { + hlt() + } } #[cfg(all(not(test), not(feature = "log-panic")))] @@ -181,6 +181,5 @@ pub extern "C" fn rust64_start() -> ! { }, ); - log!("Unable to boot from any virtio-blk device. Halting.."); - unsafe { halt_loop() } + panic!("Unable to boot from any virtio-blk device") } From 179c062933de3bd1a6b7fc281b634bc7ca0e835a Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 15:02:46 -0800 Subject: [PATCH 5/9] x86_64: Use Rust to setup SSE2 Now that we are using the x86_64 crate, we don't need ASM to set the flags on the CR0 and CR4 registers. This also makes the code much more readable. Signed-off-by: Joe Richey --- src/asm/ram64.s | 11 ----------- src/main.rs | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/src/asm/ram64.s b/src/asm/ram64.s index 0dc81b46..0e94f98a 100644 --- a/src/asm/ram64.s +++ b/src/asm/ram64.s @@ -8,17 +8,6 @@ ram64_start: movb $'L', %al outb %al, %dx - # Enable SSE2 for XMM registers (needed for EFI calling) - # Clear CR0.EM and Set CR0.MP - movq %cr0, %rax - andb $0b11111011, %al # Clear bit 2 - orb $0b00000010, %al # Set bit 1 - movq %rax, %cr0 - # Set CR4.OSFXSR and CR4.OSXMMEXCPT - movq %cr4, %rax - orb $0b00000110, %ah # Set bits 9 and 10 - movq %rax, %cr4 - # Setup the stack (at the end of our RAM region) movq $ram_max, %rsp diff --git a/src/main.rs b/src/main.rs index 78f7a86c..b4a2e185 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,7 +20,10 @@ use core::panic::PanicInfo; -use x86_64::instructions::hlt; +use x86_64::{ + instructions::hlt, + registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}, +}; #[macro_use] mod serial; @@ -83,6 +86,18 @@ fn setup_pagetables() { log!("Page tables setup"); } +// 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; @@ -166,6 +181,7 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice) -> bool { #[cfg_attr(not(test), no_mangle)] pub extern "C" fn rust64_start() -> ! { log!("\nStarting.."); + enable_sse(); setup_pagetables(); pci::print_bus(); From c5b5a7d0976c77b4d96dd64cff974d2f4a18b50a Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 15:06:05 -0800 Subject: [PATCH 6/9] paging: Use dedicated paging::Manager Like PortIO the page tables are a fundamentally global structure. By moving the paging logic to a separate file, the requirement for exclusive access is now correctly modeled with Rust types. Signed-off-by: Joe Richey --- layout.ld | 2 +- src/main.rs | 29 ++-------------------- src/paging.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 28 deletions(-) create mode 100644 src/paging.rs diff --git a/layout.ld b/layout.ld index 43da9791..af87cf45 100644 --- a/layout.ld +++ b/layout.ld @@ -35,7 +35,7 @@ SECTIONS /* Memory for identity mapping, keep synced with ADDRESS_SPACE_GIB */ address_space_gib = 4; . = ALIGN(4K); - pml2t = .; + pml2ts = .; . += 4K * address_space_gib; ASSERT((. <= ram_max - stack_size), "firmware size too big for RAM region") diff --git a/src/main.rs b/src/main.rs index b4a2e185..2c6b3766 100644 --- a/src/main.rs +++ b/src/main.rs @@ -37,6 +37,7 @@ mod efi; mod fat; mod loader; mod mem; +mod paging; mod part; mod pci; mod pe; @@ -60,32 +61,6 @@ fn panic(_: &PanicInfo) -> ! { loop {} } -/// Setup page tables to provide an identity mapping over the full 4GiB range -fn setup_pagetables() { - type PageTable = [u64; 512]; - - extern "C" { - static pml3t: PageTable; - static pml2t: PageTable; - static address_space_gib: u8; - } - let num_gib = unsafe { &address_space_gib } as *const _ as usize as u64; - log!("Setting up {} GiB identity mapping", num_gib); - - let pml2t_addr = unsafe { pml2t.as_ptr() } as usize as u64; - let pte = mem::MemoryRegion::new(pml2t_addr, num_gib * 4096); - for i in 0..(512 * num_gib) { - pte.io_write_u64(i * 8, (i << 21) + 0x83u64) - } - - let pde = mem::MemoryRegion::from_slice(unsafe { &pml3t }); - for i in 0..num_gib { - pde.io_write_u64(i * 8, (pml2t_addr + (0x1000u64 * i)) | 0x03); - } - - log!("Page tables setup"); -} - // Enable SSE2 for XMM registers (needed for EFI calling) fn enable_sse() { let mut cr0 = Cr0::read(); @@ -182,7 +157,7 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice) -> bool { pub extern "C" fn rust64_start() -> ! { log!("\nStarting.."); enable_sse(); - setup_pagetables(); + paging::MANAGER.borrow_mut().setup(); pci::print_bus(); diff --git a/src/paging.rs b/src/paging.rs new file mode 100644 index 00000000..b10a5fda --- /dev/null +++ b/src/paging.rs @@ -0,0 +1,68 @@ +use atomic_refcell::AtomicRefCell; +use x86_64::{ + registers::control::Cr3, + structures::paging::{PageSize, PageTable, PageTableFlags, PhysFrame, Size2MiB}, + PhysAddr, +}; + +// Keep in sync with address_space_gib in layout.ld +const ADDRESS_SPACE_GIB: usize = 4; + +pub static MANAGER: AtomicRefCell = AtomicRefCell::new(Manager); +pub struct Manager; + +extern "C" { + static mut pml4t: PageTable; + static mut pml3t: PageTable; + static mut pml2ts: [PageTable; ADDRESS_SPACE_GIB]; +} + +struct Tables<'a> { + l4: &'a mut PageTable, + l3: &'a mut PageTable, + l2s: &'a mut [PageTable], +} + +impl Manager { + fn tables(&mut self) -> Tables { + Tables { + l4: unsafe { &mut pml4t }, + l3: unsafe { &mut pml3t }, + l2s: unsafe { &mut pml2ts }, + } + } + + pub fn setup(&mut self) { + log!("Setting up {} GiB identity mapping", ADDRESS_SPACE_GIB); + let Tables { l4, l3, l2s } = self.tables(); + + let pt_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + // Setup Identity map using L2 huge pages + let mut next_addr = PhysAddr::new(0); + for l2 in l2s.iter_mut() { + for l2e in l2.iter_mut() { + l2e.set_addr(next_addr, pt_flags | PageTableFlags::HUGE_PAGE); + next_addr += Size2MiB::SIZE; + } + } + + // Point L3 at L2s + for (i, l2) in l2s.iter().enumerate() { + l3[i].set_addr(phys_addr(l2), pt_flags); + } + + // Point L4 at L3 + l4[0].set_addr(phys_addr(l3), pt_flags); + + // Point Cr3 at PML4 + let cr3_flags = Cr3::read().1; + let pml4t_frame = PhysFrame::from_start_address(phys_addr(l4)).unwrap(); + unsafe { Cr3::write(pml4t_frame, cr3_flags) }; + log!("Page tables setup"); + } +} + +// Map a virtual address to a PhysAddr (assumes identity mapping) +fn phys_addr(virt_addr: *const T) -> PhysAddr { + PhysAddr::new(virt_addr as u64) +} From 0021e254bcefa121b8dedc1cdeca22141524b193 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 20:06:53 -0800 Subject: [PATCH 7/9] paging: Make PageTable Memory be owned by Rust This allows for less complex linking and less `unsafe` code. It also frees up Rust to place the page tables wherever it wants. Signed-off-by: Joe Richey --- layout.ld | 10 ---------- src/main.rs | 2 +- src/paging.rs | 41 ++++++++++++++++------------------------- 3 files changed, 17 insertions(+), 36 deletions(-) diff --git a/layout.ld b/layout.ld index af87cf45..4a6bda51 100644 --- a/layout.ld +++ b/layout.ld @@ -13,10 +13,6 @@ ram_max = 2M; /* Our stack grows down from ram_max. TODO: Add a guard for stack overflows. */ stack_size = 64K; -/* Pagetable locations loaded by Firecracker/cloud-hypervisor */ -pml4t = 0x9000; -pml3t = 0xa000; - SECTIONS { /* Mapping in the program headers makes it easier to mmap the whole file. */ @@ -32,12 +28,6 @@ SECTIONS firmware_ram_size = . - ram_min; - /* Memory for identity mapping, keep synced with ADDRESS_SPACE_GIB */ - address_space_gib = 4; - . = ALIGN(4K); - pml2ts = .; - . += 4K * address_space_gib; - ASSERT((. <= ram_max - stack_size), "firmware size too big for RAM region") /* Match edk2's GccBase.lds DISCARD section */ diff --git a/src/main.rs b/src/main.rs index 2c6b3766..61c343ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![feature(global_asm)] +#![feature(global_asm, const_in_array_repeat_expressions)] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] #![cfg_attr(test, allow(unused_imports, dead_code))] diff --git a/src/paging.rs b/src/paging.rs index b10a5fda..3b3124fa 100644 --- a/src/paging.rs +++ b/src/paging.rs @@ -5,41 +5,32 @@ use x86_64::{ PhysAddr, }; -// Keep in sync with address_space_gib in layout.ld +// This is the number of GiB we will identity map. const ADDRESS_SPACE_GIB: usize = 4; -pub static MANAGER: AtomicRefCell = AtomicRefCell::new(Manager); -pub struct Manager; - -extern "C" { - static mut pml4t: PageTable; - static mut pml3t: PageTable; - static mut pml2ts: [PageTable; ADDRESS_SPACE_GIB]; -} - -struct Tables<'a> { - l4: &'a mut PageTable, - l3: &'a mut PageTable, - l2s: &'a mut [PageTable], +pub static MANAGER: AtomicRefCell = AtomicRefCell::new(Manager::new()); +pub struct Manager { + l4: PageTable, + l3: PageTable, + l2s: [PageTable; ADDRESS_SPACE_GIB], } impl Manager { - fn tables(&mut self) -> Tables { - Tables { - l4: unsafe { &mut pml4t }, - l3: unsafe { &mut pml3t }, - l2s: unsafe { &mut pml2ts }, + const fn new() -> Self { + Manager { + l4: PageTable::new(), + l3: PageTable::new(), + l2s: [PageTable::new(); ADDRESS_SPACE_GIB], } } pub fn setup(&mut self) { log!("Setting up {} GiB identity mapping", ADDRESS_SPACE_GIB); - let Tables { l4, l3, l2s } = self.tables(); let pt_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; // Setup Identity map using L2 huge pages let mut next_addr = PhysAddr::new(0); - for l2 in l2s.iter_mut() { + for l2 in self.l2s.iter_mut() { for l2e in l2.iter_mut() { l2e.set_addr(next_addr, pt_flags | PageTableFlags::HUGE_PAGE); next_addr += Size2MiB::SIZE; @@ -47,16 +38,16 @@ impl Manager { } // Point L3 at L2s - for (i, l2) in l2s.iter().enumerate() { - l3[i].set_addr(phys_addr(l2), pt_flags); + for (i, l2) in self.l2s.iter().enumerate() { + self.l3[i].set_addr(phys_addr(l2), pt_flags); } // Point L4 at L3 - l4[0].set_addr(phys_addr(l3), pt_flags); + self.l4[0].set_addr(phys_addr(&self.l3), pt_flags); // Point Cr3 at PML4 let cr3_flags = Cr3::read().1; - let pml4t_frame = PhysFrame::from_start_address(phys_addr(l4)).unwrap(); + let pml4t_frame = PhysFrame::from_start_address(phys_addr(&self.l4)).unwrap(); unsafe { Cr3::write(pml4t_frame, cr3_flags) }; log!("Page tables setup"); } From 48cbeac087b665da61d8e300ab78868706695c0e Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 20:12:49 -0800 Subject: [PATCH 8/9] layout: Use .bss section and only use a single PHDR Adding a bss section reduces binary size by ~30%. We also only use a single Program Header. This better reflects the fact that the entire firmware is loaded as a single Read/Write/Execute blob. Right now, the hypervisor's loader does not read PHDR types: https://github.com/rust-vmm/linux-loader/blob/e5c6d66d3121421672c9b25b02e8954f0ed5f58d/src/loader/mod.rs#L248-L274 Signed-off-by: Joe Richey --- layout.ld | 14 +++++--------- src/asm/ram64.s | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/layout.ld b/layout.ld index 4a6bda51..5403d7d1 100644 --- a/layout.ld +++ b/layout.ld @@ -2,9 +2,7 @@ ENTRY(ram64_start) PHDRS { - rodata PT_LOAD FILEHDR PHDRS ; - data PT_LOAD ; - text PT_LOAD ; + program PT_LOAD FILEHDR PHDRS ; } /* Loaders like to put stuff in low memory (< 1M), so we don't use it. */ @@ -19,12 +17,10 @@ SECTIONS . = ram_min; . += SIZEOF_HEADERS; - .rodata : { *(.rodata .rodata.*) } :rodata - .data : { *(.data .data.*) *(.bss .bss.*) } :data - .text : { - *(.text .text.*) - *(.ram64) - } :text + .rodata : { *(.rodata .rodata.*) } :program + .text : { *(.text .text.*) } :program + .data : { *(.data .data.*) } :program + .bss : { *(.bss .bss.*) } :program firmware_ram_size = . - ram_min; diff --git a/src/asm/ram64.s b/src/asm/ram64.s index 0e94f98a..09bbb942 100644 --- a/src/asm/ram64.s +++ b/src/asm/ram64.s @@ -1,4 +1,4 @@ -.section .ram64, "ax" +.section .text, "ax" .global ram64_start .code64 From 0deb24e79a49bd20ebea0e663ec57a1d5c57ec73 Mon Sep 17 00:00:00 2001 From: Joe Richey Date: Fri, 28 Feb 2020 20:18:18 -0800 Subject: [PATCH 9/9] asm: Make assembly code a module This makes the directory/module structure more consistent. It also avoids cluttering `main.rs`. Signed-off-by: Joe Richey --- src/asm/mod.rs | 1 + src/main.rs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 src/asm/mod.rs diff --git a/src/asm/mod.rs b/src/asm/mod.rs new file mode 100644 index 00000000..e1b44166 --- /dev/null +++ b/src/asm/mod.rs @@ -0,0 +1 @@ +global_asm!(include_str!("ram64.s")); diff --git a/src/main.rs b/src/main.rs index 61c343ab..a9bcb9b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,8 @@ mod serial; #[macro_use] mod common; +#[cfg(not(test))] +mod asm; mod block; mod bzimage; mod efi; @@ -43,9 +45,6 @@ mod pci; mod pe; mod virtio; -#[cfg(not(test))] -global_asm!(include_str!("asm/ram64.s")); - #[cfg(all(not(test), feature = "log-panic"))] #[panic_handler] fn panic(info: &PanicInfo) -> ! {