more tests passing

Signed-off-by: Sean Cross <sean@xobs.io>
This commit is contained in:
Sean Cross 2024-01-04 09:54:26 +08:00
parent abef5b7db3
commit 36d4c143f1
4 changed files with 579 additions and 243 deletions

View File

@ -1,8 +1,10 @@
use riscv_cpu::{cpu::Memory as OtherMemory, mmu::SyscallResult}; use riscv_cpu::cpu::Memory as OtherMemory;
mod definitions; mod definitions;
mod services; mod services;
mod syscalls;
use definitions::{Syscall, SyscallNumber, SyscallResultNumber}; use definitions::{Syscall, SyscallNumber, SyscallResultNumber};
pub use riscv_cpu::mmu::SyscallResult;
use std::{ use std::{
collections::{BTreeSet, HashMap, HashSet}, collections::{BTreeSet, HashMap, HashSet},
sync::{ sync::{
@ -12,8 +14,6 @@ use std::{
}, },
}; };
use crate::xous::definitions::SyscallErrorNumber;
const MEMORY_BASE: u32 = 0x8000_0000; const MEMORY_BASE: u32 = 0x8000_0000;
const ALLOCATION_START: u32 = 0x4000_0000; const ALLOCATION_START: u32 = 0x4000_0000;
const ALLOCATION_END: u32 = ALLOCATION_START + 5 * 1024 * 1024; const ALLOCATION_END: u32 = ALLOCATION_START + 5 * 1024 * 1024;
@ -364,6 +364,7 @@ impl Memory {
} }
fn ensure_page(&mut self, virt: u32) -> Option<bool> { fn ensure_page(&mut self, virt: u32) -> Option<bool> {
assert!(virt != 0);
let mut allocated = false; let mut allocated = false;
let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4; let vpn1 = ((virt >> 22) & ((1 << 10) - 1)) as usize * 4;
let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4; let vpn0 = ((virt >> 12) & ((1 << 10) - 1)) as usize * 4;
@ -635,249 +636,18 @@ impl riscv_cpu::cpu::Memory for Memory {
// println!("Syscall {:?}", SyscallNumber::from(args[0])); // println!("Syscall {:?}", SyscallNumber::from(args[0]));
match syscall { match syscall {
Syscall::IncreaseHeap(bytes, _flags) => { Syscall::IncreaseHeap(bytes, flags) => syscalls::increase_heap(self, bytes, flags),
// println!("IncreaseHeap({} bytes, flags: {:02x})", bytes, _flags);
let increase_bytes = bytes as u32;
let heap_address = self.heap_start + self.heap_size;
if self.heap_size.wrapping_add(increase_bytes) > HEAP_END {
[
SyscallResultNumber::Error as i64,
SyscallErrorNumber::OutOfMemory as i64,
0,
0,
0,
0,
0,
0,
]
.into()
} else {
for new_address in (heap_address..(heap_address + increase_bytes)).step_by(4096)
{
self.ensure_page(new_address);
}
self.heap_size += increase_bytes;
[
SyscallResultNumber::MemoryRange as i64,
heap_address as i64,
bytes,
0,
0,
0,
0,
0,
]
.into()
}
}
Syscall::MapMemory(phys, virt, size, _flags) => { Syscall::MapMemory(phys, virt, size, flags) => {
// print!( syscalls::map_memory(self, phys, virt, size, flags)
// "MapMemory(phys: {:08x}, virt: {:08x}, bytes: {}, flags: {:02x})",
// phys, virt, size, _flags
// );
if virt != 0 {
unimplemented!("Non-zero virt address");
}
if phys != 0 {
unimplemented!("Non-zero phys address");
}
if let Some(region) = self.allocate_virt_region(size as usize) {
[
SyscallResultNumber::MemoryRange as i64,
region as i64,
size,
0,
0,
0,
0,
0,
]
.into()
} else {
// self.print_mmu();
println!(
"Couldn't find a free spot to allocate {} bytes of virtual memory, or out of memory",
size as usize
);
[
SyscallResultNumber::Error as i64,
SyscallErrorNumber::OutOfMemory as i64,
0,
0,
0,
0,
0,
0,
]
.into()
}
}
Syscall::Connect(id) => {
// println!(
// "Connect([0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x}])",
// id[0], id[1], id[2], id[3]
// );
if let Some(service) = services::get_service(&id) {
let connection_id = self.connections.len() as u32 + 1;
self.connections.insert(connection_id, service);
[
SyscallResultNumber::ConnectionId as i64,
connection_id as i64,
0,
0,
0,
0,
0,
0,
]
.into()
} else {
[
SyscallResultNumber::ConnectionId as i64,
0,
0,
0,
0,
0,
0,
0,
]
.into()
}
} }
Syscall::Connect(id) => syscalls::connect(self, id),
Syscall::TryConnect(id) => syscalls::try_connect(self, id),
Syscall::SendMessage(connection_id, kind, opcode, args) => { Syscall::SendMessage(connection_id, kind, opcode, args) => {
// println!( syscalls::send_message(self, connection_id, kind, opcode, args)
// "SendMessage({}, {}, {}: {:x?})",
// connection_id, kind, opcode, args
// );
let memory_region = if kind == 1 || kind == 2 || kind == 3 {
let mut memory_region = vec![0; args[1] as usize];
for (offset, value) in memory_region.iter_mut().enumerate() {
*value = self.read_u8(
self.virt_to_phys(args[0] + offset as u32)
.expect("invalid memory address")
as u64,
);
}
Some(memory_region)
} else {
None
};
let Some(service) = self.connections.get_mut(&connection_id) else {
println!("Unhandled connection ID {}", connection_id);
return [
SyscallResultNumber::Error as i64,
9, /* ServerNotFound */
0,
0,
0,
0,
0,
0,
]
.into();
};
match kind {
1..=3 => {
let mut memory_region = memory_region.unwrap();
let extra = [args[2], args[3]];
match kind {
1 => match service.lend_mut(0, opcode, &mut memory_region, extra) {
services::LendResult::WaitForResponse(msg) => msg.into(),
services::LendResult::MemoryReturned(result) => {
for (offset, value) in memory_region.into_iter().enumerate() {
self.write_u8(args[0] as u64 + offset as u64, value);
}
[
SyscallResultNumber::MemoryReturned as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into()
}
},
2 => match service.lend(0, opcode, &memory_region, extra) {
services::LendResult::WaitForResponse(msg) => msg.into(),
services::LendResult::MemoryReturned(result) => [
SyscallResultNumber::MemoryReturned as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into(),
},
3 => {
service.send(0, opcode, &memory_region, extra);
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
}
_ => unreachable!(),
}
}
4 => {
service.scalar(0, opcode, args);
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
}
5 => match service.blocking_scalar(0, opcode, args) {
services::ScalarResult::Scalar1(result) => [
SyscallResultNumber::Scalar1 as i64,
result as i64,
0,
0,
0,
0,
0,
0,
]
.into(),
services::ScalarResult::Scalar2(result) => [
SyscallResultNumber::Scalar2 as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into(),
services::ScalarResult::Scalar5(result) => [
SyscallResultNumber::Scalar5 as i64,
result[0] as i64,
result[1] as i64,
result[2] as i64,
result[3] as i64,
result[4] as i64,
0,
0,
]
.into(),
services::ScalarResult::WaitForResponse(msg) => msg.into(),
},
_ => {
println!("Unknown message kind {}", kind);
[
SyscallResultNumber::Error as i64,
9, /* ServerNotFound */
0,
0,
0,
0,
0,
0,
]
.into()
}
} }
Syscall::TrySendMessage(connection_id, kind, opcode, args) => {
syscalls::try_send_message(self, connection_id, kind, opcode, args)
} }
Syscall::UpdateMemoryFlags(address, range, value) => { Syscall::UpdateMemoryFlags(address, range, value) => {
for addr in address..(address + range) { for addr in address..(address + range) {

View File

@ -73,12 +73,19 @@ pub enum Syscall {
i64, /* name */ i64, /* name */
), ),
Connect([u32; 4] /* Server ID */), Connect([u32; 4] /* Server ID */),
TryConnect([u32; 4] /* Server ID */),
SendMessage( SendMessage(
u32, /* Connection ID */ u32, /* Connection ID */
u32, /* message kind */ u32, /* message kind */
u32, /* opcode */ u32, /* opcode */
[u32; 4], /* descriptor */ [u32; 4], /* descriptor */
), ),
TrySendMessage(
u32, /* Connection ID */
u32, /* message kind */
u32, /* opcode */
[u32; 4], /* descriptor */
),
UpdateMemoryFlags( UpdateMemoryFlags(
i64, /* address */ i64, /* address */
i64, /* range */ i64, /* range */
@ -131,6 +138,12 @@ impl From<[i64; 8]> for Syscall {
value[3] as u32, value[3] as u32,
value[4] as u32, value[4] as u32,
]), ]),
SyscallNumber::TryConnect => Syscall::TryConnect([
value[1] as u32,
value[2] as u32,
value[3] as u32,
value[4] as u32,
]),
SyscallNumber::SendMessage => Syscall::SendMessage( SyscallNumber::SendMessage => Syscall::SendMessage(
value[1] as u32, value[1] as u32,
value[2] as u32, value[2] as u32,
@ -142,6 +155,17 @@ impl From<[i64; 8]> for Syscall {
value[7] as u32, value[7] as u32,
], ],
), ),
SyscallNumber::TrySendMessage => Syscall::TrySendMessage(
value[1] as u32,
value[2] as u32,
value[3] as u32,
[
value[4] as u32,
value[5] as u32,
value[6] as u32,
value[7] as u32,
],
),
SyscallNumber::UpdateMemoryFlags => { SyscallNumber::UpdateMemoryFlags => {
Syscall::UpdateMemoryFlags(value[1], value[2], value[3]) Syscall::UpdateMemoryFlags(value[1], value[2], value[3])
} }

View File

@ -0,0 +1,246 @@
/// Flags to be passed to the MapMemory struct.
/// Note that it is an error to have memory be
/// writable and not readable.
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Debug)]
pub struct MemoryFlags {
bits: usize,
}
impl MemoryFlags {
/// Free this memory
pub const FREE: Self = Self { bits: 0b0000_0000 };
/// Immediately allocate this memory. Otherwise it will
/// be demand-paged. This is implicitly set when `phys`
/// is not 0.
pub const VALID: Self = Self { bits: 0b0000_0001 };
/// Allow the CPU to read from this page.
pub const READ: Self = Self { bits: 0b0000_0010 };
/// Allow the CPU to write to this page.
pub const WRITE: Self = Self { bits: 0b0000_0100 };
/// Allow the CPU to execute from this page.
pub const EXECUTE: Self = Self { bits: 0b0000_1000 };
/// Accessible from user mode
pub const USERMODE: Self = Self { bits: 0b0001_0000 };
/// Globally-available
pub const GLOBAL: Self = Self { bits: 0b0010_0000 };
/// Cache access status
pub const ACCESSED: Self = Self { bits: 0b0100_0000 };
/// Page needs flushing
pub const DIRTY: Self = Self { bits: 0b1000_0000 };
pub fn bits(&self) -> usize {
self.bits
}
pub fn from_bits(raw: usize) -> Option<MemoryFlags> {
if raw > 255 {
None
} else {
Some(MemoryFlags { bits: raw })
}
}
pub fn is_empty(&self) -> bool {
self.bits == 0
}
pub fn empty() -> MemoryFlags {
MemoryFlags { bits: 0 }
}
pub fn all() -> MemoryFlags {
MemoryFlags { bits: 255 }
}
pub fn contains(&self, other: MemoryFlags) -> bool {
(self.bits & other.bits) == other.bits
}
}
impl core::fmt::Binary for MemoryFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Binary::fmt(&self.bits, f)
}
}
impl core::fmt::Octal for MemoryFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Octal::fmt(&self.bits, f)
}
}
impl core::fmt::LowerHex for MemoryFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::LowerHex::fmt(&self.bits, f)
}
}
impl core::fmt::UpperHex for MemoryFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::UpperHex::fmt(&self.bits, f)
}
}
impl core::ops::BitOr for MemoryFlags {
type Output = Self;
/// Returns the union of the two sets of flags.
#[inline]
fn bitor(self, other: MemoryFlags) -> Self {
Self {
bits: self.bits | other.bits,
}
}
}
impl core::ops::BitOrAssign for MemoryFlags {
/// Adds the set of flags.
#[inline]
fn bitor_assign(&mut self, other: Self) {
self.bits |= other.bits;
}
}
impl core::ops::BitXor for MemoryFlags {
type Output = Self;
/// Returns the left flags, but with all the right flags toggled.
#[inline]
fn bitxor(self, other: Self) -> Self {
Self {
bits: self.bits ^ other.bits,
}
}
}
impl core::ops::BitXorAssign for MemoryFlags {
/// Toggles the set of flags.
#[inline]
fn bitxor_assign(&mut self, other: Self) {
self.bits ^= other.bits;
}
}
impl core::ops::BitAnd for MemoryFlags {
type Output = Self;
/// Returns the intersection between the two sets of flags.
#[inline]
fn bitand(self, other: Self) -> Self {
Self {
bits: self.bits & other.bits,
}
}
}
impl core::ops::BitAndAssign for MemoryFlags {
/// Disables all flags disabled in the set.
#[inline]
fn bitand_assign(&mut self, other: Self) {
self.bits &= other.bits;
}
}
impl core::ops::Sub for MemoryFlags {
type Output = Self;
/// Returns the set difference of the two sets of flags.
#[inline]
fn sub(self, other: Self) -> Self {
Self {
bits: self.bits & !other.bits,
}
}
}
impl core::ops::SubAssign for MemoryFlags {
/// Disables all flags enabled in the set.
#[inline]
fn sub_assign(&mut self, other: Self) {
self.bits &= !other.bits;
}
}
impl core::ops::Not for MemoryFlags {
type Output = Self;
/// Returns the complement of this set of flags.
#[inline]
fn not(self) -> Self {
Self { bits: !self.bits } & MemoryFlags { bits: 255 }
}
}
impl core::fmt::Display for MemoryFlags {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut first = true;
if self.contains(MemoryFlags::FREE) {
write!(f, "FREE")?;
first = false;
}
if self.contains(MemoryFlags::VALID) {
if !first {
write!(f, " | ")?;
}
write!(f, "VALID")?;
first = false;
}
if self.contains(MemoryFlags::READ) {
if !first {
write!(f, " | ")?;
}
write!(f, "READ")?;
first = false;
}
if self.contains(MemoryFlags::WRITE) {
if !first {
write!(f, " | ")?;
}
write!(f, "WRITE")?;
first = false;
}
if self.contains(MemoryFlags::EXECUTE) {
if !first {
write!(f, " | ")?;
}
write!(f, "EXECUTE")?;
first = false;
}
if self.contains(MemoryFlags::USERMODE) {
if !first {
write!(f, " | ")?;
}
write!(f, "USERMODE")?;
first = false;
}
if self.contains(MemoryFlags::GLOBAL) {
if !first {
write!(f, " | ")?;
}
write!(f, "GLOBAL")?;
first = false;
}
if self.contains(MemoryFlags::ACCESSED) {
if !first {
write!(f, " | ")?;
}
write!(f, "ACCESSED")?;
first = false;
}
if self.contains(MemoryFlags::DIRTY) {
if !first {
write!(f, " | ")?;
}
write!(f, "DIRTY")?;
}
Ok(())
}
}

296
src/xous/syscalls.rs Normal file
View File

@ -0,0 +1,296 @@
use super::definitions::{SyscallErrorNumber, SyscallResultNumber};
use super::services;
use super::Memory;
use super::SyscallResult;
use riscv_cpu::cpu::Memory as OtherMemory;
pub fn map_memory(
memory: &mut Memory,
phys: i64,
virt: i64,
size: i64,
_flags: i64,
) -> SyscallResult {
// print!(
// "MapMemory(phys: {:08x}, virt: {:08x}, bytes: {}, flags: {:02x})",
// phys, virt, size, _flags
// );
if virt != 0 {
unimplemented!("Non-zero virt address");
}
if phys != 0 {
unimplemented!("Non-zero phys address");
}
if let Some(region) = memory.allocate_virt_region(size as usize) {
[
SyscallResultNumber::MemoryRange as i64,
region as i64,
size,
0,
0,
0,
0,
0,
]
.into()
} else {
// self.print_mmu();
println!(
"Couldn't find a free spot to allocate {} bytes of virtual memory, or out of memory",
size as usize
);
[
SyscallResultNumber::Error as i64,
SyscallErrorNumber::OutOfMemory as i64,
0,
0,
0,
0,
0,
0,
]
.into()
}
}
pub fn connect(memory: &mut Memory, id: [u32; 4]) -> SyscallResult {
// println!(
// "Connect([0x{:08x}, 0x{:08x}, 0x{:08x}, 0x{:08x}])",
// id[0], id[1], id[2], id[3]
// );
if let Some(service) = super::super::xous::services::get_service(&id) {
let connection_id = memory.connections.len() as u32 + 1;
memory.connections.insert(connection_id, service);
[
SyscallResultNumber::ConnectionId as i64,
connection_id as i64,
0,
0,
0,
0,
0,
0,
]
.into()
} else {
[
SyscallResultNumber::ConnectionId as i64,
0,
0,
0,
0,
0,
0,
0,
]
.into()
}
}
pub fn try_connect(memory: &mut Memory, id: [u32; 4]) -> SyscallResult {
connect(memory, id)
}
pub fn send_message(
memory: &mut Memory,
connection_id: u32,
kind: u32,
opcode: u32,
args: [u32; 4],
) -> SyscallResult {
// println!(
// "SendMessage({}, {}, {}: {:x?})",
// connection_id, kind, opcode, args
// );
let memory_region = if kind == 1 || kind == 2 || kind == 3 {
let mut memory_region = vec![0; args[1] as usize];
for (offset, value) in memory_region.iter_mut().enumerate() {
*value = memory.read_u8(
memory
.virt_to_phys(args[0] + offset as u32)
.expect("invalid memory address") as u64,
);
}
Some(memory_region)
} else {
None
};
let Some(service) = memory.connections.get_mut(&connection_id) else {
println!("Unhandled connection ID {}", connection_id);
return [
SyscallResultNumber::Error as i64,
9, /* ServerNotFound */
0,
0,
0,
0,
0,
0,
]
.into();
};
match kind {
1..=3 => {
let mut memory_region = memory_region.unwrap();
let extra = [args[2], args[3]];
match kind {
1 => match service.lend_mut(0, opcode, &mut memory_region, extra) {
services::LendResult::WaitForResponse(msg) => msg.into(),
services::LendResult::MemoryReturned(result) => {
for (offset, value) in memory_region.into_iter().enumerate() {
memory.write_u8(args[0] as u64 + offset as u64, value);
}
[
SyscallResultNumber::MemoryReturned as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into()
}
},
2 => match service.lend(0, opcode, &memory_region, extra) {
services::LendResult::WaitForResponse(msg) => msg.into(),
services::LendResult::MemoryReturned(result) => [
SyscallResultNumber::MemoryReturned as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into(),
},
3 => {
service.send(0, opcode, &memory_region, extra);
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
}
_ => unreachable!(),
}
}
4 => {
service.scalar(0, opcode, args);
[SyscallResultNumber::Ok as i64, 0, 0, 0, 0, 0, 0, 0].into()
}
5 => match service.blocking_scalar(0, opcode, args) {
services::ScalarResult::Scalar1(result) => [
SyscallResultNumber::Scalar1 as i64,
result as i64,
0,
0,
0,
0,
0,
0,
]
.into(),
services::ScalarResult::Scalar2(result) => [
SyscallResultNumber::Scalar2 as i64,
result[0] as i64,
result[1] as i64,
0,
0,
0,
0,
0,
]
.into(),
services::ScalarResult::Scalar5(result) => [
SyscallResultNumber::Scalar5 as i64,
result[0] as i64,
result[1] as i64,
result[2] as i64,
result[3] as i64,
result[4] as i64,
0,
0,
]
.into(),
services::ScalarResult::WaitForResponse(msg) => msg.into(),
},
_ => {
println!("Unknown message kind {}", kind);
[
SyscallResultNumber::Error as i64,
9, /* ServerNotFound */
0,
0,
0,
0,
0,
0,
]
.into()
}
}
}
pub fn try_send_message(
memory: &mut Memory,
connection_id: u32,
kind: u32,
opcode: u32,
args: [u32; 4],
) -> SyscallResult {
send_message(memory, connection_id, kind, opcode, args)
}
pub fn increase_heap(memory: &mut Memory, delta: i64, _flags: i64) -> SyscallResult {
assert!(delta & 0xfff == 0, "delta must be page-aligned");
let increase_bytes = delta as u32;
let heap_address = memory.heap_start + memory.heap_size;
if delta == 0 {
return [
SyscallResultNumber::MemoryRange as i64,
memory.heap_start as i64,
if memory.heap_size == 0 {
4096
} else {
memory.heap_size
} as i64,
0,
0,
0,
0,
0,
]
.into();
}
if heap_address.saturating_add(increase_bytes) > super::HEAP_END {
[
SyscallResultNumber::Error as i64,
SyscallErrorNumber::OutOfMemory as i64,
0,
0,
0,
0,
0,
0,
]
.into()
} else {
for new_address in (heap_address..(heap_address + increase_bytes)).step_by(4096) {
memory.ensure_page(new_address);
}
let new_heap_region = memory.heap_start + memory.heap_size;
memory.heap_size += increase_bytes;
[
SyscallResultNumber::MemoryRange as i64,
new_heap_region as i64,
delta,
0,
0,
0,
0,
0,
]
.into()
}
}