feat(kernel): kernel-owned memory model
- FrameTable: per-frame owner/use, replaces PagePool + ProcessMemory - Quota per process, page tables charged to owner - Regions for shared mem + device MMIO, torn down w/ owner - Platform: create/destroy AS, map/unmap, zero_frame; drop translate_va - RequestPages/ReleasePages/MapPage(move) checked vs kernel records - user window 0x1000..32MiB, private pts by construction - boot loader + device setup go through kernel api - kernel built in place, boot allocator bounded - isolation tests, randomized invariant test, FrameTable kani proofs - fixes: inflated release, unmapped release, dst va unchecked, heap overflow, unbounded pt alloc, unzeroed frames, double free
This commit is contained in:
@@ -1,13 +1,9 @@
|
||||
use super::{dma_buffer, info_page, kern, map_mmio};
|
||||
use crate::hw::uart::Uart;
|
||||
use crate::hw::virtio_gpu::VirtioGpu;
|
||||
use crate::mmu;
|
||||
use types::display::Dimension;
|
||||
use types::id::ProcessId;
|
||||
|
||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||
unsafe { &mut *crate::kernel_ptr() }
|
||||
}
|
||||
|
||||
const GPU_VA: usize = 0x20000;
|
||||
const VQUEUE_VA: usize = 0x21000;
|
||||
const CMDBUF_VA: usize = 0x23000;
|
||||
@@ -15,6 +11,8 @@ const PHYSINFO_VA: usize = 0x24000;
|
||||
const FB_VA: usize = 0x100000;
|
||||
const DEFAULT_WIDTH: Dimension = Dimension::new(640);
|
||||
const DEFAULT_HEIGHT: Dimension = Dimension::new(480);
|
||||
/// The framebuffer alone is ~1000 pages at 1280x800.
|
||||
const DISPLAY_PAGE_LIMIT: usize = 2048;
|
||||
|
||||
pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
||||
let gpu = match VirtioGpu::probe() {
|
||||
@@ -25,16 +23,13 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
||||
}
|
||||
};
|
||||
|
||||
let pt_addr = kern().platform_mut().page_table_for(display_pid);
|
||||
let pt = unsafe { &mut *(pt_addr as *mut mmu::PageTable) };
|
||||
kern()
|
||||
.set_page_limit(display_pid, DISPLAY_PAGE_LIMIT)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "display quota"));
|
||||
|
||||
map_page(pt, GPU_VA, gpu.base());
|
||||
|
||||
let vq_phys = alloc_contiguous(2);
|
||||
map_range(pt, VQUEUE_VA, vq_phys, 2);
|
||||
|
||||
let cmd_phys = mmu::allocator::alloc_zeroed_page();
|
||||
map_page(pt, CMDBUF_VA, cmd_phys);
|
||||
map_mmio(uart, display_pid, GPU_VA, gpu.base(), 1);
|
||||
let vq_phys = dma_buffer(uart, display_pid, VQUEUE_VA, 2);
|
||||
let cmd_phys = dma_buffer(uart, display_pid, CMDBUF_VA, 1);
|
||||
|
||||
gpu.init_device(vq_phys);
|
||||
|
||||
@@ -45,7 +40,6 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
||||
cmd_va: types::address::VirtualAddress::from_raw(cmd_phys),
|
||||
resp_pa: types::address::PhysicalAddress::from_raw(cmd_phys + 512),
|
||||
};
|
||||
use hw_virtio::mmio::MmioAccess;
|
||||
|
||||
let (width, height) = match hw_virtio::gpu::get_display_info(gpu.mmio(), &queue, &dma) {
|
||||
Some((w, h)) => {
|
||||
@@ -62,20 +56,21 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
||||
}
|
||||
};
|
||||
|
||||
let fb_pages = (width.raw() as usize * height.raw() as usize * 4 + 4095) / 4096;
|
||||
let fb_phys = alloc_contiguous(fb_pages);
|
||||
map_range(pt, FB_VA, fb_phys, fb_pages);
|
||||
let fb_pages = (width.raw() as usize * height.raw() as usize * 4).div_ceil(4096);
|
||||
let fb_phys = dma_buffer(uart, display_pid, FB_VA, fb_pages);
|
||||
|
||||
let info_phys = mmu::allocator::alloc_zeroed_page();
|
||||
map_page(pt, PHYSINFO_VA, info_phys);
|
||||
unsafe {
|
||||
let info = info_phys as *mut u64;
|
||||
core::ptr::write_volatile(info, cmd_phys as u64);
|
||||
core::ptr::write_volatile(info.add(1), (cmd_phys + 512) as u64);
|
||||
core::ptr::write_volatile(info.add(2), fb_phys as u64);
|
||||
core::ptr::write_volatile(info.add(3), width.raw() as u64);
|
||||
core::ptr::write_volatile(info.add(4), height.raw() as u64);
|
||||
}
|
||||
info_page(
|
||||
uart,
|
||||
display_pid,
|
||||
PHYSINFO_VA,
|
||||
&[
|
||||
cmd_phys as u64,
|
||||
(cmd_phys + 512) as u64,
|
||||
fb_phys as u64,
|
||||
width.raw() as u64,
|
||||
height.raw() as u64,
|
||||
],
|
||||
);
|
||||
|
||||
if gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, width, height) {
|
||||
uart.write_str("k-os: VirtIO GPU configured\n");
|
||||
@@ -83,29 +78,3 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
||||
uart.write_str("k-os: VirtIO GPU setup failed (display will be headless)\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(va),
|
||||
pa: types::address::PhysicalAddress::from_raw(pa),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn map_range(pt: &mut mmu::PageTable, va_base: usize, pa_base: usize, count: usize) {
|
||||
for i in 0..count {
|
||||
map_page(pt, va_base + i * 4096, pa_base + i * 4096);
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_contiguous(pages: usize) -> usize {
|
||||
let first = mmu::allocator::alloc_zeroed_page();
|
||||
for _ in 1..pages {
|
||||
mmu::allocator::alloc_zeroed_page();
|
||||
}
|
||||
first
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{dma_buffer, info_page, map_mmio};
|
||||
use crate::board;
|
||||
use crate::hw::uart::Uart;
|
||||
use crate::{board, mmu};
|
||||
use types::id::ProcessId;
|
||||
|
||||
const UART_VA: usize = 0x20000;
|
||||
@@ -8,34 +9,22 @@ const INPUT_VQUEUE_VA: usize = 0x26000;
|
||||
const INPUT_EVBUF_VA: usize = 0x28000;
|
||||
const INPUT_PHYSINFO_VA: usize = 0x29000;
|
||||
|
||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||
unsafe { &mut *crate::kernel_ptr() }
|
||||
}
|
||||
|
||||
pub fn configure(uart: &Uart, input_pid: ProcessId) {
|
||||
let pt_addr = kern().platform_mut().page_table_for(input_pid);
|
||||
let pt = unsafe { &mut *(pt_addr as *mut mmu::PageTable) };
|
||||
|
||||
// Always map UART for fallback
|
||||
map_page(pt, UART_VA, board::UART_BASE);
|
||||
map_mmio(uart, input_pid, UART_VA, board::UART_BASE, 1);
|
||||
|
||||
// Try to find VirtIO input device
|
||||
if let Some(mmio) = hw_virtio::mmio::scan_for_device(hw_virtio::mmio::DEVICE_ID_INPUT) {
|
||||
let base = mmio.base();
|
||||
map_page(pt, INPUT_MMIO_VA, base);
|
||||
|
||||
let vq_phys = alloc_contiguous(2);
|
||||
map_range(pt, INPUT_VQUEUE_VA, vq_phys, 2);
|
||||
|
||||
let evbuf_phys = mmu::allocator::alloc_zeroed_page();
|
||||
map_page(pt, INPUT_EVBUF_VA, evbuf_phys);
|
||||
|
||||
let info_phys = mmu::allocator::alloc_zeroed_page();
|
||||
map_page(pt, INPUT_PHYSINFO_VA, info_phys);
|
||||
unsafe {
|
||||
core::ptr::write_volatile(info_phys as *mut u64, evbuf_phys as u64);
|
||||
core::ptr::write_volatile((info_phys as *mut u64).add(1), vq_phys as u64);
|
||||
}
|
||||
map_mmio(uart, input_pid, INPUT_MMIO_VA, base, 1);
|
||||
let vq_phys = dma_buffer(uart, input_pid, INPUT_VQUEUE_VA, 2);
|
||||
let evbuf_phys = dma_buffer(uart, input_pid, INPUT_EVBUF_VA, 1);
|
||||
info_page(
|
||||
uart,
|
||||
input_pid,
|
||||
INPUT_PHYSINFO_VA,
|
||||
&[evbuf_phys as u64, vq_phys as u64],
|
||||
);
|
||||
|
||||
hw_virtio::mmio::init_legacy_device(&mmio, vq_phys, 32);
|
||||
uart.write_str("k-os: VirtIO keyboard configured\n");
|
||||
@@ -43,29 +32,3 @@ pub fn configure(uart: &Uart, input_pid: ProcessId) {
|
||||
uart.write_str("k-os: UART mapped for input\n");
|
||||
}
|
||||
}
|
||||
|
||||
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(va),
|
||||
pa: types::address::PhysicalAddress::from_raw(pa),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn map_range(pt: &mut mmu::PageTable, va_base: usize, pa_base: usize, count: usize) {
|
||||
for i in 0..count {
|
||||
map_page(pt, va_base + i * 4096, pa_base + i * 4096);
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_contiguous(pages: usize) -> usize {
|
||||
let first = mmu::allocator::alloc_zeroed_page();
|
||||
for _ in 1..pages {
|
||||
mmu::allocator::alloc_zeroed_page();
|
||||
}
|
||||
first
|
||||
}
|
||||
|
||||
@@ -1,3 +1,84 @@
|
||||
pub mod gpu_setup;
|
||||
pub mod input_setup;
|
||||
pub mod storage_setup;
|
||||
|
||||
use crate::hw::uart::Uart;
|
||||
use types::address::{PhysicalAddress, VirtualAddress};
|
||||
use types::bounded::PageCount;
|
||||
use types::id::ProcessId;
|
||||
use types::mapping::PagePerms;
|
||||
|
||||
// Boot-time device wiring. Everything here goes through the kernel's
|
||||
// mapping API so the kernel knows about every page a driver can touch.
|
||||
// P5 replaces these hardcoded grants with capabilities handed to init.
|
||||
|
||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||
unsafe { &mut *crate::kernel_ptr() }
|
||||
}
|
||||
|
||||
fn pages(uart: &Uart, n: usize) -> PageCount {
|
||||
u16::try_from(n)
|
||||
.ok()
|
||||
.and_then(PageCount::new)
|
||||
.unwrap_or_else(|| crate::halt(uart, "bad page count"))
|
||||
}
|
||||
|
||||
/// Maps device MMIO at `pa` into `pid` at `va`.
|
||||
pub(crate) fn map_mmio(uart: &Uart, pid: ProcessId, va: usize, pa: usize, n: usize) {
|
||||
let id = kern()
|
||||
.create_device_region(pid, PhysicalAddress::from_raw(pa), pages(uart, n))
|
||||
.unwrap_or_else(|_| crate::halt(uart, "device region failed"));
|
||||
kern()
|
||||
.map_region(id, pid, VirtualAddress::from_raw(va), PagePerms::ReadWrite)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "device map failed"));
|
||||
}
|
||||
|
||||
/// Physically contiguous, zeroed DMA memory for `owner`, mapped read-write
|
||||
/// at `va`. Returns its physical base for programming the device.
|
||||
pub(crate) fn dma_buffer(uart: &Uart, owner: ProcessId, va: usize, n: usize) -> usize {
|
||||
let id = kern()
|
||||
.create_pool_region(owner, pages(uart, n))
|
||||
.unwrap_or_else(|_| crate::halt(uart, "DMA region failed"));
|
||||
kern()
|
||||
.map_region(
|
||||
id,
|
||||
owner,
|
||||
VirtualAddress::from_raw(va),
|
||||
PagePerms::ReadWrite,
|
||||
)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "DMA map failed"));
|
||||
kern()
|
||||
.region_base(id)
|
||||
.unwrap_or_else(|| crate::halt(uart, "DMA region vanished"))
|
||||
.raw()
|
||||
}
|
||||
|
||||
/// Shares a fresh region between `owner` and `other` at the same `va`.
|
||||
pub(crate) fn shared_buffer(uart: &Uart, owner: ProcessId, other: ProcessId, va: usize, n: usize) {
|
||||
let id = kern()
|
||||
.create_pool_region(owner, pages(uart, n))
|
||||
.unwrap_or_else(|_| crate::halt(uart, "shared region failed"));
|
||||
for pid in [owner, other] {
|
||||
kern()
|
||||
.map_region(id, pid, VirtualAddress::from_raw(va), PagePerms::ReadWrite)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "shared map failed"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes `words` into a fresh page mapped at `va` in `pid`, so a driver
|
||||
/// can learn the physical addresses of its DMA buffers.
|
||||
pub(crate) fn info_page(uart: &Uart, pid: ProcessId, va: usize, words: &[u64]) {
|
||||
let va = VirtualAddress::from_raw(va);
|
||||
kern()
|
||||
.map_fresh_range(pid, va, 1, PagePerms::ReadOnly)
|
||||
.unwrap_or_else(|_| crate::halt(uart, "info page failed"));
|
||||
let pa = kern()
|
||||
.user_page_frame(pid, va)
|
||||
.unwrap_or_else(|| crate::halt(uart, "info page vanished"));
|
||||
let info = pa.raw() as *mut u64;
|
||||
for (i, &w) in words.iter().enumerate() {
|
||||
// SAFETY: `pa` is a pool frame just mapped for `pid`, identity
|
||||
// mapped in kernel space, and `words` fits in one page.
|
||||
unsafe { core::ptr::write_volatile(info.add(i), w) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
use super::{dma_buffer, info_page, map_mmio, shared_buffer};
|
||||
use crate::hw::uart::Uart;
|
||||
use crate::mmu;
|
||||
use types::id::ProcessId;
|
||||
|
||||
fn kern() -> &'static mut kernel::Kernel<crate::platform::RiscVPlatform> {
|
||||
unsafe { &mut *crate::kernel_ptr() }
|
||||
}
|
||||
|
||||
const VIRTIO_VA: usize = 0x20000;
|
||||
const VQUEUE_VA: usize = 0x21000;
|
||||
const REQHDR_VA: usize = 0x23000;
|
||||
@@ -14,111 +10,27 @@ const SHARED_BUF_VA: usize = 0x30000;
|
||||
const SHARED_BUF_PAGES: usize = 16;
|
||||
|
||||
pub fn configure(uart: &Uart, init_pid: ProcessId, storage_pid: ProcessId, virtio_base: usize) {
|
||||
let storage_pt = get_pt(storage_pid);
|
||||
let init_pt = get_pt(init_pid);
|
||||
|
||||
map_virtio_mmio(storage_pt, virtio_base);
|
||||
let vqueue_phys = alloc_and_map_virtqueue(storage_pt);
|
||||
let reqhdr_phys = alloc_and_map_reqhdr(storage_pt);
|
||||
write_phys_info(storage_pt, reqhdr_phys);
|
||||
map_mmio(uart, storage_pid, VIRTIO_VA, virtio_base, 1);
|
||||
let vqueue_phys = dma_buffer(uart, storage_pid, VQUEUE_VA, 2);
|
||||
let reqhdr_phys = dma_buffer(uart, storage_pid, REQHDR_VA, 1);
|
||||
info_page(
|
||||
uart,
|
||||
storage_pid,
|
||||
PHYSINFO_VA,
|
||||
&[
|
||||
reqhdr_phys as u64,
|
||||
(reqhdr_phys + 16) as u64,
|
||||
(reqhdr_phys + 32) as u64,
|
||||
],
|
||||
);
|
||||
reinit_device(virtio_base, vqueue_phys);
|
||||
uart.write_str("k-os: VirtIO configured for storage\n");
|
||||
|
||||
map_shared_buffer(init_pt, storage_pt);
|
||||
shared_buffer(uart, init_pid, storage_pid, SHARED_BUF_VA, SHARED_BUF_PAGES);
|
||||
uart.write_str("k-os: shared buffer mapped\n");
|
||||
}
|
||||
|
||||
fn get_pt(pid: ProcessId) -> &'static mut mmu::PageTable {
|
||||
let addr = kern().platform_mut().page_table_for(pid);
|
||||
unsafe { &mut *(addr as *mut mmu::PageTable) }
|
||||
}
|
||||
|
||||
fn map_virtio_mmio(pt: &mut mmu::PageTable, virtio_base: usize) {
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(VIRTIO_VA),
|
||||
pa: types::address::PhysicalAddress::from_raw(virtio_base),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn alloc_and_map_virtqueue(pt: &mut mmu::PageTable) -> usize {
|
||||
let phys = mmu::allocator::alloc_zeroed_page();
|
||||
mmu::allocator::alloc_zeroed_page();
|
||||
for i in 0..2usize {
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(VQUEUE_VA + i * 4096),
|
||||
pa: types::address::PhysicalAddress::from_raw(phys + i * 4096),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
}
|
||||
phys
|
||||
}
|
||||
|
||||
fn alloc_and_map_reqhdr(pt: &mut mmu::PageTable) -> usize {
|
||||
let phys = mmu::allocator::alloc_zeroed_page();
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(REQHDR_VA),
|
||||
pa: types::address::PhysicalAddress::from_raw(phys),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
phys
|
||||
}
|
||||
|
||||
fn write_phys_info(pt: &mut mmu::PageTable, reqhdr_phys: usize) {
|
||||
let phys = mmu::allocator::alloc_zeroed_page();
|
||||
mmu::map(
|
||||
pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(PHYSINFO_VA),
|
||||
pa: types::address::PhysicalAddress::from_raw(phys),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
unsafe {
|
||||
let info = phys as *mut u64;
|
||||
core::ptr::write_volatile(info, reqhdr_phys as u64);
|
||||
core::ptr::write_volatile(info.add(1), (reqhdr_phys + 16) as u64);
|
||||
core::ptr::write_volatile(info.add(2), (reqhdr_phys + 32) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn reinit_device(base: usize, vqueue_phys: usize) {
|
||||
let mmio = hw_virtio::mmio::DirectMmio::new(base);
|
||||
hw_virtio::mmio::init_legacy_device(&mmio, vqueue_phys, hw_virtio::queue::QUEUE_SIZE as u32);
|
||||
}
|
||||
|
||||
fn map_shared_buffer(init_pt: &mut mmu::PageTable, storage_pt: &mut mmu::PageTable) {
|
||||
for i in 0..SHARED_BUF_PAGES {
|
||||
let page = mmu::allocator::alloc_zeroed_page();
|
||||
let va = SHARED_BUF_VA + i * 4096;
|
||||
let mapping = mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(va),
|
||||
pa: types::address::PhysicalAddress::from_raw(page),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
};
|
||||
mmu::map(init_pt, mapping);
|
||||
mmu::map(
|
||||
storage_pt,
|
||||
mmu::Mapping {
|
||||
va: types::address::VirtualAddress::from_raw(va),
|
||||
pa: types::address::PhysicalAddress::from_raw(page),
|
||||
pages: 1,
|
||||
flags: mmu::Sv39Flags::user_rw(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user