Skip to content

Commit

Permalink
Implement initial support for NVRAM
Browse files Browse the repository at this point in the history
  • Loading branch information
fruhland committed Jul 2, 2024
1 parent 4cf5e71 commit 2e16228
Show file tree
Hide file tree
Showing 7 changed files with 270 additions and 13 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,5 @@ d3os.img
RELEASEX64_OVMF.fd
loader/kernel.elf
loader/initrd.tar
loader/initrd
loader/initrd
nvdimm0
12 changes: 6 additions & 6 deletions Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ alias = "qemu"

[tasks.debug]
command = "qemu-system-x86_64"
args = [ "-S", "-gdb", "tcp::1234", "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=pa" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=pa", "-S", "-gdb", "tcp::1234" ]
dependencies = [ "debug-signal-vscode" ]

[tasks.debug.mac]
args = [ "-S", "-gdb", "tcp::1234", "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=coreaudio" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=coreaudio", "-S", "-gdb", "tcp::1234" ]

[tasks.debug-signal-vscode]
command = "echo"
Expand All @@ -27,18 +27,18 @@ dependencies = [ "image", "ovmf" ]

[tasks.qemu]
command = "qemu-system-x86_64"
args = [ "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=pa" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=pa" ]
dependencies = [ "image", "ovmf" ]

[tasks.qemu.mac]
args = [ "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=coreaudio" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=coreaudio" ]

[tasks.qemu-no-compile]
command = "qemu-system-x86_64"
args = [ "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=pa" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=pa" ]

[tasks.qemu-no-compile.mac]
args = [ "-machine", "q35,pcspk-audiodev=audio0", "-m", "128M", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-audiodev", "id=audio0,driver=coreaudio" ]
args = [ "-machine", "q35,nvdimm=on,pcspk-audiodev=audio0", "-m", "128M,slots=2,maxmem=1G", "-cpu", "qemu64", "-bios", "RELEASEX64_OVMF.fd", "-boot", "d", "-vga", "std", "-rtc", "base=localtime", "-serial", "stdio", "-drive", "driver=raw,node-name=boot,file.driver=file,file.filename=d3os.img", "-object", "memory-backend-file,id=mem1,share=on,mem-path=nvdimm0,size=16M", "-device", "nvdimm,memdev=mem1,id=nv1,label-size=2M", "-audiodev", "id=audio0,driver=coreaudio" ]

[tasks.ovmf]
command = "wget"
Expand Down
1 change: 1 addition & 0 deletions os/kernel/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ log = "0.4.20"
goblin = { version = "0.8.2", default-features = false, features = ["elf32", "elf64", "endian_fd"]}
tar-no-std = "0.3.1"
pci_types = "0.10.0"
bitflags = "2.6.0"

[build-dependencies]
built = { version = "0.7.3", features = ["chrono", "git2"] }
4 changes: 2 additions & 2 deletions os/kernel/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ CARGO_BUILD_OPTION = "--release"
[env]
CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
RUST_TARGET_PATH = "${CARGO_MAKE_WORKING_DIRECTORY}"
SOURCE_DIRECOTRY = "${CARGO_MAKE_WORKING_DIRECTORY}/src"
SOURCE_DIRECTORY = "${CARGO_MAKE_WORKING_DIRECTORY}/src"
LINKER_FILE = "${CARGO_MAKE_WORKING_DIRECTORY}/link.ld"
RUST_OBJECT = "${BUILD_DIRECTORY}/lib${CARGO_MAKE_PROJECT_NAME}.a"
ASM_OBJECT = "${BUILD_DIRECTORY}/boot.o"
Expand All @@ -28,7 +28,7 @@ args = [ "build", "-Z", "build-std=core,alloc", "-Z", "build-std-features=compil

[tasks.build-asm]
command = "nasm"
args = [ "-f", "elf64", "-w+error=label-redef-late", "-o", "${ASM_OBJECT}", "${SOURCE_DIRECOTRY}/boot.asm" ]
args = [ "-f", "elf64", "-w+error=label-redef-late", "-o", "${ASM_OBJECT}", "${SOURCE_DIRECTORY}/boot.asm" ]

[tasks.link]
command = "${LINKER}"
Expand Down
37 changes: 33 additions & 4 deletions os/kernel/src/boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use multiboot2::{BootInformation, BootInformationHeader, EFIMemoryMapTag, Memory
use uefi::prelude::*;
use uefi::table::boot::{MemoryMap, PAGE_SIZE};
use uefi::table::Runtime;
use uefi::table::runtime::Time;
use uefi_raw::table::boot::MemoryType;
use x86_64::instructions::interrupts;
use x86_64::instructions::segmentation::{Segment, CS, DS, ES, FS, GS, SS};
Expand All @@ -34,8 +35,9 @@ use x86_64::structures::paging::{Page, PageTableFlags, PhysFrame};
use x86_64::PrivilegeLevel::Ring0;
use x86_64::structures::paging::frame::PhysFrameRange;
use x86_64::structures::paging::page::PageRange;
use crate::{allocator, apic, built_info, efi_system_table, gdt, init_acpi_tables, init_apic, init_efi_system_table, init_initrd, init_keyboard, init_pci, init_serial_port, init_terminal, initrd, logger, memory, process_manager, ps2_devices, scheduler, serial_port, terminal, timer, tss};
use crate::memory::MemorySpace;
use crate::{acpi_tables, allocator, apic, built_info, efi_system_table, gdt, init_acpi_tables, init_apic, init_efi_system_table, init_initrd, init_keyboard, init_pci, init_serial_port, init_terminal, initrd, logger, memory, process_manager, ps2_devices, scheduler, serial_port, terminal, timer, tss};
use crate::memory::{MemorySpace, nvmem};
use crate::memory::nvmem::Nfit;

// import labels from linker script 'link.ld'
extern "C" {
Expand Down Expand Up @@ -192,6 +194,33 @@ pub extern "C" fn start(multiboot2_magic: u32, multiboot2_addr: *const BootInfor
info!("Scanning PCI bus");
init_pci();

// Initialize non-volatile memory (creates identity mappings for any non-volatile memory regions)
nvmem::init();

// As a demo for NVRAM support, we read the last boot time from NVRAM and write the current boot time to it
if let Ok(nfit) = acpi_tables().lock().find_table::<Nfit>() {
if let Some(range) = nfit.get_phys_addr_ranges().first() {
let date_ptr = range.as_phys_frame_range().start.start_address().as_u64() as *mut Time;

// Read last boot time from NVRAM
let date = unsafe { date_ptr.read() };
if date.is_valid() {
info!("Last boot time: [{:0>4}-{:0>2}-{:0>2} {:0>2}:{:0>2}:{:0>2}]", date.year(), date.month(), date.day(), date.hour(), date.minute(), date.second());
}

// Get current time
if let Some(efi_system_table) = efi_system_table() {
let system_table = efi_system_table.read();
let runtime_services = unsafe { system_table.runtime_services() };

// Write current boot time to NVRAM
if let Ok(time) = runtime_services.get_time() {
unsafe { date_ptr.write(time) }
}
}
}
}

// Load initial ramdisk
let initrd_tag = multiboot.module_tags()
.find(|module| module.cmdline().is_ok_and(|name| name == "initrd"))
Expand All @@ -213,7 +242,7 @@ pub extern "C" fn start(multiboot2_magic: u32, multiboot2_addr: *const BootInfor
.expect("Shell application not available!")
.data()));

// Disable terminal logging (remove terminal outputstream)
// Disable terminal logging (remove terminal output stream)
logger().lock().remove(terminal());
terminal().clear();

Expand All @@ -228,7 +257,7 @@ pub extern "C" fn start(multiboot2_magic: u32, multiboot2_addr: *const BootInfor


/**
Description: Setup the GDT
Description: Set up the GDT
*/
fn init_gdt() {
let mut gdt = gdt().lock();
Expand Down
1 change: 1 addition & 0 deletions os/kernel/src/memory/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod alloc;
pub mod physical;
pub mod r#virtual;
pub mod nvmem;

#[derive(Clone, Copy)]
pub enum MemorySpace {
Expand Down
225 changes: 225 additions & 0 deletions os/kernel/src/memory/nvmem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
use alloc::vec::Vec;
use core::ptr;
use core::cmp::PartialEq;
use acpi::AcpiTable;
use acpi::sdt::{SdtHeader, Signature};
use bitflags::bitflags;
use log::info;
use uefi::table::boot::PAGE_SIZE;
use x86_64::structures::paging::{Page, PageTableFlags, PhysFrame};
use x86_64::structures::paging::frame::PhysFrameRange;
use x86_64::structures::paging::page::PageRange;
use x86_64::{PhysAddr, VirtAddr};
use crate::{acpi_tables, process_manager};
use crate::memory::MemorySpace;

#[allow(dead_code)]
#[repr(u16)]
#[derive(Debug, Clone, Copy, PartialEq)]
enum NfitStructureType {
SystemPhysicalAddressRange = 0,
NvdimmRegionMappingStructure = 1,
Interleave = 2,
SmbiosManagementInformation = 3,
NvdimmControlRegion = 4,
NvdimmBlockDataWindowRegion = 5,
FlushHintAddress = 6,
PlatformCapabilities = 7,
}

bitflags! {
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct AddressRangeMemoryMappingAttribute: u32 {
const UC = 0x00000001;
const WC = 0x00000002;
const WT = 0x00000004;
const WB = 0x00000008;
const UCE = 0x00000010;
const WP = 0x00001000;
const RP = 0x00002000;
const XP = 0x00004000;
const NV = 0x00008000;
const MORE_RELIABLE = 0x00010000;
const RO = 0x00020000;
const SP = 0x00040000;
}
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct Nfit {
header: SdtHeader,
reserved: u32,
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct NfitStructureHeader {
typ: NfitStructureType,
length: u16,
}

#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct SystemPhysicalAddressRange {
header: NfitStructureHeader,
spa_range_structure_index: u16,
flags: u16,
reserved: u32,
proximity_domain: u32,
address_range_type_guid: u128,
base: u64,
length: u64,
mapping_attributes: AddressRangeMemoryMappingAttribute,
}

#[allow(dead_code)]
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct NvdimmRegionMappingStructure {
header: NfitStructureHeader,
nfit_device_handle: u32,
physical_id: u16,
region_id: u16,
spa_range_structure_index: u16,
control_region_structure_index: u16,
region_size: u64,
region_offset: u64,
phys_addr_region_base: u64,
interleave_structure_index: u16,
interleave_ways: u16,
state_flags: u16,
reserved: [u8; 2],
}

#[allow(dead_code)]
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct NvdimmControlRegionStructure {
header: NfitStructureHeader,
control_region_structure_index: u16,
vendor_id: u16,
device_id: u16,
revision_id: u16,
subsystem_vendor_id: u16,
subsystem_device_id: u16,
subsystem_revision_id: u16,
valid_fields: u8,
manufacturing_location: u8,
manufacturing_date: u16,
reserved1: [u8; 2],
serial_number: u32,
region_format_interface_code: u16,
window_count: u16,
window_size: u64,
command_register_offset: u64,
command_register_size: u64,
status_register_offset: u64,
status_register_size: u64,
control_region_size: u64,
control_region_flags: u16,
reserved2: [u8; 6],
}

#[allow(dead_code)]
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct FlushHintAddressStructure {
header: NfitStructureHeader,
device_handle: u32,
hint_count: u16
}

unsafe impl AcpiTable for Nfit {
const SIGNATURE: Signature = Signature::NFIT;

fn header(&self) -> &SdtHeader {
&self.header
}
}

impl Nfit {
pub fn get_structures(&self) -> Vec<&NfitStructureHeader> {
let mut tables = Vec::<&NfitStructureHeader>::new();

let mut remaining = self.header.length as usize - size_of::<Nfit>();
let mut structure_ptr = unsafe { ptr::from_ref(self).add(1) } as *const NfitStructureHeader;

while remaining > 0 {
unsafe {
let structure = *structure_ptr;
tables.push(structure_ptr.as_ref().expect("Invalid NFIT structure"));

structure_ptr = (structure_ptr as *const u8).add(structure.length as usize) as *const NfitStructureHeader;
remaining = remaining - structure.length as usize;
}
}

return tables;
}

pub fn get_phys_addr_ranges (&self) -> Vec<&SystemPhysicalAddressRange> {
let mut ranges = Vec::<&SystemPhysicalAddressRange>::new();

self.get_structures().iter().for_each(|structure| {
let structure_type = unsafe { ptr::from_ref(structure).read_unaligned().typ };
if structure_type == NfitStructureType::SystemPhysicalAddressRange {
ranges.push(structure.as_structure::<SystemPhysicalAddressRange>());
}
});

return ranges;
}
}

impl NfitStructureHeader {
pub fn as_structure<T>(&self) -> &T {
unsafe {
ptr::from_ref(self).cast::<T>().as_ref().expect("Invalid NFIT structure")
}
}
}

impl SystemPhysicalAddressRange {
pub fn as_phys_frame_range(&self) -> PhysFrameRange {
let start = PhysFrame::from_start_address(PhysAddr::new(self.base)).expect("Invalid start address");

return PhysFrameRange { start, end: start + (self.length / PAGE_SIZE as u64) };
}
}

impl FlushHintAddressStructure {
pub fn get_flush_hint_addresses(&self) -> Vec<u64> {
let mut hints = Vec::new();
for i in 0..self.hint_count as usize {
unsafe {
let hint_ptr = (ptr::from_ref(self).add(1) as *const u64).add(i);
let hint = hint_ptr.read_unaligned();
hints.push(hint);
}
}

return hints;
}
}

pub fn init() {
if let Ok(nfit) = acpi_tables().lock().find_table::<Nfit>() {
info!("Found NFIT table");

// Search NFIT table for non-volatile memory ranges
for spa in nfit.get_phys_addr_ranges() {
// Copy values to avoid unaligned access of packed struct fields
let address = spa.base;
let length = spa.length;
info!("Found non-volatile memory (Address: [0x{:0>16x}], Length: [{} MiB])", address, length / 1024 / 1024);

// Map non-volatile memory range to kernel address space
let start_page = Page::from_start_address(VirtAddr::new(address)).unwrap();
process_manager().read().kernel_process().expect("Failed to get kernel process")
.address_space()
.map(PageRange { start: start_page, end: start_page + (length / PAGE_SIZE as u64) }, MemorySpace::Kernel, PageTableFlags::PRESENT | PageTableFlags::WRITABLE);
}
}
}

0 comments on commit 2e16228

Please sign in to comment.