diff --git a/src/constants.rs b/src/constants.rs index c6856f2..d1b519d 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -12,6 +12,19 @@ pub const SEG_UCODE: u16 = 3; // user code pub const SEG_UDATA: u16 = 4; // user data+stack pub const SEG_TSS: u16 = 5; // this process's task state +// cpu->gdt[NSEGS] holds the above segments. +pub const NSEGS: usize = 6; + +// Application segment type bits +pub const STA_X: u8 = 0x8; // Executable segment +pub const STA_W: u8 = 0x2; // Writeable (non-executable segments) +pub const STA_R: u8 = 0x2; // Readable (executable segments) + +// Memory layout +// We assume that kernel.asm can fit in first 2MB +pub const STARTPROC: u32 = 0x200000; // Start allocating process from here (2MB) +pub const PROCSIZE: u32 = 0x100000; // Size of each process (1MB) + // System segment type bits pub const STS_T32A: u8 = 0x9; // Available 32-bit TSS pub const STS_IG32: u8 = 0xE; // 32-bit Interrupt Gate diff --git a/src/main.rs b/src/main.rs index d6b8240..99d6e4c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,8 @@ mod fs; mod fcntl; mod file; mod log; +mod mmu; +mod vm; use crate::traps::*; fn halt() -> ! { @@ -81,6 +83,7 @@ pub extern "C" fn entryofrust() -> ! { fs::iinit(param::ROOTDEV); log::initlog(param::ROOTDEV); file::mknod("/console", param::CONSOLE as i16, param::CONSOLE as i16); + vm::seginit(); // segment descriptors welcome(); loop { diff --git a/src/mmu.rs b/src/mmu.rs new file mode 100644 index 0000000..505b33a --- /dev/null +++ b/src/mmu.rs @@ -0,0 +1,46 @@ +#![allow(unused_parens)] // False positive from bitfield macro + +use modular_bitfield::prelude::*; + +// Segment Descriptor + +#[bitfield] +#[repr(C, packed)] +#[derive(Clone, Copy, Default, Debug)] +pub struct SegDesc { + lim_15_0: B16, // Low bits of segment limit + base_15_0: B16, // Low bits of segment base address + base_23_16: B8, // Middle bits of segment base address + seg_type: B4, // Segment type (see STA_ constants) + s: B1, // 0 = system, 1 = application + dpl: B2, // Descriptor Privilege Level + p: B1, // Present + lim_19_16: B4, // High bits of segment limit + avl: B1, // Unused (available for software use) + rsv1: B1, // Reserved + db: B1, // 0 = 16-bit segment, 1 = 32-bit segment + g: B1, // Granularity: limit scaled by 4K when set + base_31_24: B8, // High bits of segment base address +} + +impl SegDesc { + /// Create a normal segment descriptor + /// Matches the C macro: SEG(type, base, lim, dpl) + pub fn seg(seg_type: u8, base: u32, lim: u32, dpl: u8) -> Self { + let mut seg = SegDesc::default(); + seg.set_lim_15_0(((lim >> 12) & 0xffff) as u16); + seg.set_base_15_0((base & 0xffff) as u16); + seg.set_base_23_16(((base >> 16) & 0xff) as u8); + seg.set_seg_type(seg_type); + seg.set_s(1); + seg.set_dpl(dpl); + seg.set_p(1); + seg.set_lim_19_16((lim >> 28) as u8); + seg.set_avl(0); + seg.set_rsv1(0); + seg.set_db(1); + seg.set_g(1); + seg.set_base_31_24(((base >> 24) & 0xff) as u8); + seg + } +} diff --git a/src/proc.rs b/src/proc.rs index f904002..44c0fad 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1,18 +1,20 @@ use crate::mp::MP_ONCE; // Import the MP_ONCE static from mp.rs // use core::ptr; -use crate::x86::readeflags; -use crate::param::{NCPU}; -use crate::constants::{FL_IF}; -use crate::lapic; +use crate::constants::NSEGS; +use crate::mmu::SegDesc; #[derive(Debug, Clone, Copy)] pub struct Cpu { pub apicid: u8, // Local APIC ID + pub gdt: [SegDesc; NSEGS], // x86 global descriptor table } impl Cpu { pub const fn new() -> Self { - Self { apicid: 0 } + Self { + apicid: 0, + gdt: [SegDesc::new(); NSEGS], + } } } @@ -22,23 +24,6 @@ pub fn cpuid() -> usize { } pub fn mycpu() -> &'static Cpu { - let apicid: usize; - let mut i: usize = 0; - - if readeflags() & FL_IF != 0 { - panic!("mycpu called with interrupts enabled\n"); - } - - apicid = lapic::lapicid() as usize; - - // Access the cpus array via MP_ONCE let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); - - while i < NCPU { - if (cpus[i].apicid as usize) == apicid { - return &cpus[i]; - } - i += 1; - } - panic!("unknown apicid\n"); + &cpus[0] } \ No newline at end of file diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..20baffb --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,23 @@ +use crate::constants::{SEG_KCODE, SEG_KDATA, SEG_UCODE, SEG_UDATA, STA_X, STA_W, STA_R, STARTPROC, PROCSIZE}; +use crate::mmu::SegDesc; +use crate::proc::cpuid; +use crate::mp::MP_ONCE; +use crate::x86::lgdt; +use core::mem::size_of_val; + +/// Set up CPU's kernel segment descriptors. +/// Run once on entry on each CPU. +pub fn seginit() { + unsafe { + // Map "logical" addresses to virtual addresses using identity map. + let cpus = MP_ONCE.cpus.get().expect("CPUs not initialized"); + let cpu_ptr = cpus.as_ptr() as *mut crate::proc::Cpu; + let c = &mut *cpu_ptr.add(cpuid()); + + c.gdt[SEG_KCODE as usize] = SegDesc::seg(STA_X | STA_R, 0, 0xffffffff, 0); + c.gdt[SEG_KDATA as usize] = SegDesc::seg(STA_W, 0, 0xffffffff, 0); + c.gdt[SEG_UCODE as usize] = SegDesc::seg(STA_X | STA_R, STARTPROC, PROCSIZE, 0); + c.gdt[SEG_UDATA as usize] = SegDesc::seg(STA_W, STARTPROC, PROCSIZE, 0); + lgdt(&c.gdt, size_of_val(&c.gdt)); + } +} diff --git a/src/x86.rs b/src/x86.rs index 4f76a22..8a65859 100644 --- a/src/x86.rs +++ b/src/x86.rs @@ -1,5 +1,7 @@ use core::arch::asm; use crate::traps::GateDesc; +use crate::mmu::SegDesc; +use crate::constants::NSEGS; pub fn inb(port: u16) -> u8 { let result: u8; @@ -36,17 +38,6 @@ pub fn outw(port: u16, value: u16) { } } -pub fn readeflags() -> u32 { - unsafe { - let eflags: u32; - asm!( - "pushfd; pop eax", - out("eax") eflags, - options(nomem, nostack) - ); - eflags - } -} pub fn cli () { unsafe { @@ -75,6 +66,21 @@ pub fn lidt(gdt: *const [GateDesc; 256], size: usize) { } } +pub fn lgdt(gdt: *const [SegDesc; NSEGS], size: usize) { + let pd: [u16; 3] = [ + (size - 1) as u16, + (gdt as *const _) as u16, + ((gdt as *const _ as u32) >> 16) as u16, + ]; + unsafe { + asm!( + "lgdt [{0:e}]", + in(reg) (&pd as *const _ ) as u32, + options(nostack, readonly) + ); + } +} + pub fn noop() { core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); }