x86_64: physical memory init

This commit is contained in:
Mark Poliakov 2023-07-30 16:40:30 +03:00
parent 47f920addf
commit aec161bc47
15 changed files with 536 additions and 47 deletions

View File

@ -12,13 +12,14 @@ yggdrasil-abi = { git = "https://git.alnyan.me/yggdrasil/yggdrasil-abi.git" }
# atomic_enum = "0.2.0"
bitflags = "2.3.3"
# linked_list_allocator = "0.10.5"
# spinning_top = "0.2.5"
linked_list_allocator = "0.10.5"
spinning_top = "0.2.5"
# static_assertions = "1.1.0"
# tock-registers = "0.8.1"
cfg-if = "1.0.0"
bitmap-font = { version = "0.3.0" }
embedded-graphics = "0.8.0"
git-version = "0.3.5"
[dependencies.elf]
version = "0.7.2"

View File

@ -9,7 +9,6 @@ use plat_qemu::PLATFORM;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use crate::{
absolute_address,
arch::{
aarch64::{boot::CPU_INIT_FENCE, cpu::Cpu, devtree::FdtMemoryRegionIter, smp::CPU_COUNT},
Architecture,

View File

@ -7,12 +7,14 @@ cfg_if! {
if #[cfg(target_arch = "aarch64")] {
pub mod aarch64;
pub use aarch64::intrinsics::absolute_address;
pub use aarch64::plat_qemu::{QemuPlatform as PlatformImpl, PLATFORM};
pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
use abi::error::Error;
use cfg_if::cfg_if;
} else if #[cfg(target_arch = "x86_64")] {
#[macro_use]
pub mod x86_64;
pub use x86_64::{

View File

@ -1,14 +1,23 @@
use core::arch::global_asm;
use git_version::git_version;
use yboot_proto::{
v1::FramebufferOption, LoadProtocolHeader, LoadProtocolV1, KERNEL_MAGIC, LOADER_MAGIC,
PROTOCOL_VERSION_1,
v1::{FramebufferOption, MemoryMap},
LoadProtocolHeader, LoadProtocolV1, KERNEL_MAGIC, LOADER_MAGIC, PROTOCOL_VERSION_1,
};
use crate::{
arch::{Architecture, ArchitectureImpl},
arch::{
x86_64::{exception, gdt},
Architecture, ArchitectureImpl,
},
debug,
device::platform::Platform,
mem::{
heap,
phys::{self, PageUsage},
ConvertAddress,
},
};
use super::ARCHITECTURE;
@ -35,6 +44,8 @@ static mut YBOOT_DATA: LoadProtocolV1 = LoadProtocolV1 {
},
kernel_virt_offset: KERNEL_VIRT_OFFSET as _,
memory_map: MemoryMap { address: 0, len: 0 },
opt_framebuffer: FramebufferOption {
req_width: 640,
req_height: 480,
@ -59,16 +70,29 @@ unsafe extern "C" fn __x86_64_upper_entry() -> ! {
ARCHITECTURE.init_primary_debug_sink();
debug::init();
infoln!("Yggdrasil kernel git {} starting", git_version!());
let mut i = 0;
loop {
debugln!("{} {}", env!("PROFILE"), i);
i += 1;
if YBOOT_DATA.memory_map.address > 0xFFFFFFFF {
errorln!("Unhandled case: memory map is above 4GiB");
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
// loop {
// ArchitectureImpl::wait_for_interrupt();
// }
gdt::init();
exception::init_exceptions(0);
// Setup physical memory allocation
ArchitectureImpl::init_physical_memory(&YBOOT_DATA.memory_map);
// Allocate memory for the kernel heap
let heap_base = phys::alloc_pages_contiguous(16, PageUsage::Used)
.expect("Could not allocate a block for heap");
heap::init_heap(heap_base.virtualize(), 16 * 0x1000);
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
global_asm!(

View File

@ -0,0 +1,102 @@
use core::{arch::global_asm, mem::size_of_val};
use crate::arch::{Architecture, ArchitectureImpl};
#[derive(Debug)]
#[repr(C)]
pub struct ExceptionFrame {
rax: usize,
rcx: usize,
rdx: usize,
rbx: usize,
rsi: usize,
rdi: usize,
rbp: usize,
r8: usize,
r9: usize,
r10: usize,
r11: usize,
r12: usize,
r13: usize,
r14: usize,
r15: usize,
}
#[derive(Clone, Copy)]
#[repr(packed)]
struct Entry {
base_lo: u16,
selector: u16,
__res0: u8,
flags: u8,
base_hi: u16,
base_ex: u32,
__res1: u32,
}
#[repr(packed)]
struct Pointer {
limit: u16,
offset: usize,
}
const SIZE: usize = 256;
impl Entry {
const PRESENT: u8 = 1 << 7;
const INT32: u8 = 0xE;
const NULL: Self = Self {
base_lo: 0,
base_hi: 0,
base_ex: 0,
selector: 0,
flags: 0,
__res0: 0,
__res1: 0,
};
const fn new(base: usize, selector: u16, flags: u8) -> Self {
Self {
base_lo: (base & 0xFFFF) as u16,
base_hi: ((base >> 16) & 0xFFFF) as u16,
base_ex: (base >> 32) as u32,
selector,
flags,
__res0: 0,
__res1: 0,
}
}
}
static mut IDT: [Entry; SIZE] = [Entry::NULL; SIZE];
extern "C" fn __x86_64_exception_handler() {
errorln!("An exception occurrred");
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
pub unsafe fn init_exceptions(_cpu_index: usize) {
extern "C" {
static __x86_64_exception_vectors: [usize; 32];
}
for (i, &entry) in __x86_64_exception_vectors.iter().enumerate() {
IDT[i] = Entry::new(entry, 0x08, Entry::PRESENT | Entry::INT32);
}
let idtr = Pointer {
limit: size_of_val(&IDT) as u16 - 1,
offset: &IDT as *const _ as usize,
};
core::arch::asm!("wbinvd; lidt ({0})", in(reg) &idtr, options(att_syntax));
}
global_asm!(
include_str!("vectors.S"),
exception_handler = sym __x86_64_exception_handler,
options(att_syntax)
);

111
src/arch/x86_64/gdt.rs Normal file
View File

@ -0,0 +1,111 @@
// TODO TSS
use core::mem::size_of_val;
#[repr(packed)]
struct Entry {
limit_lo: u16,
base_lo: u16,
base_mi: u8,
access: u8,
flags: u8,
base_hi: u8,
}
#[repr(packed)]
struct Pointer {
limit: u16,
offset: usize,
}
impl Entry {
const FLAG_LONG: u8 = 1 << 5;
const ACC_PRESENT: u8 = 1 << 7;
const ACC_SYSTEM: u8 = 1 << 4;
const ACC_EXECUTE: u8 = 1 << 3;
const ACC_WRITE: u8 = 1 << 1;
const ACC_RING3: u8 = 3 << 5;
const ACC_ACCESS: u8 = 1 << 0;
const NULL: Self = Self {
base_lo: 0,
base_mi: 0,
base_hi: 0,
access: 0,
flags: 0,
limit_lo: 0,
};
const fn new(base: u32, limit: u32, flags: u8, access: u8) -> Self {
Self {
base_lo: (base & 0xFFFF) as u16,
base_mi: ((base >> 16) & 0xFF) as u8,
base_hi: ((base >> 24) & 0xFF) as u8,
access,
flags: (flags & 0xF0) | (((limit >> 16) & 0xF) as u8),
limit_lo: (limit & 0xFFFF) as u16,
}
}
}
const SIZE: usize = 3;
// TODO per-CPU
static mut GDT: [Entry; SIZE] = [
// 0x00, NULL
Entry::NULL,
// 0x08, Ring0 CS64
Entry::new(
0,
0,
Entry::FLAG_LONG,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_EXECUTE,
),
// 0x10, Ring0 DS64
Entry::new(
0,
0,
0,
Entry::ACC_PRESENT | Entry::ACC_SYSTEM | Entry::ACC_WRITE,
),
];
pub unsafe fn init() {
let gdtr = Pointer {
limit: size_of_val(&GDT) as u16 - 1,
offset: &GDT as *const _ as usize,
};
core::arch::asm!(
r#"
wbinvd
lgdt ({0})
// Have to use iretq here
mov %rsp, %rcx
leaq 1f(%rip), %rax
// SS:RSP
pushq $0x10
pushq %rcx
// RFLAGS
pushfq
// CS:RIP
pushq $0x08
pushq %rax
iretq
1:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
"#,
in(reg) &gdtr,
out("rax") _,
options(att_syntax)
);
}

View File

@ -0,0 +1,12 @@
/// Returns an absolute address to the given symbol
#[macro_export]
macro_rules! absolute_address {
($sym:expr) => {{
let mut _x: usize;
unsafe {
core::arch::asm!("movabsq ${1}, {0}", out(reg) _x, sym $sym, options(att_syntax));
}
_x
}};
}

View File

@ -1,5 +1,5 @@
use abi::error::Error;
use yboot_proto::v1::FramebufferOption;
use yboot_proto::{v1::FramebufferOption, AvailableRegion, IterableMemoryMap};
use crate::{
arch::x86_64::table::{init_fixed_tables, KERNEL_TABLES},
@ -8,14 +8,59 @@ use crate::{
display::{fb_console::FramebufferConsole, linear_fb::LinearFramebuffer},
platform::Platform,
},
mem::phys::{self, reserved::reserve_region, PhysicalMemoryRegion},
util::OneTimeInit,
};
use super::Architecture;
#[macro_use]
pub mod intrinsics;
pub mod boot;
pub mod exception;
pub mod gdt;
pub mod table;
pub trait AbstractAvailableRegion {
fn start_address(&self) -> usize;
fn page_count(&self) -> usize;
}
pub trait AbstractMemoryMap<'a>: 'a {
type AvailableRegion: AbstractAvailableRegion;
type Iter: Iterator<Item = &'a Self::AvailableRegion> + Clone;
fn reserved_range(&self) -> PhysicalMemoryRegion;
fn iter(&self) -> Self::Iter;
}
impl<T: AvailableRegion> AbstractAvailableRegion for T {
fn start_address(&self) -> usize {
<T as AvailableRegion>::start_address(self)
}
fn page_count(&self) -> usize {
<T as AvailableRegion>::page_count(self)
}
}
impl<'a, T: IterableMemoryMap<'a> + 'a> AbstractMemoryMap<'a> for T {
type AvailableRegion = T::Entry;
type Iter = T::Iter;
fn reserved_range(&self) -> PhysicalMemoryRegion {
PhysicalMemoryRegion {
base: self.data_physical_base(),
size: (self.data_size() + 0xFFF) & !0xFFF,
}
}
fn iter(&self) -> Self::Iter {
<T as IterableMemoryMap>::iter_with_offset(self, X86_64::KERNEL_VIRT_OFFSET)
}
}
pub struct X86_64 {
yboot_framebuffer: OneTimeInit<FramebufferOption>,
}
@ -61,6 +106,8 @@ impl Architecture for X86_64 {
}
impl Platform for X86_64 {
const KERNEL_PHYS_BASE: usize = 0x400000;
unsafe fn init(&'static self, _is_bsp: bool) -> Result<(), Error> {
Ok(())
}
@ -100,6 +147,27 @@ impl Platform for X86_64 {
}
}
impl X86_64 {
unsafe fn init_physical_memory<'a, M: AbstractMemoryMap<'a>>(memory_map: &M) {
// Reserve the lower 8MiB of memory
reserve_region(
"lower-memory",
PhysicalMemoryRegion {
base: 0,
size: 8 << 21,
},
);
// Reserve memory map
reserve_region("memory-map", memory_map.reserved_range());
phys::init_from_iter(memory_map.iter().map(|r| PhysicalMemoryRegion {
base: r.start_address(),
size: r.page_count() * 0x1000,
}))
.expect("Failed to initialize the physical memory manager");
}
}
pub static ARCHITECTURE: X86_64 = X86_64 {
yboot_framebuffer: OneTimeInit::new(),
};

View File

@ -24,6 +24,7 @@ pub struct FixedTables {
// 512 entries
device_l3: PageTable<L3>,
device_l2i: usize,
device_l3i: usize,
}
@ -43,6 +44,7 @@ impl FixedTables {
device_l2: PageTable::zeroed(),
device_l3: PageTable::zeroed(),
device_l2i: 1,
device_l3i: 0,
}
}
@ -51,8 +53,20 @@ impl FixedTables {
if count > 512 * 512 {
panic!("Unsupported device memory mapping size");
} else if count > 512 {
let count = (count + 511) / 512;
// 2MiB mappings
todo!();
if self.device_l2i + count > 512 {
return Err(Error::OutOfMemory);
}
let virt = DEVICE_VIRT_OFFSET + (self.device_l2i << 21);
for i in 0..count {
self.device_l2[self.device_l2i + i] =
PageEntry::block(phys, PageAttributes::WRITABLE);
}
self.device_l2i += count;
Ok(virt)
} else {
// 4KiB mappings
if self.device_l3i + count > 512 {

139
src/arch/x86_64/vectors.S Normal file
View File

@ -0,0 +1,139 @@
.macro ISR_NERR, n
__x86_64_exc_\n:
cli
pushq $0
pushq $\n
jmp __x86_64_exc_common
.endm
.macro ISR_YERR, n
__x86_64_exc_\n:
cli
pushq $\n
jmp __x86_64_exc_common
.endm
// 16 general-purpose registers
.set PT_REGS_SIZE, 15 * 8
.macro EXC_SAVE_STATE
sub $PT_REGS_SIZE, %rsp
mov %rax, 0(%rsp)
mov %rcx, 8(%rsp)
mov %rdx, 16(%rsp)
mov %rbx, 32(%rsp)
mov %rsi, 36(%rsp)
mov %rdi, 40(%rsp)
mov %rbp, 48(%rsp)
mov %r8, 52(%rsp)
mov %r9, 56(%rsp)
mov %r10, 60(%rsp)
mov %r11, 64(%rsp)
mov %r12, 68(%rsp)
mov %r13, 72(%rsp)
mov %r14, 76(%rsp)
mov %r15, 80(%rsp)
.endm
.macro EXC_RESTORE_STATE
mov 0(%rsp), %rax
mov 8(%rsp), %rcx
mov 16(%rsp), %rdx
mov 32(%rsp), %rbx
mov 36(%rsp), %rsi
mov 40(%rsp), %rdi
mov 48(%rsp), %rbp
mov 52(%rsp), %r8
mov 56(%rsp), %r9
mov 60(%rsp), %r10
mov 64(%rsp), %r11
mov 68(%rsp), %r12
mov 72(%rsp), %r13
mov 76(%rsp), %r14
mov 80(%rsp), %r15
.endm
.global __x86_64_exception_vectors
.section .text
__x86_64_exc_common:
EXC_SAVE_STATE
mov %rsp, %rdi
call {exception_handler}
// TODO
1:
cli
hlt
jmp 1b
ISR_NERR 0
ISR_NERR 1
ISR_NERR 2
ISR_NERR 3
ISR_NERR 4
ISR_NERR 5
ISR_NERR 6
ISR_NERR 7
ISR_YERR 8
ISR_NERR 9
ISR_YERR 10
ISR_YERR 11
ISR_YERR 12
ISR_YERR 13
ISR_YERR 14
ISR_NERR 15
ISR_NERR 16
ISR_YERR 17
ISR_NERR 18
ISR_NERR 19
ISR_NERR 20
ISR_NERR 21
ISR_NERR 22
ISR_NERR 23
ISR_NERR 24
ISR_NERR 25
ISR_NERR 26
ISR_NERR 27
ISR_NERR 28
ISR_NERR 29
ISR_YERR 30
ISR_NERR 31
.section .rodata
.global __x86_64_exception_vectors
.p2align 4
__x86_64_exception_vectors:
.quad __x86_64_exc_0
.quad __x86_64_exc_1
.quad __x86_64_exc_2
.quad __x86_64_exc_3
.quad __x86_64_exc_4
.quad __x86_64_exc_5
.quad __x86_64_exc_6
.quad __x86_64_exc_7
.quad __x86_64_exc_8
.quad __x86_64_exc_9
.quad __x86_64_exc_10
.quad __x86_64_exc_11
.quad __x86_64_exc_12
.quad __x86_64_exc_13
.quad __x86_64_exc_14
.quad __x86_64_exc_15
.quad __x86_64_exc_16
.quad __x86_64_exc_17
.quad __x86_64_exc_18
.quad __x86_64_exc_19
.quad __x86_64_exc_20
.quad __x86_64_exc_21
.quad __x86_64_exc_22
.quad __x86_64_exc_23
.quad __x86_64_exc_24
.quad __x86_64_exc_25
.quad __x86_64_exc_26
.quad __x86_64_exc_27
.quad __x86_64_exc_28
.quad __x86_64_exc_29
.quad __x86_64_exc_30
.quad __x86_64_exc_31

View File

@ -39,9 +39,14 @@ impl LinearFramebuffer {
stride,
};
Ok(Self {
let res = Self {
inner: IrqSafeSpinlock::new(inner),
})
};
// Clear the screen
res.lock().fill_rows(0, height, 0);
Ok(res)
}
// TODO doesn't actually lock

View File

@ -12,7 +12,7 @@ pub trait Platform {
// type IrqNumber;
/// Address, to which the kernel is expected to be loaded for this platform
// const KERNEL_PHYS_BASE: usize;
const KERNEL_PHYS_BASE: usize;
/// Initializes the platform devices to their usable state.
///
@ -21,27 +21,24 @@ pub trait Platform {
/// Unsafe to call if the platform has already been initialized.
unsafe fn init(&'static self, is_bsp: bool) -> Result<(), Error>;
// TODO use init_debug instead and let the platform code handle it
/// Initializes the primary output device to provide the debugging output as early as possible.
///
/// # Safety
///
/// Unsafe to call if the device has already been initialized.
unsafe fn init_primary_debug_sink(&self);
// /// Initializes the primary serial device to provide the debugging output as early as possible.
// ///
// /// # Safety
// ///
// /// Unsafe to call if the device has already been initialized.
// unsafe fn init_primary_serial(&self);
/// Returns a display name for the platform
fn name(&self) -> &'static str;
/// Returns a reference to the primary debug output device.
///
/// # Note
///
/// May not be initialized at the moment of calling.
fn primary_debug_sink(&self) -> Option<&dyn DebugSink>;
// /// Returns a reference to the primary serial device.
// ///
// /// # Note
// ///
// /// May not be initialized at the moment of calling.
// fn primary_serial(&self) -> Option<&dyn SerialDevice>;
// /// Returns a reference to the platform's interrupt controller.
// ///
// /// # Note

View File

@ -27,7 +27,7 @@ extern crate yggdrasil_abi as abi;
// use task::process::Process;
// use vfs::{Filesystem, IoContext, VnodeRef};
//
// extern crate alloc;
extern crate alloc;
//
#[macro_use]
pub mod debug;
@ -36,7 +36,13 @@ pub mod arch;
#[panic_handler]
fn panic_handler(_pi: &core::panic::PanicInfo) -> ! {
loop {}
use arch::{Architecture, ArchitectureImpl};
fatalln!("KERNEL PANIC");
loop {
ArchitectureImpl::wait_for_interrupt();
}
}
//
pub mod device;

View File

@ -1,27 +1,25 @@
//! Memory management utilities and types
use core::{alloc::Layout, mem::size_of};
// use core::{alloc::Layout, mem::size_of};
// use abi::error::Error;
//
// use crate::{
// arch::{Architecture, ArchitectureImpl, PlatformImpl},
// device::platform::Platform,
// };
use crate::{
arch::{Architecture, ArchitectureImpl, PlatformImpl},
device::platform::Platform,
};
//
// use self::table::AddressSpace;
pub mod device;
// pub mod heap;
// pub mod phys;
pub mod heap;
pub mod phys;
pub mod table;
/// Kernel's physical load address
// pub const KERNEL_PHYS_BASE: usize = PlatformImpl::KERNEL_PHYS_BASE;
pub const KERNEL_PHYS_BASE: usize = PlatformImpl::KERNEL_PHYS_BASE;
/// Kernel's virtual memory mapping offset (i.e. kernel's virtual address is [KERNEL_PHYS_BASE] +
/// [KERNEL_VIRT_OFFSET])
// pub const KERNEL_VIRT_OFFSET: usize = ArchitectureImpl::KERNEL_VIRT_OFFSET;
// TODO fix this
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
pub const KERNEL_VIRT_OFFSET: usize = ArchitectureImpl::KERNEL_VIRT_OFFSET;
/// Interface for converting between address spaces.
///

View File

@ -4,7 +4,6 @@ use core::{iter::StepBy, mem::size_of, ops::Range};
use abi::error::Error;
use crate::{
absolute_address,
debug::LogLevel,
mem::{
phys::reserved::{is_reserved, reserve_region},
@ -16,6 +15,10 @@ use crate::{
use self::manager::PhysicalMemoryManager;
// Enumerating lots of pages is slow and I'm too lazy right now to write a better algorithm, so
// capping the page count helps
const PHYS_MEMORY_PAGE_CAP: usize = 65536;
pub mod manager;
pub mod reserved;
@ -229,6 +232,10 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
let mut page_count = 0;
for region in it {
if page_count >= PHYS_MEMORY_PAGE_CAP {
break;
}
for page in region.pages() {
if is_reserved(page) {
continue;
@ -236,10 +243,14 @@ pub unsafe fn init_from_iter<I: Iterator<Item = PhysicalMemoryRegion> + Clone>(
manager.add_available_page(page);
page_count += 1;
if page_count >= PHYS_MEMORY_PAGE_CAP {
break;
}
}
}
infoln!("{} available pages", page_count);
infoln!("{} available pages ({}KiB)", page_count, page_count * 4);
PHYSICAL_MEMORY.init(IrqSafeSpinlock::new(manager));
Ok(())