~demindiro/norost-b

64cd7c63badf9addb1db90a676f2b9e1f5bd5c73 — David Hoppenbrouwers 5 months ago 8591488 + a5d8ad0
Merge branch 'cleanup'
60 files changed, 167 insertions(+), 813 deletions(-)

M .cargo/config.toml
M Cargo.toml
M Makefile
M boot/amd64/Cargo.toml
A boot/amd64/i686-unknown-norostb.json
M boot/amd64/src/cpuid.rs
M boot/amd64/src/main.rs
D clean.sh
M drivers/hello_world/hello.s
M drivers/virtio_block/Cargo.toml
M drivers/virtio_block/src/main.rs
M kernel/Cargo.toml
M kernel/src/arch/amd64/mod.rs
M kernel/src/arch/amd64/msr.rs
M kernel/src/arch/amd64/syscall.rs
M kernel/src/arch/amd64/virtual/address_space.rs
M kernel/src/arch/amd64/virtual/common.rs
M kernel/src/arch/amd64/virtual/mod.rs
M kernel/src/arch/amd64/virtual/pml4.rs
M kernel/src/driver/acpi.rs
M kernel/src/driver/apic/io_apic.rs
M kernel/src/driver/apic/local_apic.rs
M kernel/src/driver/apic/mod.rs
M kernel/src/driver/hpet/mod.rs
M kernel/src/driver/mod.rs
M kernel/src/driver/pci/device.rs
M kernel/src/driver/pci/mod.rs
M kernel/src/driver/pci/table.rs
M kernel/src/driver/rtc/mod.rs
D kernel/src/ipc/mod.rs
D kernel/src/ipc/port.rs
D kernel/src/ipc/queue.rs
M kernel/src/log.rs
M kernel/src/main.rs
M kernel/src/memory/frame/mod.rs
M kernel/src/memory/virtual.rs
M kernel/src/object_table/mod.rs
M kernel/src/object_table/streaming.rs
M kernel/src/scheduler/memory_object.rs
M kernel/src/scheduler/mod.rs
M kernel/src/scheduler/process/elf.rs
M kernel/src/scheduler/process/mod.rs
M kernel/src/scheduler/round_robin.rs
M kernel/src/scheduler/syscall.rs
M kernel/src/scheduler/thread.rs
M kernel/src/scheduler/waker.rs
M kernel/src/sync/mutex.rs
M kernel/src/sync/spinlock.rs
M kernel/src/time/mod.rs
M lib/rust/kernel/Cargo.toml
M lib/rust/kernel/src/lib.rs
D lib/rust/kernel/src/object_table/mod.rs
D lib/rust/kernel/src/object_table/streaming.rs
M lib/rust/kernel/src/syscall.rs
M lib/rust/virtio/Cargo.toml
M lib/rust/virtio/src/pci.rs
M lib/rust/virtio_block/src/lib.rs
M mkiso.sh
M mkkernel.sh
M run.sh
M .cargo/config.toml => .cargo/config.toml +6 -0
@@ 1,3 1,9 @@
[build]
target = "x86_64-unknown-norostb.json"

[unstable]
build-std-features = ["compiler-builtins-mem"]
build-std = ["core", "compiler_builtins", "alloc"]

[target.x86_64-unknown-norostb]
runner = "./run.sh"

M Cargo.toml => Cargo.toml +1 -6
@@ 14,6 14,7 @@ members = [

[profile.dev]
panic = "abort"
opt-level = 3

[profile.release]
panic = "abort"


@@ 24,9 25,3 @@ lto = "fat"
codegen-units = 1
opt-level = 'z'
#strip = "symbols"

[unstable]
build-std = true

[build]
target = "x86_64-unknown-norostb.json"

M Makefile => Makefile +15 -0
@@ 1,2 1,17 @@
# There's a bug in cargo that causes panics when using `forced-target`
# Use Makefiles as workaround for now
build: kernel boot

kernel:
	cargo b --bin nora

boot:
	cargo b --bin noraboot --target i686-unknown-norostb.json

run:
	cargo r --bin nora

disk0:
	fallocate -l $$((32 * 512)) $@

.PHONY: kernel boot run

M boot/amd64/Cargo.toml => boot/amd64/Cargo.toml +4 -3
@@ 1,11 1,12 @@
cargo-features = ["edition2021"]
cargo-features = ["per-package-target"]

[package]
name = "noraboot"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
#default-target = "i686-unknown-norostb.json"
# FIXME this causes a panic in cargo
#forced-target = "i686-unknown-norostb.json"

[dependencies]
rsdp = "*"

A boot/amd64/i686-unknown-norostb.json => boot/amd64/i686-unknown-norostb.json +1 -0
@@ 0,0 1,1 @@
../../i686-unknown-norostb.json
\ No newline at end of file

M boot/amd64/src/cpuid.rs => boot/amd64/src/cpuid.rs +3 -0
@@ 69,8 69,11 @@ impl Features {
}

struct CPUID {
	#[allow(dead_code)]
	eax: u32,
	#[allow(dead_code)]
	ebx: u32,
	#[allow(dead_code)]
	ecx: u32,
	edx: u32,
}

M boot/amd64/src/main.rs => boot/amd64/src/main.rs +0 -1
@@ 2,7 2,6 @@
#![no_std]
#![feature(asm, asm_const)]
#![feature(maybe_uninit_uninit_array, maybe_uninit_slice)]
#![feature(option_result_unwrap_unchecked)]

mod cpuid;
mod elf64;

D clean.sh => clean.sh +0 -6
@@ 1,6 0,0 @@
#!/bin/sh

set -x

rm -rf build/
cargo clean

M drivers/hello_world/hello.s => drivers/hello_world/hello.s +0 -61
@@ 14,24 14,8 @@ _start:
	lea		rsp, [rip + stack_end]

	jmp		3f
	
	call	new_client_queue

	lea		rdi, [rip + client_queue_test]
	mov		rsi, client_queue_test_end - client_queue_test
	mov		edx, 1337
	call	submit_client_queue_entry

2:
	mov		eax, 0					# syslog
	lea		rdi, [hello]			# address of string
	mov		rsi, hello_end - hello	# length of string
	syscall

	call	push_client_queue

3:

	mov		eax, SYS_SYSLOG
	lea		rdi, [hello]			# address of string
	mov		rsi, hello_end - hello	# length of string


@@ 63,51 47,6 @@ _start:

	jmp		4b


new_client_queue:
	mov		eax, 1					# init_client_queue
	mov		edi, 0x123000			# address to map it to
	mov		esi, 4					# 16 submission entries
	mov		edx, 5					# 32 completion entries
	syscall
	test	eax, eax
	jnz		panic
	mov		[rip + client_queue], rdi
	ret


push_client_queue:
	mov		eax, 2					# push_client_queue
	syscall
	test	eax, eax
	jnz		panic
	ret


submit_client_queue_entry:
	push	rbx

	mov		rax, [rip + client_queue]
	mov		ebx, [rax + 4 * 2]
	mov		ecx, [rax + 4 * 0]
	
	and		ebx, ecx
	shl		ebx, 5
	lea		r8, [rax + 4 * 6 + rbx]
	mov		bl, 127
	mov		[r8 +  0], bl			# OP_SYSLOG
	mov		[r8 +  8], rdi
	mov		[r8 + 16], rsi
	mov		[r8 + 56], rdx			# user_data

	# Update submission head
	inc		ecx
	mov		[rax + 0], ecx

	pop		rbx
	ret


panic:
	mov		eax, SYS_SYSLOG
	lea		rdi, [panic_msg]

M drivers/virtio_block/Cargo.toml => drivers/virtio_block/Cargo.toml +0 -2
@@ 1,5 1,3 @@
cargo-features = ["edition2021"]

[package]
name = "driver_virtio_block"
version = "0.1.0"

M drivers/virtio_block/src/main.rs => drivers/virtio_block/src/main.rs +6 -23
@@ 5,7 5,6 @@
use kernel::syscall;
use kernel::syslog;

use core::mem;
use core::panic::PanicInfo;
use core::ptr::NonNull;
use core::time::Duration;


@@ 43,7 42,6 @@ extern "C" fn main() {
	let pci_config = NonNull::new(0x1000_0000 as *mut _);
	let pci_config = syscall::map_object(handle, pci_config, 0, usize::MAX).unwrap();

	use core::fmt::Write;
	syslog!("handle: {:?}", handle);

	let pci = unsafe {


@@ 57,7 55,7 @@ extern "C" fn main() {

	let mut dma_addr = 0x2666_0000;

	let mut dev = unsafe {
	let mut dev = {
		let h = pci.get(0, 0, 0).unwrap();
		match h {
			pci::Header::H0(h) => {


@@ 78,11 76,11 @@ extern "C" fn main() {
					map_addr = map_addr.wrapping_add(16);
					NonNull::new(addr as *mut _).unwrap()
				};
				let dma_alloc = |size| unsafe {
				let dma_alloc = |size| {
					syslog!("dma: {:#x}", dma_addr);
					let d = core::ptr::NonNull::new(dma_addr as *mut _).unwrap();
					let res = syscall::alloc_dma(Some(d), size).unwrap();
					dma_addr += size;
					dma_addr += res;
					let a = syscall::physical_address(d).unwrap();
					Ok((d.cast(), a))
				};


@@ 101,31 99,21 @@ extern "C" fn main() {
		*w = *r;
	}

	let h = pci.get(0, 0, 0).unwrap();
	syslog!("writing the stuff...");
	dev.write(&sectors, 0, || ()).unwrap();
	syslog!("done writing the stuff");

	let (mut rd, mut wr) = (
		core::cell::UnsafeCell::new(P([0; 4096 / 8])),
		core::cell::UnsafeCell::new(P([0; 4096 / 8])),
	);

	// Register new table of Streaming type
	let tbl = syscall::create_table("virtio-blk", syscall::TableType::Streaming).unwrap();
	
	// Register a new object

	let mut wr_i = 0;

	let mut i = 0;
	// TODO

	let mut buf = [0; 1024];

	loop {
		// Wait for events from the table
		syslog!("ermaghed");
		//let e = syscall::poll_object(tbl).unwrap();

		let job = syscall::take_table_job(tbl, &mut buf).unwrap();



@@ 184,7 172,7 @@ extern "C" fn main() {

#[naked]
#[export_name = "_start"]
unsafe fn start() {
unsafe extern "C" fn start() {
	asm!(
		"
		lea		rsp, [rip + __stack + 0x8000]


@@ 206,14 194,9 @@ struct P([u64; 512]);
#[export_name = "__stack"]
static mut STACK: [P; 0x8] = [P([0xdeadbeef; 4096 / 8]); 8];

static mut TEST: u8 = 8;
#[export_name = "test2__"]
static TEST2: u8 = 5;

#[panic_handler]
fn panic_handler(info: &PanicInfo) -> ! {
	use core::fmt::Write;
	writeln!(syscall::SysLog::default(), "Panic! {:#?}", info);
	syslog!("Panic! {:#?}", info);
	loop {
		syscall::sleep(Duration::MAX);
	}

M kernel/Cargo.toml => kernel/Cargo.toml +1 -2
@@ 1,5 1,3 @@
cargo-features = ["edition2021"]

[package]
name = "nora"
version = "0.1.0"


@@ 17,3 15,4 @@ mem-max-256m = []
driver-pci = []
driver-hpet = []
driver-pic = []


M kernel/src/arch/amd64/mod.rs => kernel/src/arch/amd64/mod.rs +2 -5
@@ 75,7 75,6 @@ fn handle_timer(rip: *const ()) -> ! {
				apic::set_timer_oneshot(d, Some(16));
				unsafe { asm!("sti") }
				power::halt();
				dbg!(Monotonic::now());
			}
		}
	}


@@ 95,10 94,8 @@ fn handle_double_fault(error: u32, rip: *const ()) {

fn handle_general_protection_fault(error: u32, rip: *const ()) {
	fatal!("General protection fault!");
	unsafe {
		fatal!("  error:   {:#x}", error);
		fatal!("  RIP:     {:p}", rip);
	}
	fatal!("  error:   {:#x}", error);
	fatal!("  RIP:     {:p}", rip);
	halt();
}


M kernel/src/arch/amd64/msr.rs => kernel/src/arch/amd64/msr.rs +2 -0
@@ 1,10 1,12 @@
pub const IA32_APIC_BASE_MSR: u32 = 0x1b;
#[allow(dead_code)]
pub const IA32_APIC_BASE_MSR_BSP: u64 = 0x100;
pub const IA32_APIC_BASE_MSR_ENABLE: u64 = 1 << 11;

pub const IA32_EFER: u32 = 0xc0000080;
pub const IA32_EFER_SCE: u64 = 1;

#[allow(dead_code)]
pub const IA32_KERNEL_GS_BASE: u32 = 0xc0000102;

pub const STAR: u32 = 0xc0000081;

M kernel/src/arch/amd64/syscall.rs => kernel/src/arch/amd64/syscall.rs +2 -3
@@ 3,7 3,6 @@ use crate::scheduler::process::Process;
use crate::scheduler::Thread;
use crate::scheduler::syscall;
use core::ptr::{self, NonNull};
use core::mem::MaybeUninit;
use alloc::{boxed::Box, sync::{Arc, Weak}};

pub unsafe fn init() {


@@ 147,7 146,7 @@ pub fn current_thread() -> Arc<Thread> {
		asm!("mov {0}, gs:[0x18]", out(reg) thread);
		let r = Arc::from_raw(thread);
		let s = r.clone();
		Arc::into_raw(r);
		let _ = Arc::into_raw(r);
		s
	}
}


@@ 158,7 157,7 @@ pub fn current_thread_weak() -> Weak<Thread> {
		asm!("mov {0}, gs:[0x18]", out(reg) thread);
		let r = Arc::from_raw(thread);
		let w = Arc::downgrade(&r);
		Arc::into_raw(r);
		let _ = Arc::into_raw(r);
		w
	}
}

M kernel/src/arch/amd64/virtual/address_space.rs => kernel/src/arch/amd64/virtual/address_space.rs +0 -2
@@ 41,12 41,10 @@ impl AddressSpace {
		hint_color: u8,
	) -> Result<(), MapError> {
		let tbl = self.table_mut();
		dbg!(address);
		for (i, f) in frames.enumerate() {
			loop {
				match common::get_entry_mut(tbl, address.add(i) as u64, 0, 3) {
					Ok(e) => {
						dbg!(format_args!("{:#x}", f.as_phys()));
						e.set_page(f.as_phys() as u64, true, rwx.w()).unwrap();
						break;
					}

M kernel/src/arch/amd64/virtual/common.rs => kernel/src/arch/amd64/virtual/common.rs +5 -0
@@ 14,11 14,16 @@ impl Entry {
	const PRESENT: u64 = 1 << 0;
	const READ_WRITE: u64 = 1 << 1;
	const USER: u64 = 1 << 2;
	#[allow(dead_code)]
	const WRITE_THROUGH: u64 = 1 << 3;
	#[allow(dead_code)]
	const CACHE_DISABLE: u64 = 1 << 4;
	#[allow(dead_code)]
	const ACCESSED: u64 = 1 << 5;
	const PAGE_SIZE: u64 = 1 << 7;
	#[allow(dead_code)]
	const GLOBAL: u64 = 1 << 8;
	#[allow(dead_code)]
	const AVAILABLE: u64 = 7 << 9;

	pub fn is_present(&self) -> bool {

M kernel/src/arch/amd64/virtual/mod.rs => kernel/src/arch/amd64/virtual/mod.rs +1 -1
@@ 15,7 15,7 @@ pub unsafe fn add_identity_mapping(phys: usize, size: usize) -> Result<NonNull<P
	struct Iter {
		phys: PPN,
		size: usize,
	};
	}

	impl Iterator for Iter {
		type Item = PageFrame;

M kernel/src/arch/amd64/virtual/pml4.rs => kernel/src/arch/amd64/virtual/pml4.rs +4 -2
@@ 17,7 17,8 @@ pub fn init() {
						for l in 1..=3 {
							let e = common::get_entry_mut(root, virt, l, 3 - l);
							let ppn = e.unwrap_or_else(|_| unreachable!()).clear().unwrap();
							frame::deallocate(1, || frame::PageFrame::from_raw(ppn, 0));
							frame::deallocate(1, || frame::PageFrame::from_raw(ppn, 0))
								.unwrap();
						}
					}
					break;


@@ 37,6 38,7 @@ pub fn init() {
			.iter()
			.filter(|e| e.is_present())
			.count();

	frame::allocate(
		256 + 128 - i,
		|frame| {


@@ 50,7 52,7 @@ pub fn init() {
		},
		common::IDENTITY_MAP_ADDRESS,
		0,
	);
	).unwrap();
}

pub struct DumpCurrent;

M kernel/src/driver/acpi.rs => kernel/src/driver/acpi.rs +0 -5
@@ 24,11 24,6 @@ pub unsafe fn init(boot: &boot::Info) {
		.try_into()
		.unwrap();
	let acpi = acpi::AcpiTables::from_rsdp(Handler, rsdp).unwrap();
	dbg!(&acpi.dsdt, &acpi.ssdts);

	for (sig, sdt) in acpi.sdts.iter() {
		dbg!(sig);
	}

	super::apic::init_acpi(&acpi);


M kernel/src/driver/apic/io_apic.rs => kernel/src/driver/apic/io_apic.rs +2 -1
@@ 1,4 1,4 @@
use super::{RegR, RegW, RegRW};
use super::RegRW;
use crate::memory::frame::PPN;
use crate::memory::r#virtual::{AddressSpace, phys_to_virt};



@@ 8,6 8,7 @@ struct IoApic {
	data: RegRW,
}

#[allow(dead_code)]
pub fn set_irq(irq: u8, apic_id: u8, vector: u8) {
	let i = 0x10 + u32::from(irq) * 2;


M kernel/src/driver/apic/local_apic.rs => kernel/src/driver/apic/local_apic.rs +2 -5
@@ 1,10 1,7 @@
use super::{RegR, RegW, RegRW};
use crate::memory::r#virtual::{AddressSpace, phys_to_virt};
use crate::memory::frame::PPN;
use core::cell::UnsafeCell;
use core::ptr::NonNull;
use core::fmt;
use core::ops::Deref;

#[repr(C, align(4096))]
pub struct LocalApic {


@@ 69,8 66,8 @@ impl fmt::Debug for LocalApic {
	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
		use core::fmt::DebugStruct as DS;
		let mut f = f.debug_struct(stringify!(LocalApic));
		let mut hex = |f: &mut DS, n, v: u32| { f.field(n, &format_args!("{:#x}", v)); };
		let mut set = |f: &mut DS, n, v: BitSet256| { f.field(n, &format_args!("{:?}", v)); };
		let hex = |f: &mut DS, n, v: u32| { f.field(n, &format_args!("{:#x}", v)); };
		let set = |f: &mut DS, n, v: BitSet256| { f.field(n, &format_args!("{:?}", v)); };

		hex(&mut f, "id",  self.id.get());
		hex(&mut f, "version",  self.version.get());

M kernel/src/driver/apic/mod.rs => kernel/src/driver/apic/mod.rs +1 -4
@@ 12,16 12,13 @@ use acpi::{AcpiHandler, AcpiTables};
// No atomic is necessary as the value is read only once anyways.
static mut TICKS_PER_SECOND: u32 = 0;

pub unsafe fn init_acpi<H>(acpi: &AcpiTables<H>)
pub unsafe fn init_acpi<H>(_: &AcpiTables<H>)
where
	H: AcpiHandler,
{
	disable_pic();
	local_apic::init();
	io_apic::init();
	let info = acpi.platform_info().unwrap();

	//io_apic::set_irq(8, 0, 40);
}

pub fn post_init() {

M kernel/src/driver/hpet/mod.rs => kernel/src/driver/hpet/mod.rs +2 -5
@@ 1,4 1,4 @@
use crate::memory::r#virtual::{phys_to_virt, add_identity_mapping};
use crate::memory::r#virtual::add_identity_mapping;
use crate::time::Monotonic;
use core::cell::UnsafeCell;
use core::{ptr, fmt};


@@ 54,6 54,7 @@ impl CapabilitiesId {
	}
}

#[allow(dead_code)]
#[repr(C)]
pub struct Timer {
	configuration_capabilities: Reg,


@@ 88,11 89,7 @@ where
		// Period is in femtoseconds.
		MULTIPLIER = u128::from(hpet().capabilities_id().period()) / 1_000_000;
	}
	dbg!(format_args!("{:#x}", h.base_address));
	dbg!(format_args!("{:p}", &hpet().configuration));
	dbg!(format_args!("{:p}", &hpet().counter));
	hpet().configuration.set(hpet().configuration.get() | 1);
	dbg!(hpet());
}

pub fn hpet() -> &'static Hpet {

M kernel/src/driver/mod.rs => kernel/src/driver/mod.rs +1 -0
@@ 5,6 5,7 @@ pub mod hpet;
#[cfg(feature = "driver-pci")]
pub mod pci;
pub mod uart;
#[cfg(feature = "driver-vga")]
pub mod vga;
#[cfg(feature = "driver-pic")]
pub mod pic;

M kernel/src/driver/pci/device.rs => kernel/src/driver/pci/device.rs +3 -50
@@ 1,13 1,9 @@
use super::PCI;
use crate::memory::frame::PageFrame;
use crate::memory::frame::PPN;
use crate::object_table::{Object, Events, Unpollable};
use crate::object_table::Object;
use crate::scheduler::MemoryObject;
use core::pin::Pin;
use core::ptr::NonNull;
use core::future::Future;
use core::task::{Context, Poll};
use pci::{BaseAddress, Header};
use pci::BaseAddress;
use alloc::boxed::Box;

/// A single PCI device.


@@ 30,40 26,6 @@ impl PciDevice {
			p2size: 0,
		}
	}

	pub fn bar_region(&self, index: u8) -> Result<BarRegion, BarError> {
		let index = usize::from(index);
		let pci = PCI.lock();
		let pci = pci.as_ref().unwrap();
		let header = pci.get(self.bus, self.device, 0).unwrap();
		match header {
			Header::H0(h) => {
				h.base_address
					.get(index)
					.ok_or(BarError::NonExistent)
					.and_then(|bar| {
						let (size, orig) = bar.size();
						bar.set(orig);
						if let Some(size) = size {
							if !BaseAddress::is_mmio(orig) {
								return Err(BarError::NotMmio);
							}
							let upper = || h.base_address.get(index + 1).map(|e| e.get());
							let addr = BaseAddress::address(orig, upper).unwrap();
							let frame = PageFrame {
								base: PPN::try_from_usize(addr.try_into().unwrap()).unwrap(),
								p2size: size.trailing_zeros() as u8 - 12, // log2
							};
							let device = PciDevice { bus: self.bus, device: self.device };
							Ok(BarRegion { device, frame })
						} else {
							Err(BarError::Invalid)
						}
					})
			}
			_ => todo!(),
		}
	}
}

impl MemoryObject for PciDevice {


@@ 95,21 57,12 @@ impl Object for PciDevice {
			base: PPN::try_from_usize(addr.try_into().unwrap()).unwrap(),
			p2size: size.trailing_zeros() as u8 - 12, // log2
		};
		let device = PciDevice { bus: self.bus, device: self.device };
		Some(Box::new(BarRegion { device, frame }))
		Some(Box::new(BarRegion { frame }))
	}
}

#[derive(Debug)]
pub enum BarError {
	NonExistent,
	Invalid,
	NotMmio,
}

/// A single MMIO region pointer to by a BAR of a PCI device.
pub struct BarRegion {
	device: PciDevice,
	frame: PageFrame,
}


M kernel/src/driver/pci/mod.rs => kernel/src/driver/pci/mod.rs +2 -14
@@ 6,15 6,10 @@
//!
//! [osdev pci]: https://wiki.osdev.org/PCI

use crate::memory::r#virtual::{add_identity_mapping, phys_to_virt};
use crate::memory::Page;
use crate::memory::r#virtual::add_identity_mapping;
use crate::object_table;
use crate::sync::SpinLock;
use acpi::{AcpiHandler, AcpiTables, PciConfigRegions};
use core::cell::Cell;
use core::fmt;
use core::num::NonZeroU32;
use core::ptr::NonNull;
use pci::Pci;
use alloc::sync::Arc;



@@ 34,7 29,6 @@ where
	// TODO this is ridiculous. Fork the crate or implement MCFG ourselves.
	for bus in 0..=255 {
		// IDK what a segment group is
		let segment_group = 0;
		if pci.physical_address(0, bus, 0, 0).is_some() {
			avail[usize::from(bus >> 7)] |= 1 << (bus & 0x7f);
		}


@@ 47,15 41,9 @@ where

	let pci = Pci::new(virt.cast(), phys.try_into().unwrap(), size, &[]);

	for bus in pci.iter() {
		for dev in bus.iter() {
			dbg!(dev);
		}
	}

	*PCI.lock() = Some(pci);

	let table = Arc::new(table::PciTable) as Arc<dyn crate::object_table::Table>;
	object_table::add_table(Arc::downgrade(&table));
	Arc::into_raw(table); // Intentionally leak the table.
	let _ = Arc::into_raw(table); // Intentionally leak the table.
}

M kernel/src/driver/pci/table.rs => kernel/src/driver/pci/table.rs +3 -6
@@ 1,7 1,5 @@
use crate::object_table::{Table, Query, NoneQuery, Id, Object, CreateObjectError, Ticket, Data, QueryResult, Error, Events, Unpollable, Job, JobTask};
use crate::scheduler::Thread;
use core::pin::Pin;
use alloc::{boxed::Box, string::String, format, sync::{Arc, Weak}};
use crate::object_table::{Table, Query, NoneQuery, Id, Object, Ticket, Data, QueryResult, Error, Job, JobTask};
use alloc::{boxed::Box, string::String, format, sync::Arc};

/// Table with all PCI devices.
pub struct PciTable;


@@ 77,7 75,6 @@ impl Iterator for QueryName {
	type Item = QueryResult;

	fn next(&mut self) -> Option<Self::Item> {
		dbg!(self.item);
		self.item.take().and_then(|(b, d, f)| {
			let pci = super::PCI.lock();
			let h = pci.as_ref().unwrap().get(b, d, f)?;


@@ 127,7 124,7 @@ fn bdf_from_string(s: &str) -> Option<(u8, u8, u8)> {
	Some((bus.parse().ok()?, dev.parse().ok()?, func.parse().ok()?))
}

fn pci_dev_object(h: pci::Header, bus: u8, dev: u8, func: u8) -> Arc<dyn Object> {
fn pci_dev_object(_h: pci::Header, bus: u8, dev: u8, _func: u8) -> Arc<dyn Object> {
	Arc::new(super::PciDevice::new(bus, dev))
}


M kernel/src/driver/rtc/mod.rs => kernel/src/driver/rtc/mod.rs +1 -1
@@ 1,7 1,7 @@
//! # RTC driver

use crate::time::Monotonic;
use core::sync::atomic::{AtomicU64, Ordering};
use core::sync::atomic::AtomicU64;

static RTC_TICKS: AtomicU64 = AtomicU64::new(0);


D kernel/src/ipc/mod.rs => kernel/src/ipc/mod.rs +0 -1
@@ 1,1 0,0 @@
pub mod queue;

D kernel/src/ipc/port.rs => kernel/src/ipc/port.rs +0 -18
@@ 1,18 0,0 @@
pub struct InPort {
	process: PID,
	index: u32,
}

pub struct OutPort {
	process: PID,
	index: u32,
}

pub struct NamedPort {
	name: Box<str>,
	process: PID,
}

pub struct ReverseNamedPort {
	index: u32,
}

D kernel/src/ipc/queue.rs => kernel/src/ipc/queue.rs +0 -201
@@ 1,201 0,0 @@
use crate::memory::frame;
use crate::memory::r#virtual::Mappable;
use crate::memory::Page;
use core::alloc::Layout;
use core::cell::Cell;
use core::mem;
use core::ptr::NonNull;
use core::num::NonZeroUsize;
use core::slice;
use core::sync::atomic::{AtomicU32, Ordering};

#[derive(Clone, Copy)]
#[repr(C)]
#[repr(align(64))] // Align to 64 bytes to avoid false sharing as much as possible
pub struct SubmissionEntry {
	pub opcode: u8,
	pub data: [u8; 64 - mem::size_of::<u8>() - mem::size_of::<u64>()],
	pub user_data: u64,
}

#[derive(Clone, Copy)]
#[repr(C)]
#[repr(align(16))] // Align to 16 bytes to avoid false sharing as much as possible
pub struct CompletionEntry {
	user_data: u64,
	_data: u64,
}

pub struct ClientQueue {
	submission_tail: u32,
	submission_mask: u32,
	completion_head: u32,
	completion_mask: u32,
	queues: NonNull<u8>,
}

#[repr(C)]
pub struct ClientQueueHeader {
	submission_head: AtomicU32,
	completion_head: AtomicU32,
	// The fields below are for the process' convienence and not used by the kernel
	submission_mask: u32,
	completion_mask: u32,
	submission_offset: u32,
	completion_offset: u32,
}

impl ClientQueue {
	pub fn new(sq_p2size: usize, cq_p2size: usize) -> Result<Self, NewClientQueueError> {
		if sq_p2size > 15 || cq_p2size > 15 {
			Err(NewClientQueueError::SizeTooLarge)?;
		}

		let sq_size = 1u32 << sq_p2size;
		let cq_size = 1u32 << cq_p2size;
		let ring_layout = Self::ring_layout(sq_size, cq_size);

		let pages = Page::min_pages_for_bytes(ring_layout.size());
		let queues = frame::allocate_contiguous(NonZeroUsize::new(pages).unwrap())
			.map_err(NewClientQueueError::AllocateError)?
			.as_ptr();

		Ok(Self {
			submission_tail: 0,
			submission_mask: sq_size - 1,
			completion_head: 0,
			completion_mask: cq_size - 1,
			queues: NonNull::new(queues).unwrap().cast(),
		})
	}

	/// Pop a submitted entry.
	///
	/// The entire value is copied since the user process might be naughty and modify the entry
	/// when it's already submitted. Reading and writing unatomically from two threads
	/// simultaneously is undefined behaviour and we'd very much like to avoid that.
	pub fn pop_submission(&mut self) -> Option<SubmissionEntry> {
		let head = self.header().submission_head.load(Ordering::Acquire);
		let tail = self.submission_tail & self.completion_mask;
		(head != tail).then(|| {
			let entry = &self.submissions()[usize::try_from(tail).unwrap()];
			let value = entry.get();
			// compiler_fence emits code: https://github.com/rust-lang/rust/issues/62256
			// the asm! macro can perform a compiler memory barrier without emitting code
			unsafe {
				asm!("# {0}", in(reg) entry);
			}
			self.submission_tail = self.submission_tail.wrapping_add(1);
			value
		})
	}

	/// Push a completion entry.
	pub fn push_completion(&mut self, entry: CompletionEntry) {
		let head = usize::try_from(self.completion_head & self.completion_mask).unwrap();
		self.completions()[head].set(entry);
		self.completion_head = self.completion_head.wrapping_add(1);
		self.header()
			.completion_head
			.store(self.completion_head, Ordering::Release);
	}

	/// Return the submissions ring.
	///
	/// Since we share the submissions with a process which may be naughty we have to use `Cell`.
	fn submissions(&self) -> &[Cell<SubmissionEntry>] {
		unsafe {
			let slen = usize::try_from(self.completion_mask).unwrap() + 1;
			let ptr = self
				.queues
				.as_ptr()
				.cast::<ClientQueueHeader>()
				.add(1)
				.cast();
			slice::from_raw_parts(ptr, slen)
		}
	}

	/// Return the completions ring.
	///
	/// Since we share the completions with a process which may be naughty we have to use `Cell`.
	fn completions(&self) -> &[Cell<CompletionEntry>] {
		unsafe {
			let slen = usize::try_from(self.completion_mask).unwrap() + 1;
			let clen = usize::try_from(self.completion_mask).unwrap() + 1;
			let ptr = self
				.queues
				.as_ptr()
				.cast::<ClientQueueHeader>()
				.add(1)
				.cast::<Cell<SubmissionEntry>>()
				.add(slen)
				.cast();
			slice::from_raw_parts(ptr, clen)
		}
	}

	fn header(&self) -> &ClientQueueHeader {
		unsafe { self.queues.cast().as_ref() }
	}

	fn ring_layout(sq_size: u32, cq_size: u32) -> Layout {
		let header_layout = Layout::new::<ClientQueueHeader>();
		let sq_layout = Layout::array::<SubmissionEntry>(sq_size.try_into().unwrap()).unwrap();
		let cq_layout = Layout::array::<CompletionEntry>(cq_size.try_into().unwrap()).unwrap();
		header_layout
			.extend(sq_layout)
			.unwrap()
			.0
			.extend(cq_layout)
			.unwrap()
			.0
	}
}

impl Drop for ClientQueue {
	fn drop(&mut self) {
		todo!()
	}
}

unsafe impl Mappable<Iter> for ClientQueue {
	fn len(&self) -> usize {
		let layout = Self::ring_layout(self.submission_mask + 1, self.completion_mask + 1);
		Page::min_pages_for_bytes(layout.size())
	}

	fn frames(&self) -> Iter {
		Iter(
			unsafe { frame::PPN::from_ptr(self.queues.as_ptr().cast()) },
			self.len(),
		)
	}
}

pub struct Iter(frame::PPN, usize);

impl Iterator for Iter {
	type Item = frame::PPN;

	fn next(&mut self) -> Option<Self::Item> {
		(self.1 > 0).then(|| {
			self.1 -= 1;
			let ppn = self.0;
			self.0 = self.0.next();
			ppn
		})
	}
}

impl ExactSizeIterator for Iter {
	fn len(&self) -> usize {
		self.1
	}
}

#[derive(Debug)]
pub enum NewClientQueueError {
	SizeTooLarge,
	AllocateError(frame::AllocateContiguousError),
}

M kernel/src/log.rs => kernel/src/log.rs +13 -8
@@ 3,8 3,8 @@ use crate::sync::SpinLock;

pub static __LOG: SpinLock<Option<UART>> = SpinLock::new(None);

pub fn init() {
	*__LOG.lock() = unsafe { Some(UART::new(0x3f8)) };
pub unsafe fn init() {
	*__LOG.lock() = Some(UART::new(0x3f8));
}

pub unsafe fn force_unlock() {


@@ 13,12 13,17 @@ pub unsafe fn force_unlock() {

#[macro_export]
macro_rules! debug {
	($($args:tt)*) => {{
		#[allow(unused_imports)]
		use core::fmt::Write;
		let mut log = $crate::log::__LOG.lock();
		writeln!(log.as_mut().unwrap(), $($args)*).unwrap();
	}}
	($($args:tt)*) => {
		#[cfg(debug_assertions)]
		{
			#[allow(unused_imports)]
			use core::fmt::Write;
			let mut log = $crate::log::__LOG.lock();
			writeln!(log.as_mut().unwrap(), $($args)*).unwrap();
		}
		#[cfg(not(debug_assertions))]
		{}
	}
}

#[macro_export]

M kernel/src/main.rs => kernel/src/main.rs +6 -4
@@ 11,6 11,7 @@
#![feature(new_uninit)]
#![feature(optimize_attribute)]
#![feature(slice_index_methods)]
#![allow(incomplete_features)] // It seems like this feature is mostly complete, really.
#![feature(trait_upcasting)]

extern crate alloc;


@@ 50,7 51,6 @@ mod arch;
mod boot;
mod driver;
mod ffi;
mod ipc;
mod memory;
mod object_table;
mod power;


@@ 61,8 61,6 @@ mod time;
#[export_name = "main"]
pub extern "C" fn main(boot_info: &boot::Info) -> ! {
	unsafe {
		memory::r#virtual::init();
		arch::init();
		log::init();
	}



@@ 84,6 82,11 @@ pub extern "C" fn main(boot_info: &boot::Info) -> ! {
	}

	unsafe {
		memory::r#virtual::init();
		arch::init();
	}

	unsafe {
		driver::init(boot_info);
	}



@@ 95,7 98,6 @@ pub extern "C" fn main(boot_info: &boot::Info) -> ! {
		let process = scheduler::process::Process::from_elf(driver.as_slice()).unwrap();
		processes.push(process);
	}
	dbg!(scheduler::thread_count());

	processes.leak()[0].run()
}

M kernel/src/memory/frame/mod.rs => kernel/src/memory/frame/mod.rs +1 -1
@@ 200,7 200,6 @@ pub enum AllocateError {
#[derive(Debug)]
pub enum AllocateContiguousError {
	OutOfFrames,
	CountIsZero,
}

#[derive(Debug)]


@@ 276,6 275,7 @@ pub unsafe fn add_memory_region(mut region: MemoryRegion) {
}

/// The amount of free memory in bytes.
#[allow(dead_code)]
pub fn free_memory() -> u128 {
	(dumb_stack::STACK.lock().count() * Page::SIZE)
		.try_into()

M kernel/src/memory/virtual.rs => kernel/src/memory/virtual.rs +0 -1
@@ 1,7 1,6 @@
mod address_space;

use super::frame::PPN;
use crate::arch::amd64::r#virtual;
pub use crate::arch::amd64::r#virtual::{
	add_identity_mapping, init, phys_to_virt, virt_to_phys, MapError,
};

M kernel/src/object_table/mod.rs => kernel/src/object_table/mod.rs +12 -12
@@ 7,16 7,13 @@

mod streaming;

use crate::scheduler::Thread;
use crate::scheduler::MemoryObject;
use crate::sync::SpinLock;
use alloc::{boxed::Box, vec::Vec, sync::{Arc, Weak}};
use core::fmt;
use core::future::Future;
use core::pin::Pin;
use core::ptr::NonNull;
use core::task::{Context, Poll, Waker};
use core::ops::{Deref, DerefMut};

pub use streaming::StreamingTable;



@@ 75,15 72,15 @@ pub struct QueryResult {
pub trait Object {
	/// Create a memory object to interact with this object. May be `None` if this object cannot
	/// be accessed directly through memory operations.
	fn memory_object(&self, offset: u64) -> Option<Box<dyn MemoryObject>> {
	fn memory_object(&self, _offset: u64) -> Option<Box<dyn MemoryObject>> {
		None
	}

	fn read(&self, _: u64, data: &mut [u8]) -> Result<Ticket, ()> {
	fn read(&self, _offset: u64, _data: &mut [u8]) -> Result<Ticket, ()> {
		Err(())
	}

	fn write(&self, _: u64, data: &[u8]) -> Result<Ticket, ()> {
	fn write(&self, _offset: u64, _data: &[u8]) -> Result<Ticket, ()> {
		Err(())
	}



@@ 106,6 103,7 @@ pub struct EventWaker {
}

impl EventListener {
	#[allow(dead_code)]
	fn new() -> (Self, EventWaker) {
		let shared = Arc::new(SpinLock::default());
		(Self { shared: shared.clone() }, EventWaker { shared })


@@ 113,6 111,7 @@ impl EventListener {
}

impl EventWaker {
	#[allow(dead_code)]
	fn complete(self, event: Events) {
		let mut l = self.shared.lock();
		l.0.take().map(|w| w.wake());


@@ 176,6 175,7 @@ bi_from!(newtype JobId <=> u32);
/// Data submitted as part of a job.
pub enum Data {
	Usize(usize),
	#[allow(dead_code)]
	Bytes(Box<[u8]>),
	Object(Arc<dyn Object>),
}


@@ 188,6 188,7 @@ impl Data {
		}
	}

	#[allow(dead_code)]
	pub fn into_bytes(self) -> Option<Box<[u8]>> {
		match self {
			Self::Bytes(n) => Some(n),


@@ 209,7 210,7 @@ impl fmt::Debug for Data {
		match self {
			Self::Usize(n) => f.field(&n),
			Self::Bytes(d) => f.field(&d),
			Self::Object(d) => f.field(&"<object>"),
			Self::Object(_d) => f.field(&"<object>"),
		};
		f.finish()
	}


@@ 238,10 239,6 @@ impl Ticket {
		let inner = Arc::new(SpinLock::new(TicketInner { waker: None, status: None }));
		(Self { inner: inner.clone() }, TicketWaker { inner })
	}

	pub fn take_result(&self) -> Option<Result<Data, Error>> {
		self.inner.lock().status.take()
	}
}

pub struct TicketWaker {


@@ 328,6 325,7 @@ impl JobWaker {
}

/// Get a list of all tables with their respective name and ID.
#[allow(dead_code)]
pub fn tables() -> Vec<(Box<str>, TableId)> {
	TABLES
		.lock()


@@ 338,6 336,7 @@ pub fn tables() -> Vec<(Box<str>, TableId)> {
}

/// Get the ID of the table with the given name.
#[allow(dead_code)]
pub fn find_table(name: &str) -> Option<TableId> {
	TABLES
		.lock()


@@ 367,6 366,7 @@ pub fn get(table_id: TableId, id: Id) -> Result<Ticket, GetError> {
}

/// Create a new object in a table.
#[allow(dead_code)]
pub fn create(table_id: TableId, name: &str, tags: &[&str]) -> Result<Ticket, CreateError> {
	TABLES
		.lock()


@@ 412,8 412,8 @@ pub enum GetError {

#[derive(Debug)]
pub enum CreateError {
	#[allow(dead_code)]
	InvalidTableId,
	CreateObjectError(CreateObjectError),
}

#[derive(Debug)]

M kernel/src/object_table/streaming.rs => kernel/src/object_table/streaming.rs +7 -14
@@ 1,11 1,6 @@
use super::*;
use crate::memory::frame::{self, PageFrame, PPN};
use crate::scheduler::{Thread, process::Process};
use crate::sync::Mutex;
use core::cell::{Cell, UnsafeCell};
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU16, AtomicU32, Ordering};
use core::time::Duration;
use core::sync::atomic::{AtomicU32, Ordering};
use alloc::{boxed::Box, sync::{Arc, Weak}, vec::Vec};

#[derive(Default)]


@@ 19,7 14,7 @@ pub struct StreamingTable {
}

impl StreamingTable {
	pub fn new(name: Box<str>, process: NonNull<Process>) -> Arc<Self> {
	pub fn new(name: Box<str>) -> Arc<Self> {
		Self { name, ..Default::default() }.into()
	}



@@ 31,10 26,10 @@ impl StreamingTable {
		let j = self.job_handlers.lock().pop();
		if let Some(w) = j {
			self.tickets.lock().push((job_id, id, ticket_waker));
			w.complete(job.into_job(dbg!(job_id), id));
			w.complete(job.into_job(job_id, id));
		} else {
			let mut l = self.jobs.lock();
			l.push((dbg!(job_id), job, id, ticket_waker));
			l.push((job_id, job, id, ticket_waker));
		}

		ticket


@@ 46,7 41,7 @@ impl Table for StreamingTable {
		&self.name
	}

	fn query(self: Arc<Self>, name: Option<&str>, tags: &[&str]) -> Box<dyn Query> {
	fn query(self: Arc<Self>, _name: Option<&str>, _tags: &[&str]) -> Box<dyn Query> {
		todo!()
	}



@@ 54,7 49,7 @@ impl Table for StreamingTable {
		self.submit_job(StreamJob::Open, id)
	}

	fn create(self: Arc<Self>, name: &str, tags: &[&str]) -> Ticket {
	fn create(self: Arc<Self>, _name: &str, _tags: &[&str]) -> Ticket {
		todo!()
	}



@@ 70,7 65,6 @@ impl Table for StreamingTable {

	fn finish_job(self: Arc<Self>, job: Job) -> Result<(), ()> {
		let mut c = self.tickets.lock();
		dbg!(job.job_id);
		let mut c = c.drain_filter(|e| e.0 == job.job_id);
		let (_, id, tw) = c.next().ok_or(())?;
		match job.ty {


@@ 115,7 109,7 @@ struct StreamObject {
}

impl Object for StreamObject {
	fn read(&self, _: u64, data: &mut [u8]) -> Result<Ticket, ()> {
	fn read(&self, _: u64, _data: &mut [u8]) -> Result<Ticket, ()> {
		todo!();
	}



@@ 134,7 128,6 @@ enum StreamJob {

impl StreamJob {
	fn into_job(self, job_id: JobId, object_id: Id) -> Job {
		dbg!(job_id);
		match self {
			StreamJob::Open => Job {
				ty: JobType::Open,

M kernel/src/scheduler/memory_object.rs => kernel/src/scheduler/memory_object.rs +4 -2
@@ 1,4 1,4 @@
use crate::memory::frame::{PageFrame, PPN};
use crate::memory::frame::PageFrame;
use alloc::boxed::Box;
use core::any::Any;
use core::ops::Range;


@@ 13,5 13,7 @@ where

	/// Mark a range of physical pages as dirty. May panic if the range
	/// is invalid.
	fn mark_dirty(&mut self, range: Range<usize>) {}
	fn mark_dirty(&mut self, range: Range<usize>) {
		let _ = range;
	}
}

M kernel/src/scheduler/mod.rs => kernel/src/scheduler/mod.rs +0 -1
@@ 6,7 6,6 @@ mod round_robin;
mod waker;

use core::time::Duration;
use crate::object_table;
use crate::time::Monotonic;
use core::future::Future;
use core::pin::Pin;

M kernel/src/scheduler/process/elf.rs => kernel/src/scheduler/process/elf.rs +0 -3
@@ 158,8 158,6 @@ impl super::Process {
				.then(|| ())
				.ok_or(ElfError::AddressOffsetMismatch)?;

			let offset = header.offset & page_mask;

			let (phys, virt) = (header.physical_address, header.virtual_address);
			let count = page_count(phys..phys + header.file_size);
			let alloc = page_count(virt..virt + header.memory_size);


@@ 225,7 223,6 @@ pub enum ElfError {
	ProgramHeaderSizeMismatch,
	OffsetOutOfBounds,
	AddressOffsetMismatch,
	AllocateError(frame::AllocateError),
	AllocateContiguousError(frame::AllocateContiguousError),
	MapError(MapError),
}

M kernel/src/scheduler/process/mod.rs => kernel/src/scheduler/process/mod.rs +7 -70
@@ 2,11 2,8 @@ mod elf;

use super::{MemoryObject, Thread};
use crate::arch;
#[cfg(feature = "driver-pci")]
use crate::driver::pci::PciDevice;
use crate::ipc::queue::{ClientQueue, NewClientQueueError};
use crate::memory::frame;
use crate::memory::r#virtual::{AddressSpace, MapError, Mappable, RWX, MemoryObjectHandle};
use crate::memory::r#virtual::{AddressSpace, MapError, RWX, MemoryObjectHandle};
use crate::memory::Page;
use crate::object_table::{Object, Query};
use core::ptr::NonNull;


@@ 20,7 17,6 @@ pub struct Process {
	//named_ports: Box<[ReverseNamedPort]>,
	thread: Option<Arc<Thread>>,
	//threads: Vec<NonNull<Thread>>,
	client_queue: Option<ClientQueue>,
	objects: Vec<Arc<dyn Object>>,
	queries: Vec<Box<dyn Query>>,
}


@@ 32,7 28,6 @@ impl Process {
			address_space,
			hint_color: 0,
			thread: None,
			client_queue: None,
			objects: Default::default(),
			queries: Default::default(),
		})


@@ 47,49 42,6 @@ impl Process {
		self.thread.clone().unwrap().resume()
	}

	pub fn init_client_queue(
		&mut self,
		address: *const Page,
		submit_p2size: u8,
		completion_p2size: u8,
	) -> Result<(), NewQueueError> {
		match self.client_queue.as_ref() {
			Some(_) => Err(NewQueueError::QueueAlreadyExists(core::ptr::null())), // TODO return start of queue
			None => {
				let queue = ClientQueue::new(submit_p2size.into(), completion_p2size.into())
					.map_err(NewQueueError::NewClientQueueError)?;
				unsafe {
					self.address_space
						.map(address, queue.frames(), RWX::RW, self.hint_color)
						.map_err(NewQueueError::MapError)?;
				}
				self.client_queue = Some(queue);
				Ok(())
			}
		}
	}

	pub fn poll_client_queue(&mut self) -> Result<(), PollQueueError> {
		let queue = self.client_queue.as_mut().ok_or(PollQueueError::NoQueue)?;
		const OP_SYSLOG: u8 = 127;
		while let Some(e) = queue.pop_submission() {
			match e.opcode {
				OP_SYSLOG => {
					let ptr = usize::from_le_bytes(e.data[7..15].try_into().unwrap());
					let len = usize::from_le_bytes(e.data[15..23].try_into().unwrap());
					let s = unsafe { core::slice::from_raw_parts(ptr as *const u8, len) };
					info!("{}", core::str::from_utf8(s).unwrap());
				}
				_ => todo!(
					"handle erroneous opcodes (opcode {}, userdata {})",
					e.opcode,
					e.user_data
				),
			}
		}
		Ok(())
	}

	/// Add an object to the process' object table.
	pub fn add_object(&mut self, object: Arc<dyn Object>) -> Result<ObjectHandle, AddObjectError> {
		self.objects.push(object);


@@ 120,16 72,17 @@ impl Process {
		self.address_space.map_object(base, obj, rwx, self.hint_color)
	}

	/// Get a reference to an object.
	pub fn get_object(&mut self, handle: ObjectHandle) -> Option<&Arc<dyn Object>> {
		self.objects.get(handle.0)
	}

	/// Get a reference to a memory object.
	#[allow(dead_code)]
	pub fn get_memory_object(&self, handle: MemoryObjectHandle) -> Option<&dyn MemoryObject> {
		self.address_space.get_object(handle)
	}

	/// Get a reference to an object.
	pub fn get_object(&mut self, handle: ObjectHandle) -> Option<&Arc<dyn Object>> {
		self.objects.get(handle.0)
	}

	/// Map a virtual address to a physical address.
	pub fn get_physical_address(&self, address: NonNull<()>) -> Option<(usize, RWX)> {
		self.address_space.get_physical_address(address)


@@ 158,22 111,6 @@ impl Drop for Process {
	}
}

pub struct ProcessID {
	index: u32,
}

#[derive(Debug)]
pub enum NewQueueError {
	QueueAlreadyExists(*const Page),
	NewClientQueueError(NewClientQueueError),
	MapError(MapError),
}

#[derive(Debug)]
pub enum PollQueueError {
	NoQueue,
}

#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct ObjectHandle(usize);

M kernel/src/scheduler/round_robin.rs => kernel/src/scheduler/round_robin.rs +1 -1
@@ 2,7 2,6 @@

use super::Thread;
use crate::sync::SpinLock;
use core::ops::Deref;
use core::ptr::NonNull;
use alloc::{boxed::Box, sync::{Arc, Weak}};



@@ 58,6 57,7 @@ pub fn next() -> Option<Arc<Thread>> {
	}
}

#[allow(dead_code)]
pub fn count() -> usize {
	THREAD_LIST.lock().0
}

M kernel/src/scheduler/syscall.rs => kernel/src/scheduler/syscall.rs +17 -63
@@ 1,11 1,10 @@
use crate::{driver, object_table};
use crate::object_table;
use crate::object_table::{Id, TableId, Job, JobId, JobType};
use crate::memory::{frame, Page, r#virtual::RWX};
use crate::scheduler::{process::Process, syscall::frame::DMAFrame};
use crate::scheduler::{process::Process, syscall::frame::DMAFrame, Thread};
use crate::scheduler::process::ObjectHandle;
use crate::ffi;
use crate::time::Monotonic;
use core::marker::PhantomData;
use core::mem;
use core::ptr::NonNull;
use core::time::Duration;


@@ 24,8 23,8 @@ pub const SYSCALLS_LEN: usize = 17;
#[export_name = "syscall_table"]
static SYSCALLS: [Syscall; SYSCALLS_LEN] = [
	syslog,
	init_client_queue,
	push_client_queue,
	undefined,
	undefined,
	alloc_dma,
	physical_address,
	next_table,


@@ 55,42 54,6 @@ extern "C" fn syslog(ptr: usize, len: usize, _: usize, _: usize, _: usize, _: us
	}
}

extern "C" fn init_client_queue(
	address: usize,
	submission_p2size: usize,
	completion_p2size: usize,
	_: usize,
	_: usize,
	_: usize,
) -> Return {
	Process::current()
		.init_client_queue(
			address as *mut _,
			submission_p2size as u8,
			completion_p2size as u8,
		)
		.unwrap();
	Return {
		status: 0,
		value: 0,
	}
}

extern "C" fn push_client_queue(
	_: usize,
	_: usize,
	_: usize,
	_: usize,
	_: usize,
	_: usize,
) -> Return {
	Process::current().poll_client_queue().unwrap();
	Return {
		status: 0,
		value: 0,
	}
}

extern "C" fn alloc_dma(base: usize, size: usize, _: usize, _: usize, _: usize, _: usize) -> Return {
	let rwx = RWX::RW;
	let base = NonNull::new(base as *mut _);


@@ 221,7 184,7 @@ extern "C" fn open_object(table_id: usize, id_l: usize, id_h: usize, _: usize, _
}

extern "C" fn map_object(handle: usize, base: usize, offset_l: usize, offset_h_or_length: usize, length_or_rwx: usize, rwx: usize) -> Return {
	let (offset, length, rwx) = match mem::size_of_val(&offset_l) {
	let (offset, _length, _rwx) = match mem::size_of_val(&offset_l) {
		4 => ((offset_h_or_length as u64) << 32 | offset_l as u64, length_or_rwx, rwx),
		8 | 16 => (offset_l as u64, offset_h_or_length, length_or_rwx),
		s => unreachable!("unsupported usize size of {}", s),


@@ 235,32 198,32 @@ extern "C" fn map_object(handle: usize, base: usize, offset_l: usize, offset_h_o
	}
}

extern "C" fn read_object(handle: usize, base: usize, length: usize, offset_l: usize, offset_h: usize, _: usize) -> Return {
	let handle = ObjectHandle::from(handle);
	let offset = merge_u64(offset_l, offset_h);
	let base = NonNull::new(base as *mut u8).unwrap();
extern "C" fn read_object(handle: usize, base: usize, _length: usize, offset_l: usize, offset_h: usize, _: usize) -> Return {
	let _handle = ObjectHandle::from(handle);
	let _offset = merge_u64(offset_l, offset_h);
	let _base = NonNull::new(base as *mut u8).unwrap();
	todo!()
}

extern "C" fn write_object(handle: usize, base: usize, length: usize, offset_l: usize, offset_h: usize, _: usize) -> Return {
	dbg!(handle, base, length, offset_l);
	let handle = ObjectHandle::from(handle);
	let offset = merge_u64(offset_l, offset_h);
	let base = NonNull::new(base as *mut u8).unwrap();
	let data = unsafe { core::slice::from_raw_parts(base.as_ptr(), length) };

	let written = Process::current().get_object(handle).unwrap().write(offset, data).unwrap();
	let written = dbg!(super::block_on(written).unwrap())
	let written = super::block_on(written).unwrap()
		.into_usize().unwrap();

	Return {
		status: 0,
		value: dbg!(written),
		value: written,
	}
}

extern "C" fn poll_object(handle: usize, _: usize, _: usize, _: usize, _: usize, _: usize) -> Return {
extern "C" fn poll_object(_handle: usize, _: usize, _: usize, _: usize, _: usize, _: usize) -> Return {
	todo!();
	/*
	let handle = ObjectHandle::from(handle);
	let object = Process::current().get_object(handle).unwrap();
	let event = super::block_on(object.event_listener().unwrap());


@@ 268,6 231,7 @@ extern "C" fn poll_object(handle: usize, _: usize, _: usize, _: usize, _: usize,
		status: 0,
		value: u32::from(event).try_into().unwrap(),
	}
	*/
}

extern "C" fn create_table(name: usize, name_len: usize, ty: usize, _options: usize, _: usize, _: usize) -> Return {


@@ 275,12 239,11 @@ extern "C" fn create_table(name: usize, name_len: usize, ty: usize, _options: us
	assert!(name_len <= 255, "name too long");
	let name = unsafe { core::slice::from_raw_parts(name.as_ptr(), name_len) };
	let name = core::str::from_utf8(name).unwrap();
	dbg!(name, ty);

	let name = name.into();
	let tbl = match ty {
		0 => {
			let tbl = object_table::StreamingTable::new(name, NonNull::from(Process::current()));
			let tbl = object_table::StreamingTable::new(name);
			object_table::add_table(Arc::downgrade(&tbl) as Weak<dyn object_table::Table>);
			tbl
		}


@@ 364,7 327,6 @@ extern "C" fn take_table_job(handle: usize, job_ptr: usize, _: usize, _: usize, 
	let tbl = Process::current().get_object(handle).unwrap().clone().as_table().unwrap();

	let mut job = unsafe { &mut *(job_ptr as *mut FfiJob) };
	dbg!(&job);
	let copy_to = unsafe { core::slice::from_raw_parts_mut(job.buffer.unwrap().as_ptr(), job.buffer_size.try_into().unwrap()) };
	let info = super::block_on(tbl.take_job());
	job.ty = info.ty.into();


@@ 398,21 360,13 @@ extern "C" fn finish_table_job(handle: usize, job_ptr: usize, _: usize, _: usize
	}
}

#[repr(transparent)]
struct SleepOptions(usize);

impl SleepOptions {
}

extern "C" fn sleep(time_l: usize, time_h: usize, _: usize, _: usize, _: usize, _: usize) -> Return {
	let time = merge_u64(time_l, time_h);
	let time = Duration::from_micros(time.into());
	dbg!(time);
	unsafe { for _ in 0..1000000000usize { asm!("") } }
	use crate::driver::apic::local_apic;

	crate::scheduler::Thread::current().set_sleep_until(Monotonic::now().saturating_add(time));
	unsafe { asm!("int 61") }; // Articifical timer interrupt.
	Thread::current().set_sleep_until(Monotonic::now().saturating_add(time));
	Thread::yield_current();

	Return {
		status: 0,

M kernel/src/scheduler/thread.rs => kernel/src/scheduler/thread.rs +1 -4
@@ 1,6 1,5 @@
use super::process::Process;
use crate::arch;
use crate::object_table::Job;
use crate::memory::frame;
use crate::time::Monotonic;
use core::cell::Cell;


@@ 13,7 12,6 @@ pub struct Thread {
	pub user_stack: Cell<Option<NonNull<usize>>>,
	pub kernel_stack: Cell<NonNull<usize>>,
	pub process: NonNull<Process>,
	kernel_stack_base: NonNull<[usize; 512]>,
	sleep_until: Cell<Monotonic>,
}



@@ 38,7 36,6 @@ impl Thread {
			kernel_stack = kernel_stack.sub(15);
			Ok(Self {
				user_stack: Cell::new(None),
				kernel_stack_base: NonNull::new(kernel_stack_base).unwrap(),
				kernel_stack: Cell::new(NonNull::new(kernel_stack).unwrap()),
				process,
				sleep_until: Cell::new(Monotonic::ZERO),


@@ 114,7 111,7 @@ impl Thread {
	}

	pub fn yield_current() {
		unsafe { asm!("int 61") }; // Fake timer interrupt
		crate::arch::yield_current_thread();
	}

	/// Cancel sleep

M kernel/src/scheduler/waker.rs => kernel/src/scheduler/waker.rs +3 -3
@@ 1,7 1,7 @@
//! Waker for asynchronous operations.

use super::Thread;
use core::task::{Context, RawWaker, RawWakerVTable, Waker};
use core::task::{RawWaker, RawWakerVTable, Waker};
use alloc::sync::Weak;

static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);


@@ 14,7 14,7 @@ pub fn new_waker(thread: Weak<Thread>) -> Waker {

unsafe fn clone(thread: *const ()) -> RawWaker {
	let t = Weak::from_raw(thread.cast::<Thread>());
	Weak::into_raw(t.clone()); // Don't free the weak pointer
	let _ = Weak::into_raw(t.clone()); // Don't free the weak pointer
	RawWaker::new(Weak::into_raw(t).cast(), &VTABLE)
}



@@ 26,7 26,7 @@ unsafe fn wake(thread: *const ()) {
unsafe fn wake_by_ref(thread: *const ()) {
	let t = Weak::from_raw(thread.cast::<Thread>());
	t.upgrade().map(|t| t.wake());
	Weak::into_raw(t); // Don't free the weak pointer
	let _ = Weak::into_raw(t); // Don't free the weak pointer
}

unsafe fn drop(thread: *const ()) {

M kernel/src/sync/mutex.rs => kernel/src/sync/mutex.rs +0 -5
@@ 1,7 1,6 @@
use crate::scheduler::Thread;
use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic::{AtomicU8, Ordering};

/// A very basic spinlock implementation. Intended for short sections that are mostly uncontended.


@@ 31,10 30,6 @@ impl<T> Mutex<T> {
			}
		}
	}

	pub unsafe fn force_unlock(&self) {
		self.lock.store(0, Ordering::Relaxed)
	}
}

unsafe impl<T> Sync for Mutex<T> {}

M kernel/src/sync/spinlock.rs => kernel/src/sync/spinlock.rs +0 -2
@@ 1,7 1,5 @@
use crate::scheduler::Thread;
use core::cell::UnsafeCell;
use core::ops::{Deref, DerefMut};
use core::ptr;
use core::sync::atomic::{AtomicU8, Ordering};

/// A very basic spinlock implementation. Intended for short sections that are mostly uncontended.

M kernel/src/time/mod.rs => kernel/src/time/mod.rs +1 -0
@@ 15,6 15,7 @@ impl Monotonic {
		Self { nanoseconds: ns.try_into().expect("nanoseconds too far in the future") }
	}

	#[allow(dead_code)]
	pub fn from_seconds(s: u128) -> Self {
		Self { nanoseconds: (s * 1_000_000_000).try_into().expect("seconds too far in the future") }
	}

M lib/rust/kernel/Cargo.toml => lib/rust/kernel/Cargo.toml +0 -2
@@ 1,5 1,3 @@
cargo-features = ["edition2021"]

[package]
name = "kernel"
version = "0.1.0"

M lib/rust/kernel/src/lib.rs => lib/rust/kernel/src/lib.rs +0 -2
@@ 7,8 7,6 @@
#[macro_use]
pub mod syscall;

pub mod object_table;

#[repr(align(4096))]
#[repr(C)]
pub struct Page([u128; 256]);

D lib/rust/kernel/src/object_table/mod.rs => lib/rust/kernel/src/object_table/mod.rs +0 -1
@@ 1,1 0,0 @@
pub mod streaming;

D lib/rust/kernel/src/object_table/streaming.rs => lib/rust/kernel/src/object_table/streaming.rs +0 -138
@@ 1,138 0,0 @@
use super::*;
use core::cell::{Cell, UnsafeCell};
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU16, Ordering};
use crate::Page;

#[repr(C)]
pub struct CommandQueue {
	commands: [UnsafeCell<CommandFFI>; 64],
	responses: [UnsafeCell<Response>; 64],
	commands_head: AtomicU16,
	commands_tail: AtomicU16,
	responses_head: AtomicU16,
	responses_tail: AtomicU16,
}

impl CommandQueue {
	pub fn push_response(&self, rsp: Response) -> Result<(), ()> {
		let h = self.responses_head.load(Ordering::Relaxed);
		let t = self.responses_tail.load(Ordering::Relaxed);
		let l = self.responses.len();
		if h == t.wrapping_add(l.try_into().unwrap()) {
			return Err(());
		}
		unsafe {
			self.responses[usize::from(h) % l].get().write(rsp);
		}
		let h = h.wrapping_add(1);
		self.responses_head.store(h, Ordering::Release);
		Ok(())
	}

	pub fn pop_command(&self) -> Option<Command> {
		let t = self.commands_tail.load(Ordering::Acquire);
		let h = self.commands_head.load(Ordering::Relaxed);
		syslog!("tail {}     head {}", t, h);
		let l = self.commands.len();
		if t == h {
			return None;
		}
		let rsp = unsafe {
			self.commands[usize::from(t) % l].get().read()
		};
		let t = t.wrapping_add(1);
		self.commands_tail.store(t, Ordering::Relaxed);
		Some(rsp.into())
	}
}

pub enum Command {
	Open { id: crate::syscall::Id, cmd_id: u16 },
	Write { handle: u32, count: usize, cmd_id: u16 },
}

impl From<CommandFFI> for Command {
	fn from(c: CommandFFI) -> Self {
		unsafe {
			let cmd_id = c.cmd_id;
			match c.op {
				0 => Self::Open { id: crate::syscall::Id(c.body.open.id), cmd_id },
				2 => Self::Write { handle: c.body.write.handle, count: c.body.write.count, cmd_id },
				_ => unreachable!(),
			}
		}
	}
}

#[repr(C)]
struct CommandFFI {
	op: u8,
	_padding: u8,
	cmd_id: u16,
	body: CommandBody,
}

union CommandBody {
	open: CommandOpen,
	write: CommandWrite,
}

#[derive(Clone, Copy)]
#[repr(C)]
struct CommandOpen {
	id: u64,
}

#[derive(Clone, Copy)]
#[repr(C)]
struct CommandWrite {
	handle: u32,
	count: usize,
}

#[repr(C)]
pub struct Response {
	status: u8,
	_padding: u8,
	cmd_id: u16,
	body: ResponseBody,
}

impl Response {
	pub fn open(c: &Command, write_ptr: NonNull<Page>, write_p2size: u8, read_ptr: NonNull<Page>, read_p2size: u8) -> Result<Self, ()> {
		let (body, cmd_id) = match c {
			Command::Open { cmd_id, .. } => (ResponseBody { open: ResponseOpen { write_ptr: Some(write_ptr), read_ptr: Some(read_ptr), write_p2size, read_p2size } }, *cmd_id),
			_ => Err(())?,
		};
		Ok(Self { status: 0, _padding: 0, cmd_id, body })
	}

	pub fn write(c: &Command, size: usize) -> Result<Self, ()> {
		let (body, cmd_id) = match c {
			Command::Write { cmd_id, count, .. } => (ResponseBody { write: ResponseWrite { size } }, *cmd_id),
			_ => Err(())?,
		};
		Ok(Self { status: 0, _padding: 0, cmd_id, body })
	}
}

union ResponseBody {
	open: ResponseOpen,
	write: ResponseWrite,
}

#[derive(Clone, Copy)]
#[repr(C)]
struct ResponseOpen {
	write_ptr: Option<NonNull<Page>>,
	read_ptr: Option<NonNull<Page>>,
	read_p2size: u8,
	write_p2size: u8,
}

#[derive(Clone, Copy)]
#[repr(C)]
struct ResponseWrite {
	size: usize,
}

M lib/rust/kernel/src/syscall.rs => lib/rust/kernel/src/syscall.rs +19 -37
@@ 1,4 1,5 @@
const ID_SYSLOG: usize = 0;

const ID_ALLOC_DMA: usize = 3;
const ID_PHYSICAL_ADDRESS: usize = 4;
const ID_NEXT_TABLE: usize = 5;


@@ 7,8 8,9 @@ const ID_QUERY_NEXT: usize = 7;
const ID_OPEN_OBJECT: usize = 8;
const ID_MAP_OBJECT: usize = 9;
const ID_SLEEP: usize = 10;

const ID_CREATE_TABLE: usize = 13;
const ID_POLL_OBJECT: usize = 14;

const ID_TAKE_TABLE_JOB: usize = 15;
const ID_FINISH_TABLE_JOB: usize = 16;



@@ 16,7 18,6 @@ use crate::Page;
use core::fmt;
use core::marker::PhantomData;
use core::num::NonZeroUsize;
use core::ops::Deref;
use core::ptr::NonNull;
use core::time::Duration;



@@ 155,7 156,7 @@ impl fmt::Debug for ObjectInfo<'_> {
			I: Iterator<Item = &'a [u8]>,
		{
			fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
				let mut s = self.0.take().unwrap();
				let s = self.0.take().unwrap();
				let mut f = f.debug_list();
				s.for_each(|e| { f.entry(&ByteStr(e)); });
				f.finish()


@@ 228,7 229,7 @@ impl Default for JobId {

#[optimize(size)]
#[inline]
pub extern "C" fn syslog(s: &[u8]) -> Result<usize, (NonZeroUsize, usize)> {
pub fn syslog(s: &[u8]) -> Result<usize, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 246,7 247,7 @@ pub extern "C" fn syslog(s: &[u8]) -> Result<usize, (NonZeroUsize, usize)> {
}

#[inline]
pub extern "C" fn alloc_dma(base: Option<NonNull<Page>>, size: usize) -> Result<usize, (NonZeroUsize, usize)> {
pub fn alloc_dma(base: Option<NonNull<Page>>, size: usize) -> Result<usize, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 264,7 265,7 @@ pub extern "C" fn alloc_dma(base: Option<NonNull<Page>>, size: usize) -> Result<
}

#[inline]
pub extern "C" fn physical_address(base: NonNull<Page>) -> Result<usize, (NonZeroUsize, usize)> {
pub fn physical_address(base: NonNull<Page>) -> Result<usize, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 281,7 282,7 @@ pub extern "C" fn physical_address(base: NonNull<Page>) -> Result<usize, (NonZer
}

#[inline]
pub extern "C" fn next_table(id: Option<TableId>) -> Option<(TableId, TableInfo)> {
pub fn next_table(id: Option<TableId>) -> Option<(TableId, TableInfo)> {
	let (status, value): (usize, usize);
	let mut info = TableInfo::default();
	unsafe {


@@ 300,7 301,7 @@ pub extern "C" fn next_table(id: Option<TableId>) -> Option<(TableId, TableInfo)
}

#[inline]
pub extern "C" fn query_table(id: TableId, name: Option<&[u8]>, tags: &[Slice<u8>]) -> Result<QueryHandle, (NonZeroUsize, usize)> {
pub fn query_table(id: TableId, name: Option<&[u8]>, tags: &[Slice<u8>]) -> Result<QueryHandle, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 321,7 322,7 @@ pub extern "C" fn query_table(id: TableId, name: Option<&[u8]>, tags: &[Slice<u8
}

#[inline]
pub extern "C" fn query_next(query: QueryHandle, info: &mut ObjectInfo) -> Result<(), (NonZeroUsize, usize)> {
pub fn query_next(query: QueryHandle, info: &mut ObjectInfo) -> Result<(), (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 341,7 342,7 @@ pub extern "C" fn query_next(query: QueryHandle, info: &mut ObjectInfo) -> Resul
}

#[inline]
pub extern "C" fn open_object(table_id: TableId, id: Id) -> Result<Handle, (NonZeroUsize, usize)> {
pub fn open_object(table_id: TableId, id: Id) -> Result<Handle, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 359,7 360,7 @@ pub extern "C" fn open_object(table_id: TableId, id: Id) -> Result<Handle, (NonZ
}

#[inline]
pub extern "C" fn map_object(handle: Handle, base: Option<NonNull<Page>>, offset: u64, length: usize) -> Result<NonNull<Page>, (NonZeroUsize, usize)> {
pub fn map_object(handle: Handle, base: Option<NonNull<Page>>, offset: u64, length: usize) -> Result<NonNull<Page>, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	unsafe {
		asm!(


@@ 380,21 381,20 @@ pub extern "C" fn map_object(handle: Handle, base: Option<NonNull<Page>>, offset


#[inline]
pub extern "C" fn sleep(duration: Duration) -> Result<(), (NonZeroUsize, usize)> {
pub fn sleep(duration: Duration) {
	let micros = u64::try_from(duration.as_micros()).unwrap_or(u64::MAX);
	let (status, value): (usize, usize);
	unsafe {
		asm!(
			"syscall",
			in("eax") ID_SLEEP,
			in("rdi") micros,
			lateout("rax") status,
			lateout("rdx") value,
			// Ignore failures and pretend the sleep terminated early
			lateout("rax") _,
			lateout("rdx") _,
			lateout("rcx") _,
			lateout("r11") _,
		)
	}
	ret(status, value).map(|_| ())
}

#[inline]


@@ 421,26 421,6 @@ pub fn create_table(name: &str, ty: TableType) -> Result<Handle, (NonZeroUsize, 
}

#[inline]
pub fn poll_object<'a>(handle: Handle) -> Result<Job<'a>, (NonZeroUsize, usize)> {
	todo!();
	let (status, value): (usize, usize);
	let mut job = Job::default();
	unsafe {
		asm!(
			"syscall",
			in("eax") ID_POLL_OBJECT,
			in("rdi") handle.0,
			in("rsi") &mut job,
			lateout("rax") status,
			lateout("rdx") value,
			lateout("rcx") _,
			lateout("r11") _,
		)
	}
	ret(status, value).map(|v| job)
}

#[inline]
pub fn take_table_job(handle: Handle, buffer: &mut [u8]) -> Result<Job, (NonZeroUsize, usize)> {
	let (status, value): (usize, usize);
	let mut job = Job::default();


@@ 488,7 468,9 @@ pub struct SysLog {
impl SysLog {
	#[optimize(size)]
	fn flush(&mut self) {
		syslog(&self.buffer[..usize::from(self.index)]);
		// Ignore errors because what can we do? Panic won't do us any
		// good either.
		let _ = syslog(&self.buffer[..usize::from(self.index)]);
		self.index = 0;
	}
}

M lib/rust/virtio/Cargo.toml => lib/rust/virtio/Cargo.toml +0 -2
@@ 1,5 1,3 @@
cargo-features = ["edition2021"]

[package]
name = "virtio"
version = "0.1.0"

M lib/rust/virtio/src/pci.rs => lib/rust/virtio/src/pci.rs +1 -1
@@ 1,7 1,6 @@
use core::convert::TryFrom;
use core::fmt;
use core::marker::PhantomData;
use core::num::NonZeroU8;
use core::ptr::NonNull;
use endian::{u16le, u32le, u64le};
use volatile::VolatileCell;


@@ 56,6 55,7 @@ impl Capability {
	pub const NOTIFY_CONFIGURATION: u8 = 2;
	pub const ISR_CONFIGURATION: u8 = 3;
	pub const DEVICE_CONFIGURATION: u8 = 4;
	#[allow(dead_code)]
	pub const PCI_CONFIGURATION: u8 = 5;
}


M lib/rust/virtio_block/src/lib.rs => lib/rust/virtio_block/src/lib.rs +1 -3
@@ 9,7 9,7 @@ use core::fmt;
use core::mem;
use core::ptr::NonNull;
use endian::{u16le, u32le, u64le};
use virtio::pci::{CommonConfig, DeviceConfig, Notify};
use virtio::pci::CommonConfig;
use virtio::queue;

const SIZE_MAX: u32 = 1 << 1;


@@ 99,8 99,6 @@ struct RequestStatus {
	status: u8,
}

use virtio::pci::*;

impl<'a, F> BlockDevice<'a, F>
where
	F: Fn(*const ()) -> usize,

M mkiso.sh => mkiso.sh +1 -1
@@ 14,7 14,7 @@ cp boot/$ARCH/grub/grub.cfg isodir/boot/grub/grub.cfg
(cd drivers/hello_world && ./build.sh)
cp drivers/hello_world/hello isodir/drivers/hello_world
(cd drivers/virtio_block && cargo build --release --target $RUST_TARGET_FILE)
cp target/x86_64-unknown-norostb/release/driver_virtio_block isodir/drivers/virtio_block
cp target/$RUST_TARGET/release/driver_virtio_block isodir/drivers/virtio_block
grub-mkrescue -o norost.iso isodir \
	--locales= \
	--fonts= \

M mkkernel.sh => mkkernel.sh +0 -1
@@ 7,7 7,6 @@ set -e
cd kernel
cargo rustc \
	--release \
	--target $RUST_TARGET_FILE \
	-- \
	-C linker=$CC \
	-C link-arg=-nostartfiles \

M run.sh => run.sh +2 -3
@@ 10,8 10,7 @@ cpu="--enable-kvm -cpu host"
qemu-system-x86_64 \
	$cpu \
	-drive format=raw,file=norost.iso \
	-serial mon:stdio $@ \
	-serial mon:stdio \
	-machine q35 \
	-drive file=disk0,format=raw,if=none,id=disk0 \
	-device virtio-blk-pci,drive=disk0 \
	#-s
	-device virtio-blk-pci,drive=disk0