From b742f92c0a6678b860d52dc493162f3a63267371 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sun, 18 Oct 2026 02:08:32 +0200 Subject: [PATCH] feat: add scripts/new-adapter.sh to scaffold adapter crates with consistent structure --- scripts/new-adapter.sh | 346 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100755 scripts/new-adapter.sh diff --git a/scripts/new-adapter.sh b/scripts/new-adapter.sh new file mode 100755 index 0000000..755b9c7 --- /dev/null +++ b/scripts/new-adapter.sh @@ -0,0 +1,346 @@ +#!/bin/bash +set -euo pipefail + +if [ $# -lt 1 ]; then + echo "Usage: $0 " + echo "Example: $0 arm" + echo "Creates crates/adapter-/ with the standard adapter structure." + exit 1 +fi + +ARCH="$1" +CRATE="adapter-${ARCH}" +DIR="crates/${CRATE}" + +if [ -d "$DIR" ]; then + echo "Error: ${DIR} already exists." + exit 1 +fi + +echo "Creating adapter for: ${ARCH}" + +mkdir -p "${DIR}/src/hw" +mkdir -p "${DIR}/src/mmu" +mkdir -p "${DIR}/src/trap" + +# Cargo.toml +cat > "${DIR}/Cargo.toml" < "${DIR}/src/board.rs" <<'EOF' +// TODO: fill in device addresses for this platform +pub const RAM_BASE: usize = 0x0; +pub const RAM_SIZE: usize = 0x0; +pub const UART_BASE: usize = 0x0; +pub const IRQ_CONTROLLER_BASE: usize = 0x0; +pub const TIMER_BASE: usize = 0x0; +pub const USER_POOL_BASE: usize = 0x0; +pub const USER_POOL_PAGES: usize = 256; +pub const TICK_INTERVAL: u64 = 10_000_000; +EOF + +# hw/mod.rs +cat > "${DIR}/src/hw/mod.rs" <<'EOF' +pub mod uart; +pub mod irq; +pub mod timer; +EOF + +# hw/uart.rs +cat > "${DIR}/src/hw/uart.rs" <<'EOF' +use crate::board; + +pub struct Uart { + base: usize, +} + +impl Uart { + pub fn new() -> Self { + Self { base: board::UART_BASE } + } + + pub fn put_char(&self, byte: u8) { + let ptr = self.base as *mut u8; + unsafe { core::ptr::write_volatile(ptr, byte) }; + } + + pub fn write_str(&self, s: &str) { + for byte in s.bytes() { + self.put_char(byte); + } + } + + pub fn write_dec(&self, mut value: usize) { + if value == 0 { + self.put_char(b'0'); + return; + } + let mut buf = [0u8; 20]; + let mut i = 0; + while value > 0 { + buf[i] = b'0' + (value % 10) as u8; + value /= 10; + i += 1; + } + while i > 0 { + i -= 1; + self.put_char(buf[i]); + } + } +} +EOF + +# hw/irq.rs +cat > "${DIR}/src/hw/irq.rs" <<'EOF' +// TODO: implement interrupt controller for this platform +// (e.g., GIC for ARM, APIC for x86) +EOF + +# hw/timer.rs +cat > "${DIR}/src/hw/timer.rs" <<'EOF' +// TODO: implement timer for this platform + +pub fn read_time() -> u64 { + 0 +} + +pub fn init() { + // TODO: set up periodic timer +} + +pub fn handle_interrupt() { + // TODO: acknowledge and reschedule timer +} +EOF + +# mmu/mod.rs +cat > "${DIR}/src/mmu/mod.rs" <<'EOF' +pub mod allocator; +pub mod page_table; + +pub use allocator::init_allocator; +pub use page_table::{activate, init_kernel_page_table, PAGE_SIZE}; +EOF + +# mmu/allocator.rs +cat > "${DIR}/src/mmu/allocator.rs" <<'EOF' +use super::page_table::PAGE_SIZE; + +static mut NEXT_FREE: usize = 0; + +pub fn init_allocator(start: usize) { + unsafe { NEXT_FREE = start; } +} + +pub fn alloc_zeroed_page() -> usize { + unsafe { + let addr = NEXT_FREE; + NEXT_FREE += PAGE_SIZE; + core::ptr::write_bytes(addr as *mut u8, 0, PAGE_SIZE); + addr + } +} +EOF + +# mmu/page_table.rs +cat > "${DIR}/src/mmu/page_table.rs" <<'EOF' +pub const PAGE_SIZE: usize = 4096; + +// TODO: implement page table format for this architecture + +pub fn init_kernel_page_table() { + // TODO: identity-map kernel memory +} + +pub fn activate(_root: usize) { + // TODO: write page table base register +} +EOF + +# trap/mod.rs +cat > "${DIR}/src/trap/mod.rs" <<'EOF' +// TODO: implement trap/exception vector for this architecture + +pub fn init() { + // TODO: set trap vector register, enable interrupts +} +EOF + +# platform.rs +cat > "${DIR}/src/platform.rs" <<'EOF' +use domain::memory::MemoryRegion; +use domain::thread::SavedContext; +use platform::Platform; +use types::{ + address::{EntryPoint, PhysicalAddress, StackPointer}, + bounded::PageCount, + id::{CoreId, IrqId, ProcessId}, + permission::PermissionSet, + time::{Ticks, TimerDuration}, +}; + +pub struct AdapterPlatform { + num_cores: u8, +} + +impl AdapterPlatform { + pub fn new() -> Self { + Self { num_cores: 1 } + } +} + +impl Platform for AdapterPlatform { + fn configure_protection(&mut self, _process: ProcessId, _regions: &[MemoryRegion]) {} + fn context_switch(&mut self, _from: &mut SavedContext, _to: &SavedContext) {} + fn enable_user_mode(&mut self, _entry: EntryPoint, _stack: StackPointer) -> ! { + loop { unsafe { core::arch::asm!("wfi") }; } + } + fn set_timer(&mut self, _duration: TimerDuration) {} + fn current_ticks(&self) -> Ticks { + Ticks::from_raw(0) + } + fn mask_irq(&mut self, _irq: IrqId) {} + fn unmask_irq(&mut self, _irq: IrqId) {} + fn send_ipi(&mut self, _target: CoreId) {} + fn core_id(&self) -> CoreId { + CoreId::from_raw(0) + } + fn num_cores(&self) -> u8 { + self.num_cores + } + fn map_shared( + &mut self, + _process: ProcessId, + _base: PhysicalAddress, + _size: PageCount, + _permissions: PermissionSet, + ) {} +} +EOF + +# main.rs +cat > "${DIR}/src/main.rs" <<'EOF' +#![no_std] +#![no_main] + +mod board; +mod hw; +mod mmu; +mod platform; +mod trap; + +use hw::uart::Uart; + +// TODO: add boot assembly entry point via global_asm! or a .S file + +unsafe extern "C" { + static _bss_start: u8; + static _bss_end: u8; + static _heap_start: u8; +} + +fn zero_bss() { + unsafe { + let start = &_bss_start as *const u8 as *mut u8; + let end = &_bss_end as *const u8 as *mut u8; + let len = end as usize - start as usize; + core::ptr::write_bytes(start, 0, len); + } +} + +#[unsafe(no_mangle)] +extern "C" fn kmain() -> ! { + zero_bss(); + + let uart = Uart::new(); + uart.write_str("k-os: booted\n"); + + // TODO: init MMU, traps, timer + // TODO: create Kernel, boot, run + + loop { + unsafe { core::arch::asm!("wfi") }; + } +} + +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + let uart = Uart::new(); + uart.write_str("PANIC: "); + if let Some(location) = info.location() { + uart.write_str(location.file()); + uart.put_char(b':'); + uart.write_dec(location.line() as usize); + } + uart.put_char(b'\n'); + loop { + unsafe { core::arch::asm!("wfi") }; + } +} +EOF + +# linker.ld — skeleton +cat > "${DIR}/linker.ld" <<'EOF' +/* TODO: set ENTRY and base address for this platform */ +OUTPUT_ARCH(riscv) /* TODO: change to target arch */ +ENTRY(_start) + +SECTIONS { + . = 0x80000000; /* TODO: set platform entry address */ + + .text : { + *(.text.entry) + *(.text .text.*) + } + + . = ALIGN(4096); + .rodata : { *(.rodata .rodata.*) } + + . = ALIGN(4096); + .data : { *(.data .data.*) } + + . = ALIGN(4096); + .bss : { + _bss_start = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + _bss_end = .; + } + + . = ALIGN(4096); + _stack_bottom = .; + . += 0x80000; + _stack_top = .; + + _heap_start = .; +} +EOF + +echo "" +echo "Created ${DIR}/ with structure:" +find "${DIR}" -type f | sort | sed "s|${DIR}/||" +echo "" +echo "Next steps:" +echo " 1. Add \"${DIR}\" to workspace members in Cargo.toml" +echo " 2. Fill in board.rs with device addresses" +echo " 3. Add boot assembly (global_asm! in main.rs or .S file)" +echo " 4. Update linker.ld for target architecture" +echo " 5. Implement TODOs in hw/, mmu/, trap/, platform.rs"