diff --git a/.gitignore b/.gitignore index 9e8c8ee..10c91f1 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ compile_commands.json sysroot/bin sysroot/usr +sysroot/.crates.toml +sysroot/.crates2.json rust/target yunq/venv diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 3f089b0..840f030 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -117,6 +117,15 @@ dependencies = [ "yunq", ] +[[package]] +name = "teton" +version = "0.1.0" +dependencies = [ + "mammoth", + "victoriafalls", + "yellowstone", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -129,6 +138,17 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "victoriafalls" +version = "0.1.0" +dependencies = [ + "mammoth", + "yellowstone", + "yunq", + "yunq-derive", + "yunqc", +] + [[package]] name = "yellowstone" version = "0.1.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index b27a0fb..eb3c943 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,7 +1,7 @@ [workspace] members = [ - "lib/mammoth", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "usr/testbed", + "lib/mammoth", "lib/victoriafalls", "lib/yellowstone", "lib/yunq", "lib/yunq-derive", "sys/teton", "usr/testbed", ] resolver = "2" diff --git a/rust/lib/mammoth/src/mem.rs b/rust/lib/mammoth/src/mem.rs index 750365d..dd1834c 100644 --- a/rust/lib/mammoth/src/mem.rs +++ b/rust/lib/mammoth/src/mem.rs @@ -1,4 +1,6 @@ use crate::syscall; +use crate::zion::{z_cap_t, ZError}; +use alloc::slice; use linked_list_allocator::LockedHeap; #[global_allocator] @@ -16,3 +18,57 @@ pub fn init_heap() { CAN_ALLOC = true; } } + +pub struct MemoryRegion { + mem_cap: z_cap_t, + virt_addr: u64, + size: u64, +} + +impl MemoryRegion { + pub fn direct_physical(paddr: u64, size: u64) -> Result { + let mem_cap = syscall::memory_object_direct_physical(paddr, size)?; + let virt_addr = syscall::address_space_map(mem_cap)?; + Ok(Self { + mem_cap, + virt_addr, + size, + }) + } + + pub fn from_cap(mem_cap: z_cap_t) -> Result { + let virt_addr = syscall::address_space_map(mem_cap)?; + let size = syscall::memory_object_inspect(mem_cap)?; + Ok(Self { + mem_cap, + virt_addr, + size, + }) + } + + pub fn slice(&self) -> &[T] { + unsafe { + slice::from_raw_parts( + self.virt_addr as *const T, + self.size as usize / size_of::(), + ) + } + } + + pub fn mut_slice(&self) -> &mut [T] { + unsafe { + slice::from_raw_parts_mut( + self.virt_addr as *mut T, + self.size as usize / size_of::(), + ) + } + } +} + +impl Drop for MemoryRegion { + fn drop(&mut self) { + syscall::address_space_unmap(self.virt_addr, self.virt_addr + self.size) + .expect("Failed to unmap memory"); + syscall::cap_release(self.mem_cap).expect("Failed to release memory cap"); + } +} diff --git a/rust/lib/mammoth/src/syscall.rs b/rust/lib/mammoth/src/syscall.rs index 86765af..a41b761 100644 --- a/rust/lib/mammoth/src/syscall.rs +++ b/rust/lib/mammoth/src/syscall.rs @@ -89,6 +89,31 @@ pub fn memory_object_create(size: u64) -> Result { Ok(vmmo_cap) } +pub fn memory_object_direct_physical(paddr: u64, size: u64) -> Result { + let mut vmmo_cap = 0; + syscall( + zion::kZionMemoryObjectCreatePhysical, + &zion::ZMemoryObjectCreatePhysicalReq { + paddr, + size, + vmmo_cap: &mut vmmo_cap, + }, + )?; + Ok(vmmo_cap) +} + +pub fn memory_object_inspect(mem_cap: z_cap_t) -> Result { + let mut mem_size = 0; + syscall( + zion::kZionMemoryObjectInspect, + &zion::ZMemoryObjectInspectReq { + vmmo_cap: mem_cap, + size: &mut mem_size, + }, + )?; + Ok(mem_size) +} + pub fn address_space_map(vmmo_cap: z_cap_t) -> Result { let mut vaddr: u64 = 0; // FIXME: Allow caller to pass these options. @@ -104,6 +129,17 @@ pub fn address_space_map(vmmo_cap: z_cap_t) -> Result { Ok(vaddr) } +pub fn address_space_unmap(lower_addr: u64, upper_addr: u64) -> Result<(), ZError> { + syscall( + zion::kZionAddressSpaceUnmap, + &zion::ZAddressSpaceUnmapReq { + vmas_cap: unsafe { crate::init::SELF_VMAS_CAP }, + lower_addr, + upper_addr, + }, + ) +} + pub fn port_poll( port_cap: z_cap_t, bytes: &mut [u8], @@ -211,3 +247,7 @@ pub fn reply_port_recv( Ok((num_bytes, num_caps)) } + +pub fn cap_release(cap: z_cap_t) -> Result<(), ZError> { + syscall(zion::kZionCapRelease, &zion::ZCapReleaseReq { cap }) +} diff --git a/rust/lib/victoriafalls/Cargo.toml b/rust/lib/victoriafalls/Cargo.toml new file mode 100644 index 0000000..7f3566b --- /dev/null +++ b/rust/lib/victoriafalls/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "victoriafalls" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../mammoth" } +yellowstone = { path = "../yellowstone" } +yunq = {path = "../yunq"} +yunq-derive = {path = "../yunq-derive"} + +[build-dependencies] +yunqc = {path = "../../../yunq/rust"} + + diff --git a/rust/lib/victoriafalls/build.rs b/rust/lib/victoriafalls/build.rs new file mode 100644 index 0000000..7e2d7d5 --- /dev/null +++ b/rust/lib/victoriafalls/build.rs @@ -0,0 +1,14 @@ +use std::fs; + +fn main() { + let input_file = "../../../sys/victoriafalls/lib/victoriafalls/victoriafalls.yunq"; + + println!("cargo::rerun-if-changed={input_file}"); + + let input = fs::read_to_string(input_file).expect("Failed to read input file"); + + let code = yunqc::codegen(&input).expect("Failed to generate yunq code."); + + let out = std::env::var("OUT_DIR").unwrap() + "/yunq.rs"; + fs::write(out, code).expect("Failed to write generated code."); +} diff --git a/rust/lib/victoriafalls/src/file.rs b/rust/lib/victoriafalls/src/file.rs new file mode 100644 index 0000000..ec6b190 --- /dev/null +++ b/rust/lib/victoriafalls/src/file.rs @@ -0,0 +1,42 @@ +use crate::OpenFileRequest; +use crate::VFSClient; +use alloc::string::ToString; +use mammoth::zion::ZError; + +static mut VFS_CLIENT: Option = None; + +fn get_client() -> &'static mut VFSClient { + unsafe { + if let None = VFS_CLIENT { + let endpoint_cap = yellowstone::from_init_endpoint() + .get_endpoint(&yellowstone::GetEndpointRequest { + endpoint_name: "victoriafalls".to_string(), + }) + .expect("Failed to get VFS endpoint"); + + VFS_CLIENT = Some(VFSClient::new(endpoint_cap.endpoint)); + } + VFS_CLIENT.as_mut().unwrap() + } +} + +pub struct File { + memory: mammoth::mem::MemoryRegion, +} + +impl File { + pub fn open(path: &str) -> Result { + let vfs = get_client(); + let resp = vfs.open_file(&OpenFileRequest { + path: path.to_string(), + })?; + + Ok(Self { + memory: mammoth::mem::MemoryRegion::from_cap(resp.memory)?, + }) + } + + pub fn slice(&self, offset: usize, len: usize) -> &[u8] { + &self.memory.slice()[offset..offset + len] + } +} diff --git a/rust/lib/victoriafalls/src/lib.rs b/rust/lib/victoriafalls/src/lib.rs new file mode 100644 index 0000000..e35a8ef --- /dev/null +++ b/rust/lib/victoriafalls/src/lib.rs @@ -0,0 +1,7 @@ +#![no_std] + +use core::include; + +include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +pub mod file; diff --git a/rust/lib/yellowstone/src/lib.rs b/rust/lib/yellowstone/src/lib.rs index 3cec9d6..0fc673f 100644 --- a/rust/lib/yellowstone/src/lib.rs +++ b/rust/lib/yellowstone/src/lib.rs @@ -3,3 +3,17 @@ use core::include; include!(concat!(env!("OUT_DIR"), "/yunq.rs")); + +use mammoth::init::INIT_ENDPOINT; + +static mut YELLOWSTONE_INIT: Option = None; + +pub fn from_init_endpoint() -> &'static mut YellowstoneClient { + unsafe { + if let None = YELLOWSTONE_INIT { + YELLOWSTONE_INIT = Some(YellowstoneClient::new(INIT_ENDPOINT)); + } + + YELLOWSTONE_INIT.as_mut().unwrap() + } +} diff --git a/rust/lib/yunq/src/message.rs b/rust/lib/yunq/src/message.rs index 7b84b8d..44ca368 100644 --- a/rust/lib/yunq/src/message.rs +++ b/rust/lib/yunq/src/message.rs @@ -79,7 +79,7 @@ impl YunqMessage for Empty { where Self: Sized, { - todo!() + Ok(Self {}) } fn serialize( @@ -88,6 +88,6 @@ impl YunqMessage for Empty { _offset: usize, _caps: &mut Vec, ) -> Result { - todo!() + Ok(0) } } diff --git a/rust/sys/teton/Cargo.toml b/rust/sys/teton/Cargo.toml new file mode 100644 index 0000000..2b897ce --- /dev/null +++ b/rust/sys/teton/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "teton" +version = "0.1.0" +edition = "2021" + +[dependencies] +mammoth = { path = "../../lib/mammoth" } +victoriafalls = { path = "../../lib/victoriafalls" } +yellowstone = { path = "../../lib/yellowstone" } diff --git a/rust/sys/teton/src/console.rs b/rust/sys/teton/src/console.rs new file mode 100644 index 0000000..c6e0ccb --- /dev/null +++ b/rust/sys/teton/src/console.rs @@ -0,0 +1,18 @@ +use crate::framebuffer::Framebuffer; +use crate::psf::Psf; + +pub struct Console { + framebuffer: Framebuffer, + psf: Psf, +} + +impl Console { + pub fn new(framebuffer: Framebuffer, psf: Psf) -> Self { + Self { framebuffer, psf } + } + + pub fn write_char(&self, c: char) { + let glyph = self.psf.glyph(c as u32); + self.framebuffer.draw_glyph(glyph, 0, 0) + } +} diff --git a/rust/sys/teton/src/framebuffer.rs b/rust/sys/teton/src/framebuffer.rs new file mode 100644 index 0000000..268c573 --- /dev/null +++ b/rust/sys/teton/src/framebuffer.rs @@ -0,0 +1,38 @@ +use mammoth::{mem::MemoryRegion, zion::ZError}; +use yellowstone::FramebufferInfo; + +pub struct Framebuffer { + fb_info: FramebufferInfo, + memory_region: MemoryRegion, +} + +impl Framebuffer { + pub fn from_info(fb_info: FramebufferInfo) -> Result { + let size = fb_info.height * fb_info.pitch; + let memory_region = MemoryRegion::direct_physical(fb_info.address_phys, size)?; + Ok(Self { + fb_info, + memory_region, + }) + } + + fn draw_pixel(&self, row: u32, col: u32, pixel: u32) { + let index = row * (self.fb_info.pitch as u32 / 4) + col; + self.memory_region.mut_slice()[index as usize] = pixel; + } + + pub fn draw_glyph(&self, glyph: &[u8], row: u32, col: u32) { + let gl_width = 8; + let gl_height = 16; + + for r in 0..gl_height { + for c in 0..gl_width { + if ((glyph[r] >> c) % 2) == 1 { + self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0xFFFFFFFF); + } else { + self.draw_pixel(row + (r as u32), col + (gl_width - c - 1), 0); + } + } + } + } +} diff --git a/rust/sys/teton/src/main.rs b/rust/sys/teton/src/main.rs new file mode 100644 index 0000000..5599784 --- /dev/null +++ b/rust/sys/teton/src/main.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] + +extern crate alloc; + +mod console; +mod framebuffer; +mod psf; + +use mammoth::{debug, define_entry, zion::z_err_t}; + +define_entry!(); + +#[no_mangle] +extern "C" fn main() -> z_err_t { + debug!("Teton Starting"); + + let yellowstone = yellowstone::from_init_endpoint(); + let framebuffer_info = yellowstone + .get_framebuffer_info() + .expect("Failed to get framebuffer info."); + + debug!( + "FB addr {:#x}, bpp {}, width {} , height {}, pitch {}", + framebuffer_info.address_phys, + framebuffer_info.bpp, + framebuffer_info.width, + framebuffer_info.height, + framebuffer_info.pitch + ); + + let framebuffer = framebuffer::Framebuffer::from_info(framebuffer_info) + .expect("Failed to create framebuffer"); + + let psf = psf::Psf::new("/default8x16.psfu").expect("Failed to open font file."); + let console = console::Console::new(framebuffer, psf); + console.write_char('>'); + + /* + + Terminal terminal(console); + terminal.Register(); + + Thread lthread = terminal.Listen(); + + check(lthread.Join()); + */ + + 0 +} diff --git a/rust/sys/teton/src/psf.rs b/rust/sys/teton/src/psf.rs new file mode 100644 index 0000000..6be3da5 --- /dev/null +++ b/rust/sys/teton/src/psf.rs @@ -0,0 +1,71 @@ +use mammoth::debug; +use mammoth::zion::ZError; +use victoriafalls::file::File; + +const MAGIC_HEADER: u32 = 0x864AB572; + +#[repr(C)] +struct PsfHeader { + magic: u32, /* magic bytes to identify PSF */ + version: u32, /* zero */ + headersize: u32, /* offset of bitmaps in file, 32 */ + flags: u32, /* 0 if there's no unicode table */ + numglyph: u32, /* number of glyphs */ + bytes_per_glyph: u32, /* size of each glyph */ + height: u32, /* height in pixels */ + width: u32, /* width in pixels */ +} + +pub struct Psf { + file: File, + header: &'static PsfHeader, +} + +impl Psf { + pub fn new(path: &str) -> Result { + let file = File::open(&path)?; + + let header = file + .slice(0, core::mem::size_of::()) + .as_ptr() + .cast(); + let psf = Self { + file, + header: unsafe { &*header }, + }; + + psf.validate()?; + + Ok(psf) + } + + fn validate(&self) -> Result<(), ZError> { + if self.header.magic != MAGIC_HEADER { + debug!("PSF: Magic value: {:x}", self.header.magic); + return Err(ZError::INVALID_ARGUMENT); + } + + if self.header.version != 0 { + debug!("PSF non-zero version"); + return Err(ZError::INVALID_ARGUMENT); + } + + if self.header.height != 0x10 { + debug!("PSF height other than 16 not handled"); + return Err(ZError::UNIMPLEMENTED); + } + + if self.header.width != 0x8 { + debug!("PSF width other than 8 not handled"); + return Err(ZError::UNIMPLEMENTED); + } + Ok(()) + } + + pub fn glyph(&self, index: u32) -> &[u8] { + let offset: usize = + (self.header.headersize + (index * self.header.bytes_per_glyph)) as usize; + let len: usize = self.header.bytes_per_glyph as usize; + &self.file.slice(offset, len) + } +} diff --git a/scripts/qemu.sh b/scripts/qemu.sh index 1fc4eb9..0ae6b41 100755 --- a/scripts/qemu.sh +++ b/scripts/qemu.sh @@ -12,11 +12,19 @@ pushd $BUILD_DIR ninja ninja install -export CARGO_INSTALL_ROOT="${DIR}/../sysroot/usr/" +CARGO_USR_ROOT="${DIR}/../sysroot/usr/" +CARGO_SYS_ROOT="${DIR}/../sysroot/" # Need to pushd so rustup gets the toolchain from rust/rust_toolchain.toml pushd "${DIR}/../rust" -cargo install --force --path "${DIR}/../rust/usr/testbed/" + +for BIN in ${DIR}/../rust/usr/*/; do + cargo install --force --path "${BIN}" --root $CARGO_USR_ROOT +done + +for BIN in ${DIR}/../rust/sys/*/; do + cargo install --force --path "${BIN}" --root $CARGO_SYS_ROOT +done popd sudo sh ${DIR}/build_image.sh disk.img