feat: add scripts/new-adapter.sh to scaffold adapter crates with consistent structure
This commit is contained in:
346
scripts/new-adapter.sh
Executable file
346
scripts/new-adapter.sh
Executable file
@@ -0,0 +1,346 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Usage: $0 <arch>"
|
||||
echo "Example: $0 arm"
|
||||
echo "Creates crates/adapter-<arch>/ 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" <<EOF
|
||||
[package]
|
||||
name = "${CRATE}"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
types = { workspace = true }
|
||||
domain = { workspace = true }
|
||||
platform = { workspace = true }
|
||||
kernel = { workspace = true }
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
EOF
|
||||
|
||||
# board.rs — platform-specific constants
|
||||
cat > "${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<AdapterPlatform>, 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"
|
||||
Reference in New Issue
Block a user