From 5d0a3608d17c28757b7b78534e43e7bd5cd21fe8 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Sun, 31 Aug 2025 22:41:09 -0600 Subject: [PATCH] WIP keyboard syscall --- Cargo.lock | 39 ++++++++++++++----- abi/Cargo.toml | 3 ++ abi/src/lib.rs | 40 +++++++------------- abi_sys/src/lib.rs | 64 +++++++++++++++++++++++--------- kernel/src/abi.rs | 56 ++++++++++++++-------------- kernel/src/elf.rs | 36 ++++++++++++------ kernel/src/main.rs | 34 +++++++++++++++-- user-apps/calculator/src/main.rs | 51 +++++++++++++++++-------- 8 files changed, 210 insertions(+), 113 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f917d9..6551289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,8 +17,11 @@ name = "abi" version = "0.1.0" dependencies = [ "abi_sys", + "embassy-time 0.5.0", "embedded-graphics", "shared", + "spin", + "talc", ] [[package]] @@ -186,7 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f377753756ec12e76b52d2dd657437be0448cc9736402ffadd0b8b8b9602c8a1" dependencies = [ "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embedded-io", "embedded-io-async", "futures-intrusive", @@ -320,7 +323,7 @@ dependencies = [ "embassy-futures", "embassy-net-driver-channel", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embedded-hal 1.0.0", "embedded-io-async", "futures", @@ -494,7 +497,7 @@ dependencies = [ "embassy-futures", "embassy-hal-internal 0.3.0", "embassy-sync 0.7.0", - "embassy-time", + "embassy-time 0.4.0", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -588,7 +591,7 @@ dependencies = [ "embassy-futures", "embassy-hal-internal 0.2.0", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embassy-usb-driver", "embedded-hal 0.2.7", "embedded-hal 1.0.0", @@ -626,7 +629,7 @@ dependencies = [ "embassy-futures", "embassy-hal-internal 0.2.0", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embassy-time-driver", "embassy-time-queue-utils", "embassy-usb-driver", @@ -696,10 +699,26 @@ dependencies = [ ] [[package]] -name = "embassy-time-driver" -version = "0.2.0" +name = "embassy-time" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d45f5d833b6d98bd2aab0c2de70b18bfaa10faf661a1578fd8e5dfb15eb7eba" +checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" +dependencies = [ + "cfg-if", + "critical-section", + "document-features", + "embassy-time-driver", + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "embedded-hal-async", + "futures-core", +] + +[[package]] +name = "embassy-time-driver" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" dependencies = [ "document-features", ] @@ -1187,7 +1206,7 @@ dependencies = [ "embassy-futures", "embassy-rp 0.4.0", "embassy-sync 0.7.0", - "embassy-time", + "embassy-time 0.4.0", "embassy-usb", "embedded-graphics", "embedded-hal 0.2.7", @@ -2086,7 +2105,7 @@ dependencies = [ "bt-hci", "embassy-futures", "embassy-sync 0.6.2", - "embassy-time", + "embassy-time 0.4.0", "embedded-io", "futures", "heapless", diff --git a/abi/Cargo.toml b/abi/Cargo.toml index d20ffeb..8a9af49 100644 --- a/abi/Cargo.toml +++ b/abi/Cargo.toml @@ -7,3 +7,6 @@ edition = "2024" embedded-graphics = "0.8.1" shared = { path = "../shared" } abi_sys = { path = "../abi_sys" } +talc = "4.4.3" +spin = "0.10.0" +embassy-time = "0.5.0" diff --git a/abi/src/lib.rs b/abi/src/lib.rs index 86547b9..ec5b528 100644 --- a/abi/src/lib.rs +++ b/abi/src/lib.rs @@ -1,20 +1,20 @@ #![no_std] -use abi_sys::{Syscall, call_abi}; -use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers}; +use abi_sys::draw_iter; +pub use abi_sys::{get_key, print}; +pub use embassy_time; +pub use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers}; +use talc::*; -pub fn print(msg: &str) { - let syscall = Syscall::Print { - msg: msg.as_ptr(), - len: msg.len(), - }; - unsafe { - call_abi(&syscall); - } -} +static mut ARENA: [u8; 10000] = [0; 10000]; + +#[global_allocator] +static ALLOCATOR: Talck, ClaimOnOom> = + Talc::new(unsafe { ClaimOnOom::new(Span::from_array(core::ptr::addr_of!(ARENA).cast_mut())) }) + .lock(); pub mod display { - use crate::{Syscall, call_abi}; + use crate::draw_iter; use embedded_graphics::{ Pixel, geometry::{Dimensions, Point}, @@ -30,18 +30,6 @@ pub mod display { pub struct Display; - impl Display { - fn syscall_draw(&self, pixels: &[Pixel565]) { - let syscall = Syscall::DrawIter { - pixels: pixels.as_ptr(), - len: pixels.len(), - }; - unsafe { - call_abi(&syscall); - } - } - } - impl Dimensions for Display { fn bounding_box(&self) -> Rectangle { Rectangle { @@ -71,13 +59,13 @@ pub mod display { count += 1; if count == BUF_SIZE { - self.syscall_draw(&buf[..count]); + draw_iter(&buf[..count]); count = 0; } } if count > 0 { - self.syscall_draw(&buf[..count]); + draw_iter(&buf[..count]); } Ok(()) diff --git a/abi_sys/src/lib.rs b/abi_sys/src/lib.rs index b0586ca..10dea33 100644 --- a/abi_sys/src/lib.rs +++ b/abi_sys/src/lib.rs @@ -1,5 +1,9 @@ #![no_std] +extern crate alloc; +use alloc::boxed::Box; + +use core::pin::Pin; pub use embedded_graphics::{ Pixel, geometry::Point, @@ -7,26 +11,50 @@ pub use embedded_graphics::{ }; use shared::keyboard::{KeyCode, KeyEvent, KeyState, Modifiers}; -// Instead of extern, declare a static pointer in a dedicated section +pub type EntryFn = fn() -> Pin>>; + #[unsafe(no_mangle)] -#[unsafe(link_section = ".user_reloc")] -#[allow(non_upper_case_globals)] -pub static mut call_abi_ptr: usize = 0; +#[unsafe(link_section = ".userapp")] +pub static mut CALL_ABI_TABLE: [usize; CallAbiTable::COUNT] = [0; CallAbiTable::COUNT]; -// Helper to call it -pub unsafe fn call_abi(call: *const Syscall) { - let f: extern "C" fn(*const Syscall) = unsafe { core::mem::transmute(call_abi_ptr) }; - f(call); +#[repr(usize)] +#[derive(Clone, Copy)] +pub enum CallAbiTable { + Print = 0, + DrawIter = 1, + GetKey = 2, } -#[repr(C)] -pub enum Syscall { - DrawIter { - pixels: *const Pixel, - len: usize, - }, - Print { - msg: *const u8, - len: usize, - }, +impl CallAbiTable { + pub const COUNT: usize = 3; +} + +pub type PrintAbi = extern "Rust" fn(msg: &str); + +pub fn print(msg: &str) { + unsafe { + let ptr = CALL_ABI_TABLE[CallAbiTable::Print as usize]; + let f: PrintAbi = core::mem::transmute(ptr); + f(msg); + } +} + +pub type DrawIterAbi = extern "Rust" fn(pixels: &[Pixel]); + +pub fn draw_iter(pixels: &[Pixel]) { + unsafe { + let ptr = CALL_ABI_TABLE[CallAbiTable::DrawIter as usize]; + let f: DrawIterAbi = core::mem::transmute(ptr); + f(pixels); + } +} + +pub type GetKeyAbi = extern "Rust" fn() -> Option; + +pub fn get_key() -> Option { + unsafe { + let ptr = CALL_ABI_TABLE[CallAbiTable::GetKey as usize]; + let f: GetKeyAbi = core::mem::transmute(ptr); + f() + } } diff --git a/kernel/src/abi.rs b/kernel/src/abi.rs index 8bf87f1..4463097 100644 --- a/kernel/src/abi.rs +++ b/kernel/src/abi.rs @@ -1,4 +1,7 @@ -use abi_sys::Syscall; +use core::pin::Pin; + +use abi_sys::{DrawIterAbi, GetKeyAbi, Pixel, PrintAbi}; +use alloc::boxed::Box; use defmt::info; use embassy_futures::block_on; use embedded_graphics::{ @@ -8,35 +11,30 @@ use embedded_graphics::{ prelude::{Point, RgbColor, Size}, primitives::{PrimitiveStyle, Rectangle, StyledDrawable}, }; +use shared::keyboard::KeyEvent; -use crate::display::FRAMEBUFFER; +use crate::{KEY_CACHE, display::FRAMEBUFFER}; -#[allow(unused)] -pub extern "C" fn call_abi(call: *const Syscall) { - info!("called abi"); - let call = unsafe { &*call }; - match call { - Syscall::DrawIter { pixels, len } => { - // SAFETY: we're trusting the user program here - let slice = unsafe { core::slice::from_raw_parts(*pixels, *len) }; +// ensure the abi and the kernel fn signatures are the same +const _: PrintAbi = print; +const _: DrawIterAbi = draw_iter; +const _: GetKeyAbi = get_key; - let framebuffer = block_on(FRAMEBUFFER.lock()); - framebuffer - .borrow_mut() - .as_mut() - .unwrap() - .draw_iter(slice.iter().copied()) - .unwrap(); - } - Syscall::Print { msg, len } => { - // SAFETY: we're trusting the user program here - let slice = unsafe { core::slice::from_raw_parts(*msg, *len) }; - - if let Ok(str) = str::from_utf8(slice) { - defmt::info!("{:?}", str); - } else { - defmt::error!("Failed to parse user print str") - } - } - } +pub extern "Rust" fn print(msg: &str) { + defmt::info!("{:?}", msg); +} + +pub extern "Rust" fn draw_iter(pixels: &[Pixel]) { + let framebuffer = block_on(FRAMEBUFFER.lock()); + framebuffer + .borrow_mut() + .as_mut() + .unwrap() + .draw_iter(pixels.iter().copied()) + .unwrap(); +} + +pub extern "Rust" fn get_key() -> Option { + defmt::info!("get key called"); + unsafe { KEY_CACHE.dequeue() } } diff --git a/kernel/src/elf.rs b/kernel/src/elf.rs index a553aa4..3a04ba1 100644 --- a/kernel/src/elf.rs +++ b/kernel/src/elf.rs @@ -1,5 +1,15 @@ #![allow(static_mut_refs)] -use core::{alloc::Layout, ffi::c_void, ptr::NonNull, slice::from_raw_parts_mut}; +use crate::abi; +use abi_sys::{CallAbiTable, EntryFn}; +use alloc::boxed::Box; +use core::{ + alloc::Layout, + ffi::c_void, + pin::Pin, + ptr::NonNull, + slice::from_raw_parts_mut, + task::{Context, Poll}, +}; use goblin::elf::{Elf, header::ET_DYN, program_header::PT_LOAD, sym}; // userland ram region defined in memory.x @@ -8,8 +18,6 @@ unsafe extern "C" { static __userapp_end__: u8; } -type EntryFn = extern "C" fn(); - pub unsafe fn load_binary(bytes: &[u8]) -> Result { let elf = Elf::parse(&bytes).expect("Failed to parse ELF"); @@ -55,16 +63,22 @@ pub unsafe fn load_binary(bytes: &[u8]) -> Result { let call_abi_sym = elf .syms .iter() - .find(|s| elf.strtab.get_at(s.st_name).unwrap() == "call_abi_ptr") - .expect("call_abi_ptr not found"); + .find(|s| elf.strtab.get_at(s.st_name).unwrap() == "CALL_ABI_TABLE") + .expect("syscall table not found"); - // Virtual address inside user RAM - let addr = call_abi_sym.st_value as *mut usize; + let table_base = call_abi_sym.st_value as *mut usize; - // Patch it - unsafe { - core::ptr::write(addr, crate::abi::call_abi as usize); + let entries: &[(CallAbiTable, usize)] = &[ + (CallAbiTable::Print, abi::print as usize), + (CallAbiTable::DrawIter, abi::draw_iter as usize), + (CallAbiTable::GetKey, abi::get_key as usize), + ]; + assert!(entries.len() == CallAbiTable::COUNT); + + for &(abi_idx, func_ptr) in entries { + unsafe { + table_base.add(abi_idx as usize).write(func_ptr); + } } - Ok(unsafe { core::mem::transmute(elf.entry as u32) }) } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index e48c034..57b696e 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -1,7 +1,11 @@ #![feature(impl_trait_in_assoc_type)] +#![feature(type_alias_impl_trait)] #![feature(ascii_char)] #![cfg_attr(not(test), no_std)] #![cfg_attr(not(test), no_main)] +#![allow(static_mut_refs)] + +extern crate alloc; mod abi; mod display; @@ -23,12 +27,13 @@ use crate::{ storage::{SDCARD, SdCard}, usb::{ENABLE_SCSI, usb_handler}, }; +use alloc::vec::Vec; use {defmt_rtt as _, panic_probe as _}; use defmt::unwrap; use embassy_executor::{Executor, Spawner}; -use embassy_futures::join::join; +use embassy_futures::join::{join, join3}; use embassy_rp::{ gpio::{Input, Level, Output, Pull}, i2c::{self, I2c}, @@ -40,10 +45,15 @@ use embassy_rp::{ spi::{self, Spi}, usb as embassy_rp_usb, }; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; +use embassy_sync::{ + blocking_mutex::{Mutex, raw::CriticalSectionRawMutex}, + signal::Signal, +}; use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_sdmmc::SdCard as SdmmcSdCard; +use heapless::spsc::Queue; +use shared::keyboard::KeyEvent; use static_cell::StaticCell; use talc::*; @@ -118,7 +128,7 @@ async fn userland_task() { defmt::info!("Running binary"); let entry = unsafe { load_binary(binary_data).unwrap() }; - entry(); + entry().await; } struct Display { @@ -194,5 +204,21 @@ async fn kernel_task(display: Display, sd: Sd, mcu: Mcu, usb: USB) { let usb_fut = usb_handler(usb); ENABLE_SCSI.store(true, core::sync::atomic::Ordering::Relaxed); - join(usb_fut, display_fut).await; + join3(usb_fut, display_fut, async { + loop { + Timer::after_millis(100).await; + get_keys().await + } + }) + .await; +} + +static mut KEY_CACHE: Queue = Queue::new(); + +async fn get_keys() { + if let Some(event) = read_keyboard_fifo().await { + unsafe { + let _ = KEY_CACHE.enqueue(event); + } + } } diff --git a/user-apps/calculator/src/main.rs b/user-apps/calculator/src/main.rs index 9f2674e..337533c 100644 --- a/user-apps/calculator/src/main.rs +++ b/user-apps/calculator/src/main.rs @@ -1,8 +1,10 @@ #![no_std] #![no_main] -use abi::{display::Display, print}; -use core::panic::PanicInfo; +extern crate alloc; +use abi::{KeyCode, display::Display, embassy_time, get_key, print}; +use alloc::{boxed::Box, string::String, vec}; +use core::{panic::PanicInfo, pin::Pin}; use embedded_graphics::{ Drawable, geometry::{Dimensions, Point}, @@ -17,21 +19,40 @@ fn panic(_info: &PanicInfo) -> ! { loop {} } -#[unsafe(no_mangle)] -pub extern "C" fn _start() { - print("Starting Calculator app"); +pub async fn main() { + print("Starting Async Calculator app"); let mut display = Display; let character_style = MonoTextStyle::new(&FONT_6X10, Rgb565::RED); - // Draw centered text. - let text = "embedded-graphics"; - Text::with_alignment( - text, - display.bounding_box().center() + Point::new(0, 15), - character_style, - Alignment::Center, - ) - .draw(&mut display) - .unwrap(); + let mut text = vec!['H', 'E', 'L', 'L', 'O']; + + loop { + Text::with_alignment( + &text.iter().cloned().collect::(), + display.bounding_box().center() + Point::new(0, 15), + character_style, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + if let Some(event) = get_key() { + print("User got event"); + match event.key { + KeyCode::Char(ch) => { + text.push(ch); + } + KeyCode::Backspace => { + text.pop(); + } + _ => (), + } + } + } +} + +#[unsafe(no_mangle)] +pub extern "Rust" fn _start() -> Pin>> { + Box::pin(async { main().await }) }