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/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/layout.ld b/layout.ld index 43da9791..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. */ @@ -13,31 +11,19 @@ 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. */ . = 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; - /* Memory for identity mapping, keep synced with ADDRESS_SPACE_GIB */ - address_space_gib = 4; - . = ALIGN(4K); - pml2t = .; - . += 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/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 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/asm/ram64.s b/src/asm/ram64.s index 4f9ca392..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 @@ -8,22 +8,7 @@ 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 jmp rust64_start - -halt_loop: - hlt - jmp halt_loop \ No newline at end of file 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..4c55a4cc 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, @@ -184,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 e7522ecd..72a9a8c8 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}; @@ -50,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 { @@ -65,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 c0e39021..43a3c285 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 { @@ -81,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) } @@ -205,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 c4ee2813..b1dedd82 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 atomic_refcell::AtomicRefCell; +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, + }, +}; + 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)] @@ -47,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], @@ -86,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 { @@ -174,7 +175,7 @@ pub extern "win64" fn allocate_pages( ) -> Status { let (status, new_address) = ALLOCATOR - .lock() + .borrow_mut() .allocate_pages( allocate_type, memory_type, @@ -190,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( @@ -200,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 { @@ -211,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 @@ -228,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, @@ -245,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( @@ -596,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, @@ -606,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, @@ -614,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/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/main.rs b/src/main.rs index 816c179b..a9bcb9b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,43 +12,46 @@ // 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))] #![cfg_attr(not(feature = "log-serial"), allow(unused_variables, unused_imports))] +use core::panic::PanicInfo; + +use x86_64::{ + instructions::hlt, + registers::control::{Cr0, Cr0Flags, Cr4, Cr4Flags}, +}; + #[macro_use] -mod logger; +mod serial; #[macro_use] mod common; -use core::panic::PanicInfo; - +#[cfg(not(test))] +mod asm; mod block; mod bzimage; mod efi; mod fat; mod loader; mod mem; +mod paging; mod part; mod pci; mod pe; 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")))] @@ -57,30 +60,16 @@ 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(); + 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; @@ -166,7 +155,8 @@ fn boot_from_device(device: &mut block::VirtioBlockDevice) -> bool { #[cfg_attr(not(test), no_mangle)] pub extern "C" fn rust64_start() -> ! { log!("\nStarting.."); - setup_pagetables(); + enable_sse(); + paging::MANAGER.borrow_mut().setup(); pci::print_bus(); @@ -181,6 +171,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") } diff --git a/src/paging.rs b/src/paging.rs new file mode 100644 index 00000000..3b3124fa --- /dev/null +++ b/src/paging.rs @@ -0,0 +1,59 @@ +use atomic_refcell::AtomicRefCell; +use x86_64::{ + registers::control::Cr3, + structures::paging::{PageSize, PageTable, PageTableFlags, PhysFrame, Size2MiB}, + PhysAddr, +}; + +// This is the number of GiB we will identity map. +const ADDRESS_SPACE_GIB: usize = 4; + +pub static MANAGER: AtomicRefCell = AtomicRefCell::new(Manager::new()); +pub struct Manager { + l4: PageTable, + l3: PageTable, + l2s: [PageTable; ADDRESS_SPACE_GIB], +} + +impl Manager { + 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 pt_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + // Setup Identity map using L2 huge pages + let mut next_addr = PhysAddr::new(0); + 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; + } + } + + // Point L3 at L2s + for (i, l2) in self.l2s.iter().enumerate() { + self.l3[i].set_addr(phys_addr(l2), pt_flags); + } + + // Point L4 at L3 + 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(&self.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) +} diff --git a/src/pci.rs b/src/pci.rs index dd5be5f1..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::virtio::Error as VirtioError; -use crate::virtio::VirtioTransport; - -use crate::mem; - -const CONFIG_ADDRESS: u16 = 0xcf8; -const CONFIG_DATA: u16 = 0xcfc; +use crate::{ + mem, + virtio::{Error as VirtioError, VirtioTransport}, +}; 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 +static PCI_CONFIG: AtomicRefCell = AtomicRefCell::new(PciConfig::new()); - let mut config_address_port: Port = unsafe { Port::new(CONFIG_ADDRESS) }; - config_address_port.write(addr); - - 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 d4c39b42..d74545e9 100644 --- a/src/logger.rs +++ b/src/serial.rs @@ -16,33 +16,30 @@ // from Philipp Oppermann use core::fmt; -use spin::Mutex; -use cpuio::Port; +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)*); }};