initial kernel code
This commit is contained in:
commit
4c299a4491
93 changed files with 7405 additions and 0 deletions
12
.cargo/config
Normal file
12
.cargo/config
Normal file
|
@ -0,0 +1,12 @@
|
|||
[target.powerpc-unknown-linux-gnu]
|
||||
linker = "powerpc-elf-gcc"
|
||||
ar = "powerpc-elf-ar"
|
||||
rustflags = [
|
||||
"-C", "link-args=-nostdlib -ffreestanding -mbig-endian -fPIC",
|
||||
"-C", "target-feature=+crt-static"
|
||||
]
|
||||
|
||||
[tarcet.riscv32i-unknown-none-elf]
|
||||
rustflags = [
|
||||
"-C", "link-args=-nostdlib -ffreestanding -fPIC",
|
||||
]
|
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/target
|
||||
/.idea
|
||||
/test.img
|
||||
/test_ppc.img
|
||||
/mount
|
48
Cargo.lock
generated
Normal file
48
Cargo.lock
generated
Normal file
|
@ -0,0 +1,48 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5252b3d2648e5eedbc1a6f501e3c795e07025c1e93bbf8bbdd6eef7f447a6d54"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.160"
|
||||
source = "git+https://github.com/rust-lang/compiler-builtins#a6145905160801fb2a6ac45aab3f1ceeeabc15a8"
|
||||
|
||||
[[package]]
|
||||
name = "ddi"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d"
|
||||
|
||||
[[package]]
|
||||
name = "lbos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"compiler_builtins",
|
||||
"ddi",
|
||||
"liblbos",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblbos"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
41
Cargo.toml
Normal file
41
Cargo.toml
Normal file
|
@ -0,0 +1,41 @@
|
|||
[workspace]
|
||||
members = ["liblbos"]
|
||||
exclude = ["turntable", "makeddi", "ddi", "example"]
|
||||
|
||||
[package]
|
||||
name = "lbos"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
compiler_builtins = { git = "https://github.com/rust-lang/compiler-builtins", features = ["mem"], optional = true }
|
||||
liblbos = { path = "liblbos" }
|
||||
ddi = { path = "ddi", default-features = false }
|
||||
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
strip = false
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
debug_messages = []
|
||||
arch_virt = ["dev_virtio", "fs_fat32", "liblbos/arch_riscv32", "ddi/arch_riscv32"]
|
||||
arch_ofw = []
|
||||
arch_ppc32 = ["compiler_builtins", "arch_ofw", "fs_fat32"]
|
||||
dev_virtio = []
|
||||
fs_fat32 = []
|
13
LICENSE
Normal file
13
LICENSE
Normal file
|
@ -0,0 +1,13 @@
|
|||
Copyright 2025 Real Microsoft, LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this repository except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
89
README.md
Normal file
89
README.md
Normal file
|
@ -0,0 +1,89 @@
|
|||
# lifeblood os
|
||||
### extreme warning: this project is in an INCREDIBLY early stage, see **warnings** section
|
||||
lifeblood os is the operating system for the lifeblood project
|
||||
it currently supports riscv32imac, and has some basic support for ppc32 (albeit it probably won't compile, and
|
||||
has no drivers)
|
||||
|
||||
## warnings
|
||||
- as of right now, this project has just gotten to the point where it can run the shell and run programs from it.
|
||||
please keep this in mind.
|
||||
- the design of the kernel is *very esoteric*, this is mainly due to it being targeted for systems with very limited
|
||||
memory, as the lifeblood computer was originally designed around 32K of ram (although we're currently redesigning
|
||||
to include more, and the qemu emulator version requires 5MiB to run)
|
||||
- tons of stuff is missing, notably if you develop a program, you may run into a relocation issue due to the limitations
|
||||
of the lbos relocator.
|
||||
- speaking of which, if you do not have experience with low level programming, you may currently find it difficult to
|
||||
write programs, however still feel free to explore the code and see what you can do!
|
||||
|
||||
## building
|
||||
install the rust toolchain for riscv32imac-unknown-none-elf, then run the following commands for each thing you want
|
||||
to build:
|
||||
### kernel
|
||||
```shell
|
||||
LBOS_ARCH=virt cargo build --release --target riscv32imac-unknown-none-elf --features arch_virt
|
||||
```
|
||||
### TURNTBL.DDI
|
||||
```shell
|
||||
cd turntable
|
||||
LBOS_ARCH=riscv32 cargo build --release --target riscv32imac-unknown-none-elf --features arch_riscv32
|
||||
cd ..
|
||||
# the following is required to convert the ELF output to the DDI format that lbos uses
|
||||
cd makeddi
|
||||
run -- ../turntable/target/riscv32imac-unknown-none-elf/release/turntable ../turntable/target/riscv32imac-unknown-none-elf/release/TURNTBL.DDI
|
||||
cd ..
|
||||
```
|
||||
### EXAMPLE.DDI
|
||||
```shell
|
||||
cd example
|
||||
LBOS_ARCH=riscv32 cargo build --release --target riscv32imac-unknown-none-elf --features arch_riscv32
|
||||
cd ..
|
||||
# the following is required to convert the ELF output to the DDI format that lbos uses
|
||||
cd makeddi
|
||||
run -- ../example/target/riscv32imac-unknown-none-elf/release/example ../example/target/riscv32imac-unknown-none-elf/release/EXAMPLE.DDI
|
||||
cd ..
|
||||
```
|
||||
|
||||
## getting it running
|
||||
you'll need to install qemu with support for riscv32, and make a fat32 filesystem image with the shell "TURNTBL.DDI"
|
||||
and optionally the "EXAMPLE.DDI" program.
|
||||
**NOTE FOR FAT32 FILESYSTEM:** it is currently required that the sector size is 512 bytes, this is hardcoded; cluster
|
||||
size is not hardcoded.
|
||||
the best way to do this is to run something like
|
||||
```shell
|
||||
dd if=/dev/zero of=test.img bs=1M count=256 status=progress
|
||||
mkdosfs -F 32 test.img
|
||||
sudo losetup -fP test.img
|
||||
```
|
||||
and then run the `./copy_to_fs.sh` script to copy the built files to the filesystem like so
|
||||
```shell
|
||||
./copy_to_fs.sh /dev/loop0 # or whatever the loopback device you created is called, see losetup -a
|
||||
```
|
||||
note that the script will likely need to be run as root because of linux's requirements for fat32 filesystems; and more
|
||||
importantly that the script expects the binary files to be in the directories above in the build section.
|
||||
after you do this, the filesystem should be ready for the kernel and you should be able to run a variant of the
|
||||
following to run the kernel
|
||||
```shell
|
||||
qemu-system-riscv32 -machine virt -bios none -drive if=none,format=raw,file=/dev/loop0,id=disk1 -device virtio-blk-device,drive=disk1 -serial mon:stdio -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos
|
||||
```
|
||||
(note that this will take over your terminal and steal CTRL+C, so you'll need to kill it by closing the terminal or
|
||||
manually killing the process)
|
||||
|
||||
## developing programs
|
||||
currently, the best way to program is to use rust and link to the liblbos library included in this repo,
|
||||
you should be able to using something like
|
||||
```toml
|
||||
[dependencies]
|
||||
liblbos = { git = "https://forge.voremicrocomputers.com/Vore_Microcomputers/lifeblood_os.git" }
|
||||
```
|
||||
but i have not tested this yet so please tell me if it doesn't work
|
||||
|
||||
if you want to develop a program in another language, you'll need to investigate everything in this repo to figure out
|
||||
how lbos expects programs to look. (this will be documented eventually)
|
||||
|
||||
good places to start with either developing in rust, or porting another language are:
|
||||
- turntable, the lbos shell which showcases some basic fs operations and task loading
|
||||
- example, a simple hello world program that should give you the bare minimum to build and link correctly
|
||||
- liblbos, a helper library that defines structs (all should be C repr) as well as helper functions for syscalls and
|
||||
kernel IPC
|
||||
|
||||
feel free to shoot me an email if you have any questions! (nikocs at voremicrocomputers dot com)
|
33
build.rs
Normal file
33
build.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
fn main() {
|
||||
let arch = std::env::var("LBOS_ARCH").unwrap_or("virt".to_string());
|
||||
println!("cargo:rerun-if-env-changed=LBOS_ARCH");
|
||||
println!("cargo:rustc-cfg=feature=\"arch_{}\"", arch);
|
||||
|
||||
println!("cargo:rerun-if-changed=src/arch/{}/asm", arch);
|
||||
|
||||
if arch == "ppc32" {
|
||||
let mut cc_build = cc::Build::new();
|
||||
let cc_build = cc_build
|
||||
.file(format!("src/arch/{}/asm/realmode.S", arch))
|
||||
.file(format!("src/arch/{}/asm/trap.S", arch));
|
||||
|
||||
match &arch[..] {
|
||||
"ppc32" => {
|
||||
cc_build
|
||||
.flag("-mcpu=powerpc")
|
||||
.flag("-m32")
|
||||
.flag("-nostdlib")
|
||||
.flag("-msoft-float");
|
||||
},
|
||||
_ => panic!("unknown architecture: {}", arch),
|
||||
}
|
||||
|
||||
cc_build.compile("vap_asm");
|
||||
|
||||
// link to the assembly file
|
||||
println!("cargo:rustc-link-lib=static=vap_asm");
|
||||
}
|
||||
|
||||
// specify the linker.ld script
|
||||
println!("cargo:rustc-link-arg=-Tsrc/arch/{arch}/asm/linker.ld");
|
||||
}
|
16
copy_to_fs.sh
Executable file
16
copy_to_fs.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/bin/bash
|
||||
|
||||
storage_device="${1}"
|
||||
|
||||
umount "${storage_device}"
|
||||
echo "MOUNTING..."
|
||||
mkdir -p mount
|
||||
mount "${storage_device}" mount
|
||||
|
||||
echo "COPYING..."
|
||||
cp -v turntable/target/riscv32imac-unknown-none-elf/release/TURNTBL.DDI mount
|
||||
cp -v example/target/riscv32imac-unknown-none-elf/release/EXAMPLE.DDI mount
|
||||
|
||||
echo "UNMOUNTING..."
|
||||
umount "${storage_device}"
|
||||
echo "COMPLETE"
|
2
ddi/.gitignore
vendored
Normal file
2
ddi/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.idea
|
7
ddi/Cargo.lock
generated
Normal file
7
ddi/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ddi"
|
||||
version = "0.1.0"
|
10
ddi/Cargo.toml
Normal file
10
ddi/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "ddi"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
default = ["arch_riscv32"]
|
||||
arch_riscv32 = []
|
2
ddi/src/arch/mod.rs
Normal file
2
ddi/src/arch/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "arch_riscv32")]
|
||||
pub mod riscv32;
|
109
ddi/src/arch/riscv32/mod.rs
Normal file
109
ddi/src/arch/riscv32/mod.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
use crate::DDIRelocationHeader;
|
||||
|
||||
#[repr(u16)]
|
||||
pub enum RiscVRelocationType {
|
||||
None = 0,
|
||||
// word32, sym + addend
|
||||
R32 = 1,
|
||||
// word64, sym + addend
|
||||
R64 = 2,
|
||||
// usize, base + addend
|
||||
Relative = 3,
|
||||
// direct copy
|
||||
Copy = 4,
|
||||
// not yet handled
|
||||
JumpSlot = 5,
|
||||
TlsDtpMod32 = 6,
|
||||
TlsDtpMod64 = 7,
|
||||
TlsDtpRel32 = 8,
|
||||
TlsDtpRel64 = 9,
|
||||
TlsTpRel32 = 10,
|
||||
TlsTpRel64 = 11,
|
||||
// 12 bit PC relative branch
|
||||
// btype, sym + addend - position
|
||||
Branch = 16,
|
||||
// 20 bit PC relative jump
|
||||
// jtype, sym + addend - position
|
||||
Jal = 17,
|
||||
// 32 bit PC relative call
|
||||
// u+i type pair, sym + addend - position
|
||||
Call = 18,
|
||||
// should be same as call
|
||||
CallPLT = 19,
|
||||
// high 20 bits of 32 bit PC relative GOT access
|
||||
// u type, symbol got offset + got address + addend - position
|
||||
GOTHi20 = 20,
|
||||
// should be same as GOT
|
||||
TlsGOTHi20 = 21,
|
||||
// should be same as GOT
|
||||
TlsGDHi20 = 22,
|
||||
// high 20 bits of 32 bit pc relative reference
|
||||
// u type, sym + addend - position
|
||||
PCRelHi20 = 23,
|
||||
// low 12 bits of 32 bit pc relative reference, addend always 0
|
||||
// i type, sym - position
|
||||
PCRelLo12I = 24,
|
||||
// low 12 bits of 32 bit pc relative reference, addend always 0
|
||||
// s type sym - position
|
||||
PCRelLo12S = 25,
|
||||
// high 20 bits of 32 bit absolute address
|
||||
// u type, sym + addend
|
||||
Hi20 = 26,
|
||||
// low 12 bits of 32 bit absolute address
|
||||
// i type, sym + addend
|
||||
Lo12I = 27,
|
||||
// low 12 bits of 32 bit absolute address
|
||||
// s type, sym + addend
|
||||
Lo12S = 28,
|
||||
}
|
||||
|
||||
fn patch_hi20_uins(mut instruction: u32, mut pointer: u32) -> u32 {
|
||||
// check if LO is going to result in a negative number
|
||||
let lo = pointer & 0xFFF;
|
||||
if lo & 0x800 != 0 {
|
||||
// lo is negative
|
||||
// add 0x1000 to hi
|
||||
pointer += 0x1000;
|
||||
}
|
||||
let hi = (pointer & 0xFFFFF000) & 0xFFFFF000;
|
||||
instruction = (instruction & !0xFFFFF000) | hi;
|
||||
instruction
|
||||
}
|
||||
|
||||
fn patch_lo12_iins(mut instruction: u32, pointer: u32) -> u32 {
|
||||
let lo = ((pointer & 0xFFF) << 20) & 0xFFF00000;
|
||||
instruction = (instruction & !0xFFF00000) | lo;
|
||||
instruction
|
||||
}
|
||||
|
||||
fn sap_addr(target_pointer: u32, target_segment_base: u32, relocation_pointer: u32, current_segment_base: u32) -> u32 {
|
||||
(target_pointer).wrapping_sub(relocation_pointer)
|
||||
}
|
||||
|
||||
pub fn apply_relocation(segment_buffer: &mut [u8], current_segment_base: u32, target_segment_base: u32, relocation_header: &DDIRelocationHeader, unhandled_callback: fn(u16)) {
|
||||
match relocation_header.relocation_type {
|
||||
x if x == RiscVRelocationType::None as u16 => {
|
||||
// do nothing
|
||||
}
|
||||
x if x == RiscVRelocationType::CallPLT as u16 || x == RiscVRelocationType::Call as u16 => {
|
||||
// these are relative, do nothing for now
|
||||
}
|
||||
x if x == RiscVRelocationType::Hi20 as u16 => {
|
||||
let u_ins_ptr = relocation_header.relocation_pointer as usize;
|
||||
let mut u_ins = u32::from_le_bytes(segment_buffer[u_ins_ptr..u_ins_ptr+4].try_into().unwrap());
|
||||
let addr = relocation_header.target_pointer as u32 + target_segment_base;
|
||||
u_ins = patch_hi20_uins(u_ins, addr);
|
||||
segment_buffer[u_ins_ptr..u_ins_ptr+4].copy_from_slice(&u_ins.to_le_bytes());
|
||||
}
|
||||
x if x == RiscVRelocationType::Lo12I as u16 => {
|
||||
let i_ins_ptr = relocation_header.relocation_pointer as usize;
|
||||
let mut i_ins = u32::from_le_bytes(segment_buffer[i_ins_ptr..i_ins_ptr+4].try_into().unwrap());
|
||||
let addr = relocation_header.target_pointer as u32 + target_segment_base;
|
||||
i_ins = patch_lo12_iins(i_ins, addr);
|
||||
segment_buffer[i_ins_ptr..i_ins_ptr+4].copy_from_slice(&i_ins.to_le_bytes());
|
||||
}
|
||||
x => {
|
||||
unhandled_callback(x);
|
||||
}
|
||||
}
|
||||
}
|
126
ddi/src/lib.rs
Normal file
126
ddi/src/lib.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
#![no_std]
|
||||
//! どこでも一緒、いつまでも!〜
|
||||
|
||||
pub mod arch;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DDIHeader {
|
||||
pub magic: [u8; 4], // "ddI\0"
|
||||
pub version: u8,
|
||||
pub ddi_type: u8,
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum DDIType {
|
||||
LBOSRiscV = b'R',
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DDILBOSHeader {
|
||||
pub entrypoint: u32,
|
||||
pub segment_header_pointer: u32,
|
||||
pub segment_header_count: u32,
|
||||
pub relocation_header_pointer: u32,
|
||||
pub relocation_header_count: u32,
|
||||
pub entrypoint_segment: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DDISegmentHeader {
|
||||
pub segment_pointer: u32,
|
||||
pub segment_size: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DDIRelocationHeader {
|
||||
pub relocation_segment: u32, // what segment this relocation is for
|
||||
pub relocation_pointer: u32, // offset from the start of the segment
|
||||
pub target_segment: u32, // what segment this relocation points to
|
||||
pub target_pointer: u16, // offset from the start of the target segment
|
||||
pub relocation_type: u16, // arch specific, see arch/riscv32/mod.rs
|
||||
}
|
||||
|
||||
pub fn read_ddi_header(data: &[u8]) -> Option<DDIHeader> {
|
||||
if data.len() < size_of::<DDIHeader>() {
|
||||
return None;
|
||||
}
|
||||
Some(DDIHeader {
|
||||
magic: data[0..4].try_into().ok()?,
|
||||
version: data[4],
|
||||
ddi_type: match data[5] {
|
||||
x if x == DDIType::LBOSRiscV as u8 => DDIType::LBOSRiscV as u8,
|
||||
_ => return None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_ddi_header(data: &mut [u8], header: &DDIHeader) {
|
||||
data[0..4].copy_from_slice(&header.magic);
|
||||
data[4] = header.version;
|
||||
data[5] = header.ddi_type;
|
||||
}
|
||||
|
||||
pub fn read_ddi_lbos_header(data: &[u8]) -> Option<DDILBOSHeader> {
|
||||
if data.len() < size_of::<DDIHeader>() + size_of::<DDILBOSHeader>() {
|
||||
return None;
|
||||
}
|
||||
let header_offset = size_of::<DDIHeader>();
|
||||
Some(DDILBOSHeader {
|
||||
entrypoint: u32::from_be_bytes((&data[header_offset..header_offset+4]).try_into().ok()?),
|
||||
segment_header_pointer: u32::from_be_bytes((&data[header_offset+4..header_offset+8]).try_into().ok()?),
|
||||
segment_header_count: u32::from_be_bytes((&data[header_offset+8..header_offset+12]).try_into().ok()?),
|
||||
relocation_header_pointer: u32::from_be_bytes((&data[header_offset+12..header_offset+16]).try_into().ok()?),
|
||||
relocation_header_count: u32::from_be_bytes((&data[header_offset+16..header_offset+20]).try_into().ok()?),
|
||||
entrypoint_segment: u32::from_be_bytes((&data[header_offset+20..header_offset+24]).try_into().ok()?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_ddi_lbos_header(data: &mut [u8], header: &DDILBOSHeader) {
|
||||
let header_offset = size_of::<DDIHeader>();
|
||||
data[header_offset..header_offset+4].copy_from_slice(&header.entrypoint.to_be_bytes());
|
||||
data[header_offset+4..header_offset+8].copy_from_slice(&header.segment_header_pointer.to_be_bytes());
|
||||
data[header_offset+8..header_offset+12].copy_from_slice(&header.segment_header_count.to_be_bytes());
|
||||
data[header_offset+12..header_offset+16].copy_from_slice(&header.relocation_header_pointer.to_be_bytes());
|
||||
data[header_offset+16..header_offset+20].copy_from_slice(&header.relocation_header_count.to_be_bytes());
|
||||
data[header_offset+20..header_offset+24].copy_from_slice(&header.entrypoint_segment.to_be_bytes());
|
||||
}
|
||||
|
||||
pub fn read_ddi_segment_header(data: &[u8], header: &DDILBOSHeader, index: usize) -> Option<DDISegmentHeader> {
|
||||
if data.len() < header.segment_header_pointer as usize + size_of::<DDISegmentHeader>() + (index * size_of::<DDISegmentHeader>()) {
|
||||
return None;
|
||||
}
|
||||
let header_offset = header.segment_header_pointer as usize + (index * size_of::<DDISegmentHeader>());
|
||||
Some(DDISegmentHeader {
|
||||
segment_pointer: u32::from_be_bytes((&data[header_offset..header_offset+4]).try_into().ok()?),
|
||||
segment_size: u32::from_be_bytes((&data[header_offset+4..header_offset+8]).try_into().ok()?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_ddi_segment_header(data: &mut [u8], header: &DDILBOSHeader, index: usize, datum: &DDISegmentHeader) {
|
||||
let header_offset = header.segment_header_pointer as usize + (index * size_of::<DDISegmentHeader>());
|
||||
data[header_offset..header_offset+4].copy_from_slice(&datum.segment_pointer.to_be_bytes());
|
||||
data[header_offset+4..header_offset+8].copy_from_slice(&datum.segment_size.to_be_bytes());
|
||||
}
|
||||
|
||||
pub fn read_ddi_relocation_header(data: &[u8], header: &DDILBOSHeader, index: usize) -> Option<DDIRelocationHeader> {
|
||||
if data.len() < header.relocation_header_pointer as usize + size_of::<DDIRelocationHeader>() + (index * size_of::<DDIRelocationHeader>()) {
|
||||
return None;
|
||||
}
|
||||
let header_offset = header.relocation_header_pointer as usize + (index * size_of::<DDIRelocationHeader>());
|
||||
Some(DDIRelocationHeader {
|
||||
relocation_segment: u32::from_be_bytes((&data[header_offset..header_offset+4]).try_into().ok()?),
|
||||
relocation_pointer: u32::from_be_bytes((&data[header_offset+4..header_offset+8]).try_into().ok()?),
|
||||
target_segment: u32::from_be_bytes((&data[header_offset+8..header_offset+12]).try_into().ok()?),
|
||||
target_pointer: u16::from_be_bytes((&data[header_offset+12..header_offset+14]).try_into().ok()?),
|
||||
relocation_type: u16::from_be_bytes((&data[header_offset+14..header_offset+16]).try_into().ok()?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn write_ddi_relocation_header(data: &mut [u8], header: &DDILBOSHeader, index: usize, datum: &DDIRelocationHeader) {
|
||||
let header_offset = header.relocation_header_pointer as usize + (index * size_of::<DDIRelocationHeader>());
|
||||
data[header_offset..header_offset+4].copy_from_slice(&datum.relocation_segment.to_be_bytes());
|
||||
data[header_offset+4..header_offset+8].copy_from_slice(&datum.relocation_pointer.to_be_bytes());
|
||||
data[header_offset+8..header_offset+12].copy_from_slice(&datum.target_segment.to_be_bytes());
|
||||
data[header_offset+12..header_offset+14].copy_from_slice(&datum.target_pointer.to_be_bytes());
|
||||
data[header_offset+14..header_offset+16].copy_from_slice(&datum.relocation_type.to_be_bytes());
|
||||
}
|
106
ddi/src/main.rs
Normal file
106
ddi/src/main.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use ddi::{
|
||||
DDIType, read_ddi_header, read_ddi_lbos_header, read_ddi_relocation_header,
|
||||
read_ddi_segment_header,
|
||||
};
|
||||
use ddi::arch::riscv32::{apply_relocation, RiscVRelocationType};
|
||||
|
||||
pub fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let input = args.get(1).expect("no input file");
|
||||
let buf = std::fs::read(input).expect("failed to read file");
|
||||
|
||||
let header = read_ddi_header(&buf);
|
||||
if let Some(header) = header {
|
||||
println!("UWU header:");
|
||||
if header.magic == *b"ddI\0" {
|
||||
println!("magic: ddI\\0 (valid)");
|
||||
} else {
|
||||
println!("magic: invalid");
|
||||
}
|
||||
match &header.ddi_type {
|
||||
x if *x == DDIType::LBOSRiscV as u8 => {
|
||||
println!("type: lbos riscv");
|
||||
}
|
||||
_ => {
|
||||
println!("type: invalid");
|
||||
}
|
||||
}
|
||||
println!("version: {}", header.version);
|
||||
let mut segment_buffers = Vec::new();
|
||||
let mut fake_segment_bases = Vec::new();
|
||||
let mut last_base = 0x80023C00;
|
||||
|
||||
match header.ddi_type {
|
||||
x if x == DDIType::LBOSRiscV as u8 => {
|
||||
if let Some(lbos) = read_ddi_lbos_header(&buf) {
|
||||
println!("entrypoint: {:x}", lbos.entrypoint);
|
||||
println!("segments:");
|
||||
for i in 0..lbos.segment_header_count as usize {
|
||||
let header = read_ddi_segment_header(&buf, &lbos, i);
|
||||
if let Some(header) = header {
|
||||
println!(" segment {}:", i);
|
||||
println!(" located at {}", header.segment_pointer);
|
||||
println!(" size: {}", header.segment_size);
|
||||
segment_buffers.push(
|
||||
buf[header.segment_pointer as usize
|
||||
..(header.segment_pointer + header.segment_size) as usize]
|
||||
.to_vec(),
|
||||
);
|
||||
fake_segment_bases.push(last_base);
|
||||
last_base += 0x1000;
|
||||
} else {
|
||||
println!("invalid fvb segment header at {i}");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
println!("relocations:");
|
||||
for i in 0..lbos.relocation_header_count as usize {
|
||||
let header = read_ddi_relocation_header(&buf, &lbos, i);
|
||||
if let Some(header) = header {
|
||||
println!(" relocation {}:", i);
|
||||
println!(" segment: {}", header.relocation_segment);
|
||||
println!(" pointer: {:x}", header.relocation_pointer);
|
||||
println!(" target: {}", header.target_segment);
|
||||
println!(" target pointer: {:x}", header.target_pointer);
|
||||
println!(" reltype: {}", header.relocation_type);
|
||||
|
||||
let initial_instruction = &segment_buffers[header.relocation_segment as usize][header.relocation_pointer as usize..header.relocation_pointer as usize +4];
|
||||
let second_instruction = if header.relocation_type == RiscVRelocationType::CallPLT as u16 || header.relocation_type == RiscVRelocationType::Call as u16 {
|
||||
&segment_buffers[header.relocation_segment as usize][header.relocation_pointer as usize + 4..header.relocation_pointer as usize + 8]
|
||||
} else {
|
||||
initial_instruction
|
||||
};
|
||||
println!(" initial instruction: {:x} {:x} {:x} {:x}", initial_instruction[0], initial_instruction[1], initial_instruction[2], initial_instruction[3]);
|
||||
if header.relocation_type == RiscVRelocationType::CallPLT as u16 || header.relocation_type == RiscVRelocationType::Call as u16 {
|
||||
println!(" second instruction: {:x} {:x} {:x} {:x}", second_instruction[0], second_instruction[1], second_instruction[2], second_instruction[3]);
|
||||
}
|
||||
let current_segment_base = fake_segment_bases[header.relocation_segment as usize];
|
||||
let target_segment_base = fake_segment_bases[header.target_segment as usize];
|
||||
apply_relocation(&mut segment_buffers[header.target_segment as usize], current_segment_base, target_segment_base, &header, |x| {
|
||||
println!(" unhandled relocation: {:x}", x);
|
||||
});
|
||||
let final_instruction = &segment_buffers[header.relocation_segment as usize][header.relocation_pointer as usize..header.relocation_pointer as usize +4];
|
||||
let second_final_instruction = if header.relocation_type == RiscVRelocationType::CallPLT as u16 || header.relocation_type == RiscVRelocationType::Call as u16 {
|
||||
&segment_buffers[header.relocation_segment as usize][header.relocation_pointer as usize + 4..header.relocation_pointer as usize + 8]
|
||||
} else {
|
||||
final_instruction
|
||||
};
|
||||
println!(" (relocating to: {:x})", target_segment_base);
|
||||
println!(" final instruction: {:x} {:x} {:x} {:x}", final_instruction[0], final_instruction[1], final_instruction[2], final_instruction[3]);
|
||||
if header.relocation_type == RiscVRelocationType::CallPLT as u16 || header.relocation_type == RiscVRelocationType::Call as u16 {
|
||||
println!(" second final instruction: {:x} {:x} {:x} {:x}", second_final_instruction[0], second_final_instruction[1], second_final_instruction[2], second_final_instruction[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("invalid header");
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("invalid header");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println!("no ddi header");
|
||||
}
|
||||
}
|
2
example/.gitignore
vendored
Normal file
2
example/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.idea
|
14
example/Cargo.lock
generated
Normal file
14
example/Cargo.lock
generated
Normal file
|
@ -0,0 +1,14 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"liblbos",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "liblbos"
|
||||
version = "0.1.0"
|
25
example/Cargo.toml
Normal file
25
example/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "example"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
liblbos = { path = "../liblbos" }
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[features]
|
||||
default = ["arch_riscv32"]
|
||||
arch_riscv32 = ["liblbos/arch_riscv32"]
|
16
example/build.rs
Normal file
16
example/build.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
fn main() {
|
||||
let arch = std::env::var("LBOS_ARCH").unwrap_or("riscv32".to_string());
|
||||
println!("cargo:rerun-if-env-changed=LBOS_ARCH");
|
||||
println!("cargo:rustc-cfg=feature=\"arch_{}\"", arch);
|
||||
|
||||
println!("cargo:rerun-if-changed=src/arch/{}/asm", arch);
|
||||
|
||||
// specify the linker.ld script
|
||||
println!("cargo:rustc-link-arg=-Tsrc/arch/{arch}/asm/linker.ld");
|
||||
|
||||
// output relocation info
|
||||
println!("cargo:rustc-link-arg=--emit-relocs");
|
||||
|
||||
// don't page align sections
|
||||
println!("cargo:rustc-link-arg=-n");
|
||||
}
|
2
example/src/arch/mod.rs
Normal file
2
example/src/arch/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "arch_riscv32")]
|
||||
mod riscv32;
|
34
example/src/arch/riscv32/asm/linker.ld
Normal file
34
example/src/arch/riscv32/asm/linker.ld
Normal file
|
@ -0,0 +1,34 @@
|
|||
OUTPUT_FORMAT( "elf32-littleriscv" )
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
|
||||
ENTRY( _start )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
program (wxa) : ORIGIN = 0x0, LENGTH = 20480
|
||||
}
|
||||
|
||||
PHDRS
|
||||
{
|
||||
prg PT_LOAD ;
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
. = ALIGN(512);
|
||||
*(.text .text.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.data : {
|
||||
*(.sdata .sdata.*) *(.data .data.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.bss : {
|
||||
*(.sbss .sbss.*)
|
||||
*(.bss .bss.*)
|
||||
} >program AT>program :prg
|
||||
}
|
10
example/src/arch/riscv32/mod.rs
Normal file
10
example/src/arch/riscv32/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use crate::main;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn _start() -> isize {
|
||||
unsafe {
|
||||
main();
|
||||
}
|
||||
|
||||
0
|
||||
}
|
17
example/src/main.rs
Normal file
17
example/src/main.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use crate::terminal::println;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
liblbos::syscalls::write_terminal(b"abort\n");
|
||||
loop {}
|
||||
}
|
||||
mod arch;
|
||||
mod terminal;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn main() {
|
||||
println("hello world!");
|
||||
}
|
10
example/src/terminal.rs
Normal file
10
example/src/terminal.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub fn print(s: &str) {
|
||||
unsafe {
|
||||
liblbos::syscalls::write_terminal(s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn println(s: &str) {
|
||||
print(s);
|
||||
print("\n");
|
||||
}
|
2
liblbos/.gitignore
vendored
Normal file
2
liblbos/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.idea
|
7
liblbos/Cargo.lock
generated
Normal file
7
liblbos/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "liblbos"
|
||||
version = "0.1.0"
|
10
liblbos/Cargo.toml
Normal file
10
liblbos/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "liblbos"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
arch_riscv32 = []
|
16
liblbos/src/arch/mod.rs
Normal file
16
liblbos/src/arch/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::syscalls::{sc2usize, SysCall};
|
||||
|
||||
#[cfg(feature = "arch_riscv32")]
|
||||
pub mod riscv32;
|
||||
|
||||
pub fn stall() {
|
||||
#[cfg(feature = "arch_riscv32")]
|
||||
riscv32::stall();
|
||||
}
|
||||
|
||||
pub fn syscall(sc: SysCall, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize) -> usize {
|
||||
#[cfg(feature = "arch_riscv32")]
|
||||
{
|
||||
riscv32::virt_syscall(sc2usize(sc) as _, a1 as _, a2 as _, a3 as _, a4 as _, a5 as _, a6 as _) as _
|
||||
}
|
||||
}
|
7
liblbos/src/arch/riscv32/asm/syscall.s
Normal file
7
liblbos/src/arch/riscv32/asm/syscall.s
Normal file
|
@ -0,0 +1,7 @@
|
|||
.option pic
|
||||
.option norvc
|
||||
.section .text
|
||||
.global _syscall
|
||||
_syscall:
|
||||
ecall
|
||||
ret
|
17
liblbos/src/arch/riscv32/mod.rs
Normal file
17
liblbos/src/arch/riscv32/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
use core::arch::{asm, global_asm};
|
||||
|
||||
global_asm!(include_str!("asm/syscall.s"));
|
||||
|
||||
pub fn stall() {
|
||||
unsafe {
|
||||
asm!("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _syscall(syscall_num: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32) -> u32;
|
||||
}
|
||||
|
||||
pub fn virt_syscall(num: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32) -> u32 {
|
||||
unsafe { _syscall(num, a1, a2, a3, a4, a5, a6) }
|
||||
}
|
55
liblbos/src/fs.rs
Normal file
55
liblbos/src/fs.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
#[repr(C)]
|
||||
pub struct FileSystem {
|
||||
pub unknown: [u8; 32]
|
||||
}
|
||||
|
||||
impl FileSystem {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
unknown: [0; 32]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DirectoryReader {
|
||||
pub unknown: [u8; 32]
|
||||
}
|
||||
|
||||
impl DirectoryReader {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
unknown: [0; 32]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct FileReader {
|
||||
pub unknown: [u8; 32]
|
||||
}
|
||||
|
||||
impl FileReader {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
unknown: [0; 32]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(u8)]
|
||||
pub enum RecordType {
|
||||
None = 0,
|
||||
Directory = 1,
|
||||
File = 2,
|
||||
Unknown = 3,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Record {
|
||||
pub name: [u8; 12],
|
||||
pub record_type: u8,
|
||||
/// this is fs implementation specific, don't trust this value outside of kernel
|
||||
pub target: u32,
|
||||
pub total_size_bytes: u32,
|
||||
}
|
310
liblbos/src/ktask.rs
Normal file
310
liblbos/src/ktask.rs
Normal file
|
@ -0,0 +1,310 @@
|
|||
#[repr(u16)]
|
||||
pub enum KTaskRequestType {
|
||||
DoNothing = 0,
|
||||
/// prints a message to the terminal greeting the user (:
|
||||
SayHi = 1,
|
||||
/// readies the filesystem for use
|
||||
/// takes a FileSystem struct as defined in crate::fs, this struct contains unknown members.
|
||||
/// that depend on what hbd filesystem the kernel was compiled with, but will always be 32 bytes.
|
||||
/// it's probably best to stack allocate it in the calling procees.
|
||||
/// 0 indicates success, 1 indicates failure
|
||||
FSOpen = 2,
|
||||
/// takes a string, interprets it as an absolute filesystem path, and opens it as a directory reader.
|
||||
/// requires a previous call to FSOpen.
|
||||
/// 0 indicates success,
|
||||
/// 1 = directory not found
|
||||
/// 2 = invalid record (i.e. you tried to open a file as a directory)
|
||||
/// 3 = user error (you probably didn't prefix the path with '/')
|
||||
FSOpenDir = 3,
|
||||
/// takes a directory reader and an empty record struct, and reads one record from the directory into it.
|
||||
/// requires a previous call to FSOpenDir
|
||||
/// 0 = success
|
||||
/// 1 = end of directory
|
||||
/// 2 = other error
|
||||
FSReadDir = 4,
|
||||
/// takes a file record and an empty file reader struct, and readies the file reader struct for reading
|
||||
/// requires a previous call to fsopen
|
||||
/// 0 = success
|
||||
/// 1 = failure
|
||||
FSOpenFile = 5,
|
||||
/// reads data from the file reader into the provided buffer, until either the buffer is full or the file is at the end.
|
||||
/// requires a previous call to FSOpenFile
|
||||
/// returns success on all calls, including the last one; if EOF is reached, the buffer was not touched
|
||||
/// 0 = success
|
||||
/// 1 = end of file
|
||||
/// 2 = other error
|
||||
FSReadFile = 6,
|
||||
/// seeks forward N sectors in the file.
|
||||
/// requires a previous call to FSOpenFile
|
||||
/// 0 = success
|
||||
/// 1 = failure (i.e. cannot seek due to EOF)
|
||||
FSSeek = 7,
|
||||
/// given a DDI file buffer, loads the file into memory and returns the required information to execute it
|
||||
/// after loading, you can free the file buffer without concern
|
||||
/// 0 = success
|
||||
/// 1 = failure
|
||||
LoadDDIFile = 8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskRequest {
|
||||
pub req: u16,
|
||||
pub replyto: u8,
|
||||
pub retval: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSOpenRequest {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *mut crate::fs::FileSystem,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSOpenDirRequest {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *const crate::fs::FileSystem,
|
||||
pub dir: *mut crate::fs::DirectoryReader,
|
||||
pub path: *const u8,
|
||||
pub pathlen: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSReadDirRequest {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *const crate::fs::FileSystem,
|
||||
pub dir: *mut crate::fs::DirectoryReader,
|
||||
pub record: *mut crate::fs::Record,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSOpenFileRequest {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *const crate::fs::FileSystem,
|
||||
pub record: *const crate::fs::Record,
|
||||
pub reader: *mut crate::fs::FileReader,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSReadFileRequest {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *const crate::fs::FileSystem,
|
||||
pub reader: *mut crate::fs::FileReader,
|
||||
pub buffer: *mut u8,
|
||||
pub bufferlen: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskFSSeek {
|
||||
pub base: KTaskRequest,
|
||||
pub fs: *const crate::fs::FileSystem,
|
||||
pub reader: *mut crate::fs::FileReader,
|
||||
pub sectors: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KTaskLoadDDIFile {
|
||||
pub base: KTaskRequest,
|
||||
pub file_buf_pointer: usize,
|
||||
pub file_buf_len: usize,
|
||||
pub task_setup_ptr: usize,
|
||||
}
|
||||
|
||||
pub fn request_type_from_req(req: u16) -> KTaskRequestType {
|
||||
match req {
|
||||
0 => KTaskRequestType::DoNothing,
|
||||
1 => KTaskRequestType::SayHi,
|
||||
2 => KTaskRequestType::FSOpen,
|
||||
3 => KTaskRequestType::FSOpenDir,
|
||||
4 => KTaskRequestType::FSReadDir,
|
||||
5 => KTaskRequestType::FSOpenFile,
|
||||
6 => KTaskRequestType::FSReadFile,
|
||||
7 => KTaskRequestType::FSSeek,
|
||||
8 => KTaskRequestType::LoadDDIFile,
|
||||
_ => KTaskRequestType::DoNothing,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn query_ktask(req: usize) {
|
||||
crate::syscalls::send_notification(0, req);
|
||||
crate::syscalls::wait_for_notif_ack(0);
|
||||
let tmp = crate::syscalls::wait_for_notification();
|
||||
if tmp != req {
|
||||
crate::syscalls::write_terminal(b"ktask failed to respond properly!\n");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ktask_sayhi() {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskRequest) };
|
||||
req.req = KTaskRequestType::SayHi as u16;
|
||||
req.replyto = ci;
|
||||
}
|
||||
query_ktask(req);
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
}
|
||||
|
||||
pub fn ktask_fsopen(fs: &mut crate::fs::FileSystem) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenRequest) };
|
||||
req.base.req = KTaskRequestType::FSOpen as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *mut _;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenRequest) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_fsopendir(
|
||||
fs: &crate::fs::FileSystem,
|
||||
dir: &mut crate::fs::DirectoryReader,
|
||||
path: &[u8],
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenDirRequest) };
|
||||
req.base.req = KTaskRequestType::FSOpenDir as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *const _;
|
||||
req.dir = dir as *mut _;
|
||||
req.path = path.as_ptr();
|
||||
req.pathlen = path.len() as u32;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenDirRequest) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_fsreaddir(
|
||||
fs: &crate::fs::FileSystem,
|
||||
dir: &mut crate::fs::DirectoryReader,
|
||||
record: &mut crate::fs::Record,
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadDirRequest) };
|
||||
req.base.req = KTaskRequestType::FSReadDir as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *const _;
|
||||
req.dir = dir as *mut _;
|
||||
req.record = record as *mut _;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadDirRequest) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_fsopenfile(
|
||||
fs: &crate::fs::FileSystem,
|
||||
record: &crate::fs::Record,
|
||||
reader: &mut crate::fs::FileReader,
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenFileRequest) };
|
||||
req.base.req = KTaskRequestType::FSOpenFile as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *const _;
|
||||
req.record = record as *const _;
|
||||
req.reader = reader as *mut _;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenFileRequest) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_fsreadfile(
|
||||
fs: &crate::fs::FileSystem,
|
||||
reader: &mut crate::fs::FileReader,
|
||||
buffer: &mut [u8],
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadFileRequest) };
|
||||
req.base.req = KTaskRequestType::FSReadFile as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *const _;
|
||||
req.reader = reader as *mut _;
|
||||
req.buffer = buffer.as_mut_ptr();
|
||||
req.bufferlen = buffer.len() as u32;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadFileRequest) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_fsseek(
|
||||
fs: &crate::fs::FileSystem,
|
||||
reader: &mut crate::fs::FileReader,
|
||||
sectors: u32,
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSSeek) };
|
||||
req.base.req = KTaskRequestType::FSSeek as u16;
|
||||
req.base.replyto = ci;
|
||||
req.fs = fs as *const _;
|
||||
req.reader = reader as *mut _;
|
||||
req.sectors = sectors;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSSeek) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
||||
|
||||
pub fn ktask_loadddifile(
|
||||
file_buf_pointer: usize,
|
||||
file_buf_len: usize,
|
||||
task_setup_ptr: usize,
|
||||
) -> usize {
|
||||
let ci = crate::syscalls::current_task();
|
||||
let req = crate::syscalls::alloc_blocks(1);
|
||||
{
|
||||
let req = unsafe { &mut *(req as *mut KTaskLoadDDIFile) };
|
||||
req.base.req = KTaskRequestType::LoadDDIFile as u16;
|
||||
req.base.replyto = ci;
|
||||
req.file_buf_pointer = file_buf_pointer;
|
||||
req.file_buf_len = file_buf_len;
|
||||
req.task_setup_ptr = task_setup_ptr;
|
||||
}
|
||||
query_ktask(req);
|
||||
let retval = {
|
||||
let req = unsafe { &mut *(req as *mut KTaskLoadDDIFile) };
|
||||
req.base.retval
|
||||
};
|
||||
crate::syscalls::free_blocks(req, 1);
|
||||
retval as usize
|
||||
}
|
14
liblbos/src/lib.rs
Normal file
14
liblbos/src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
#![no_std]
|
||||
|
||||
pub mod fs;
|
||||
pub mod ktask;
|
||||
pub mod syscalls;
|
||||
mod arch;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct TaskSetup {
|
||||
pub epc: usize,
|
||||
pub ddi_first_addr: usize,
|
||||
pub ddi_size: usize,
|
||||
pub wait_for_task_exit: bool,
|
||||
}
|
164
liblbos/src/syscalls.rs
Normal file
164
liblbos/src/syscalls.rs
Normal file
|
@ -0,0 +1,164 @@
|
|||
use crate::arch::{stall, syscall};
|
||||
use crate::TaskSetup;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KernelInfo {
|
||||
pub current_process_count: usize,
|
||||
pub total_mem_blocks: usize,
|
||||
pub free_mem_blocks: usize,
|
||||
}
|
||||
|
||||
#[repr(u16)]
|
||||
pub enum KTaskNotificationRequest {
|
||||
DoNothing = 0,
|
||||
SayHi = 1,
|
||||
}
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum SysCall {
|
||||
/// does absolutely nothing
|
||||
NoAction = 0,
|
||||
/// provide pointer to kernel info struct in first arg and it will be filled
|
||||
KernelInfo = 1,
|
||||
/// provide TaskSetup pointer in first arg and task will spawn, returns new task id
|
||||
CreateTask = 2,
|
||||
/// the currently executing task will exit
|
||||
ExitTask = 3,
|
||||
/// returns the current task id
|
||||
CurrentTask = 4,
|
||||
/// takes all available characters in the INBUF, places them in the given buffer, and returns the number of characters placed in the buffer
|
||||
ReadInbuf = 5,
|
||||
/// writes the given N characters in the buffer to the serial port
|
||||
WriteTerminal = 6,
|
||||
/// allocates N blocks of contiguous memory and returns the first block
|
||||
AllocBlocks = 7,
|
||||
/// frees N blocks starting at the given block
|
||||
FreeBlocks = 8,
|
||||
/// reads N sectors from the hard block device and writes them to the given block, this syscall will block the current task
|
||||
/// returns 0 on success and 1 on failure
|
||||
ReadHBD = 9,
|
||||
/// sends a notification to the given task.
|
||||
/// (taskid, addr) -> ret
|
||||
/// returns 0 on success and 1 on failure (i.e. taskid is invalid)
|
||||
/// addr is one block of memory.
|
||||
/// calling this syscall will NOT block the current process, you must call WaitForNotification to
|
||||
/// receive your reply.
|
||||
/// the point at which you are allowed to free the block is dependent on the task that is waiting for the notification,
|
||||
/// however in general it is not safe to free the block until a WaitForNotifAck is called.
|
||||
/// IMPORTANT NOTE:
|
||||
/// sending multiple notifications to a single task without calling WaitForNotifAck is NOT SUPPORTED
|
||||
/// doing this results in undefined behavior
|
||||
SendNotification = 10,
|
||||
/// waits for a notification to arrive
|
||||
/// () -> addr
|
||||
/// this will block indefinitely until a notification arrives,^
|
||||
/// use PendingNotifications to check if there are any pending notifications if you want to avoid blocking.
|
||||
/// return value is an address to one block of memory.
|
||||
/// this return value should also be used as a method of identified associated notifications,
|
||||
/// i.e. in a system like the following
|
||||
/// TASK1 - - - TASK2 - - - TASK1
|
||||
/// send recv,send recv
|
||||
/// the same block should be used for all notifications, and overwritten repeatedly
|
||||
WaitForNotification = 11,
|
||||
/// returns number of pending notifications
|
||||
PendingNotifications = 12,
|
||||
/// waits for a sent notification to be received
|
||||
/// (taskid) -> 0
|
||||
WaitForNotifAck = 13,
|
||||
|
||||
/// initializes kernel, ONLY CALL ONCE! IN FACT, YOU PROBABLY NEVER NEED TO CALL THIS
|
||||
InitKernel = 666
|
||||
}
|
||||
|
||||
pub fn sc2usize(sc: SysCall) -> usize {
|
||||
sc as usize
|
||||
}
|
||||
|
||||
pub fn usize2sc(u: usize) -> SysCall {
|
||||
match u {
|
||||
0 => SysCall::NoAction,
|
||||
1 => SysCall::KernelInfo,
|
||||
2 => SysCall::CreateTask,
|
||||
3 => SysCall::ExitTask,
|
||||
4 => SysCall::CurrentTask,
|
||||
5 => SysCall::ReadInbuf,
|
||||
6 => SysCall::WriteTerminal,
|
||||
7 => SysCall::AllocBlocks,
|
||||
8 => SysCall::FreeBlocks,
|
||||
9 => SysCall::ReadHBD,
|
||||
10 => SysCall::SendNotification,
|
||||
11 => SysCall::WaitForNotification,
|
||||
12 => SysCall::PendingNotifications,
|
||||
13 => SysCall::WaitForNotifAck,
|
||||
666 => SysCall::InitKernel,
|
||||
_ => SysCall::NoAction,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn kinfo() -> KernelInfo {
|
||||
let mut kinfo = KernelInfo {
|
||||
current_process_count: 0,
|
||||
total_mem_blocks: 0,
|
||||
free_mem_blocks: 0,
|
||||
};
|
||||
|
||||
syscall(SysCall::KernelInfo, &mut kinfo as *mut KernelInfo as usize, 0, 0, 0, 0, 0);
|
||||
|
||||
kinfo
|
||||
}
|
||||
|
||||
pub fn create_task(ts: TaskSetup) -> u8 {
|
||||
syscall(SysCall::CreateTask, &ts as *const TaskSetup as usize, 0, 0, 0, 0, 0) as u8
|
||||
}
|
||||
|
||||
pub fn exit() -> ! {
|
||||
syscall(SysCall::ExitTask, 0, 0, 0, 0, 0, 0);
|
||||
|
||||
loop {
|
||||
stall();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn current_task() -> u8 {
|
||||
syscall(SysCall::CurrentTask, 0, 0, 0, 0, 0, 0) as u8
|
||||
}
|
||||
|
||||
pub fn read_inbuf(buf: &mut [u8]) -> usize {
|
||||
syscall(SysCall::ReadInbuf, buf.as_mut_ptr() as usize, buf.len(), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn alloc_blocks(n: usize) -> usize {
|
||||
syscall(SysCall::AllocBlocks, n, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn free_blocks(addr: usize, n: usize) {
|
||||
syscall(SysCall::FreeBlocks, addr, n, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
pub fn write_terminal(buf: &[u8]) {
|
||||
syscall(SysCall::WriteTerminal, buf.as_ptr() as usize, buf.len(), 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
pub fn read_hbd(sector: usize, buf_addr: usize, count: usize) -> usize {
|
||||
syscall(SysCall::ReadHBD, sector, buf_addr, count, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn send_notification(taskid: u8, addr: usize) {
|
||||
syscall(SysCall::SendNotification, taskid as usize, addr, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
pub fn wait_for_notification() -> usize {
|
||||
syscall(SysCall::WaitForNotification, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn pending_notifications() -> usize {
|
||||
syscall(SysCall::PendingNotifications, 0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
pub fn wait_for_notif_ack(taskid: u8) {
|
||||
syscall(SysCall::WaitForNotifAck, taskid as usize, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
pub fn init_kernel() {
|
||||
syscall(SysCall::InitKernel, 0, 0, 0, 0, 0, 0);
|
||||
}
|
15
load_ppc.sh
Executable file
15
load_ppc.sh
Executable file
|
@ -0,0 +1,15 @@
|
|||
#!/bin/bash
|
||||
|
||||
storage_device="${1}"
|
||||
|
||||
umount "${storage_device}"
|
||||
echo "MOUNTING..."
|
||||
mkdir -p /mnt/lbos-mnt
|
||||
mount "${storage_device}" /mnt/lbos-mnt
|
||||
|
||||
echo "COPYING..."
|
||||
cp -v target/powerpc-unknown-linux-gnu/release/lbos /mnt/lbos-mnt
|
||||
|
||||
echo "UNMOUNTING..."
|
||||
umount "${storage_device}"
|
||||
echo "COMPLETE"
|
2
makeddi/.gitignore
vendored
Normal file
2
makeddi/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.idea
|
21
makeddi/Cargo.lock
generated
Normal file
21
makeddi/Cargo.lock
generated
Normal file
|
@ -0,0 +1,21 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ddi"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "elf"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55dd888a213fc57e957abf2aa305ee3e8a28dbe05687a251f33b637cd46b0070"
|
||||
|
||||
[[package]]
|
||||
name = "makeddi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ddi",
|
||||
"elf",
|
||||
]
|
8
makeddi/Cargo.toml
Normal file
8
makeddi/Cargo.toml
Normal file
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "makeddi"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
elf = "0.8.0"
|
||||
ddi = { path = "../ddi" }
|
179
makeddi/src/main.rs
Normal file
179
makeddi/src/main.rs
Normal file
|
@ -0,0 +1,179 @@
|
|||
use std::collections::BTreeMap;
|
||||
use elf::{abi, ElfBytes};
|
||||
use elf::endian::LittleEndian;
|
||||
use ddi::{write_ddi_header, write_ddi_lbos_header, write_ddi_relocation_header, write_ddi_segment_header, DDIHeader, DDILBOSHeader, DDIRelocationHeader, DDISegmentHeader, DDIType};
|
||||
|
||||
fn main() {
|
||||
let args = std::env::args().collect::<Vec<_>>();
|
||||
let input = args.get(1).expect("input file required as first argument");
|
||||
let output = args
|
||||
.get(2)
|
||||
.expect("output file required as second argument");
|
||||
|
||||
let raw = std::fs::read(input).expect("failed to read input file");
|
||||
let elf = ElfBytes::<LittleEndian>::minimal_parse(&raw).expect(
|
||||
"failed to parse input file as ELF",
|
||||
);
|
||||
|
||||
let mut obuf = vec![0u8; size_of::<DDIHeader>()];
|
||||
let header = DDIHeader {
|
||||
magic: *b"ddI\0",
|
||||
version: 1,
|
||||
ddi_type: DDIType::LBOSRiscV as u8,
|
||||
};
|
||||
write_ddi_header(&mut obuf, &header);
|
||||
let mut header = DDILBOSHeader {
|
||||
entrypoint: 0,
|
||||
segment_header_pointer: 0,
|
||||
segment_header_count: 0,
|
||||
relocation_header_pointer: 0,
|
||||
relocation_header_count: 0,
|
||||
entrypoint_segment: 0,
|
||||
};
|
||||
|
||||
let dataoff = (obuf.len() + size_of::<DDILBOSHeader>()) as u32;
|
||||
|
||||
let mut usegs = vec![];
|
||||
let mut section_to_segment = BTreeMap::new();
|
||||
let mut segment_to_base = BTreeMap::new();
|
||||
let mut data = vec![];
|
||||
for eseg in elf.segments().expect("failed to get ELF segments") {
|
||||
const PT_LOAD: u32 = 0x00000001;
|
||||
if eseg.p_type == PT_LOAD {
|
||||
let seg = DDISegmentHeader {
|
||||
segment_pointer: data.len() as u32 + dataoff,
|
||||
segment_size: eseg.p_memsz as u32,
|
||||
};
|
||||
println!("segment {} needs align {}, handle in the future", usegs.len(), eseg.p_align);
|
||||
if elf.ehdr.e_entry as usize >= eseg.p_vaddr as usize && elf.ehdr.e_entry <= eseg.p_vaddr + eseg.p_memsz {
|
||||
// entry point is in this segment
|
||||
let ep = elf.ehdr.e_entry as usize;
|
||||
header.entrypoint = ep as u32;
|
||||
header.entrypoint_segment = usegs.len() as u32;
|
||||
}
|
||||
let d = &raw[eseg.p_offset as usize..(eseg.p_offset + eseg.p_filesz) as usize];
|
||||
let mut d = d.to_vec();
|
||||
if eseg.p_memsz as u32 > d.len() as u32 {
|
||||
d.resize(eseg.p_memsz as usize, 0);
|
||||
}
|
||||
data.extend_from_slice(&d);
|
||||
header.segment_header_count += 1;
|
||||
let section_headers = elf.section_headers().expect("failed to load section headers");
|
||||
let mut found = false;
|
||||
for (i, section) in section_headers.iter().enumerate() {
|
||||
if eseg.p_vaddr <= section.sh_addr && eseg.p_vaddr + eseg.p_memsz >= section.sh_addr + section.sh_size {
|
||||
println!("found section {} for segment {}", section.sh_name, usegs.len());
|
||||
section_to_segment.insert(section.sh_name, usegs.len() as u32);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
eprintln!("no section found for segment {}", usegs.len());
|
||||
}
|
||||
segment_to_base.insert(usegs.len(), eseg.p_vaddr);
|
||||
|
||||
usegs.push(seg);
|
||||
}
|
||||
}
|
||||
println!("found {} segments", usegs.len());
|
||||
println!("section mapper: {:?}", section_to_segment);
|
||||
|
||||
header.segment_header_pointer = data.len() as u32 + dataoff;
|
||||
obuf.extend_from_slice(&[0u8; size_of::<DDILBOSHeader>()]);
|
||||
write_ddi_lbos_header(&mut obuf, &header);
|
||||
obuf.extend_from_slice(&data);
|
||||
for (i, useg) in usegs.iter().enumerate() {
|
||||
obuf.extend_from_slice(&[0u8; size_of::<DDISegmentHeader>()]);
|
||||
write_ddi_segment_header(&mut obuf, &header, i, useg);
|
||||
}
|
||||
|
||||
// relocations
|
||||
let offset = obuf.len() as u32;
|
||||
let mut relocations = vec![];
|
||||
for section_header in elf.section_headers().expect("failed to get ELF section headers") {
|
||||
match section_header.sh_type {
|
||||
abi::SHT_REL => {
|
||||
let rels = elf.section_data_as_rels(§ion_header).expect("failed to get ELF section data");
|
||||
for (i, rel) in rels.enumerate() {
|
||||
let mut segment_containing_instruction = None;
|
||||
for (j, seg) in usegs.iter().enumerate() {
|
||||
let base = segment_to_base.get(&j).expect("failed to get segment base");
|
||||
if section_header.sh_addr <= *base && section_header.sh_addr + section_header.sh_size >= (*base + seg.segment_size as u64) {
|
||||
segment_containing_instruction = Some(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(segment_containing_instruction) = segment_containing_instruction {
|
||||
let symtab = elf.symbol_table().expect("failed to get symbol table");
|
||||
let symbol = symtab.as_ref().expect("failed to get symbol table entry");
|
||||
let section = elf.section_headers().unwrap().get(symbol.0.get(rel.r_sym as _).expect("bad rel symbol").st_shndx as _).expect("failed to get section");
|
||||
if let Some(target_segment) = section_to_segment.get(§ion.sh_name) {
|
||||
let offset = symbol.0.get(rel.r_sym as _).expect("bad rel symbol").st_value as u32;
|
||||
let ddirel = DDIRelocationHeader {
|
||||
relocation_segment: segment_containing_instruction as u32,
|
||||
relocation_pointer: rel.r_offset as u32,
|
||||
target_segment: *target_segment as u32,
|
||||
target_pointer: offset as u16,
|
||||
relocation_type: rel.r_type as u16,
|
||||
};
|
||||
relocations.push(ddirel);
|
||||
} else {
|
||||
println!("not including relocation {i} in section {} due to missing section {}", section_header.sh_name, section.sh_name);
|
||||
}
|
||||
} else {
|
||||
eprintln!("failed to find segment for relocation {i} in section {}", section_header.sh_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
abi::SHT_RELA => {
|
||||
let rels = elf.section_data_as_relas(§ion_header).expect("failed to get ELF section data");
|
||||
for (i, rel) in rels.enumerate() {
|
||||
let mut segment_containing_instruction = None;
|
||||
for (j, seg) in usegs.iter().enumerate() {
|
||||
let base = segment_to_base.get(&j).expect("failed to get segment base");
|
||||
if *base <= section_header.sh_addr && (*base + seg.segment_size as u64) >= (section_header.sh_addr + section_header.sh_size) {
|
||||
segment_containing_instruction = Some(j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(segment_containing_instruction) = segment_containing_instruction {
|
||||
let symtab = elf.symbol_table().expect("failed to get symbol table");
|
||||
let symbol = symtab.as_ref().expect("failed to get symbol table entry");
|
||||
let section = elf.section_headers().unwrap().get(symbol.0.get(rel.r_sym as _).expect("bad rel symbol").st_shndx as _).expect("failed to get section");
|
||||
if let Some(target_segment) = section_to_segment.get(§ion.sh_name) {
|
||||
let offset = symbol.0.get(rel.r_sym as _).expect("bad rel symbol").st_value as u32;
|
||||
println!("addend: {}", rel.r_addend);
|
||||
let ddirel = DDIRelocationHeader {
|
||||
relocation_segment: segment_containing_instruction as u32,
|
||||
relocation_pointer: rel.r_offset as u32,
|
||||
target_segment: *target_segment as u32,
|
||||
target_pointer: offset as u16,
|
||||
relocation_type: rel.r_type as u16,
|
||||
};
|
||||
relocations.push(ddirel);
|
||||
} else {
|
||||
println!("not including relocation {i} in section {} due to missing section {}", section_header.sh_name, section.sh_name);
|
||||
}
|
||||
} else {
|
||||
eprintln!("failed to find segment for relocation {i} in section {}", section_header.sh_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
header.relocation_header_pointer = offset;
|
||||
header.relocation_header_count = relocations.len() as u32;
|
||||
write_ddi_lbos_header(&mut obuf, &header);
|
||||
for (i, rel) in relocations.iter().enumerate() {
|
||||
obuf.extend_from_slice(&[0u8; size_of::<DDIRelocationHeader>()]);
|
||||
write_ddi_relocation_header(&mut obuf, &header, i, rel);
|
||||
}
|
||||
|
||||
let len = obuf.len();
|
||||
std::fs::write(output, obuf).expect("failed to write output file");
|
||||
|
||||
println!("wrote {} bytes to {}", len, output);
|
||||
}
|
2
qemurun.sh
Executable file
2
qemurun.sh
Executable file
|
@ -0,0 +1,2 @@
|
|||
#!/usr/bin/env bash
|
||||
qemu-system-riscv32 -machine virt -bios none -display gtk -drive if=none,format=raw,file=/dev/loop0,id=disk1 -device virtio-blk-device,drive=disk1 -serial mon:stdio -d guest_errors,unimp -m 5M -kernel target/riscv32imac-unknown-none-elf/release/lbos
|
8
qemurun_ppc.sh
Executable file
8
qemurun_ppc.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# todo: this is incredibly system specific, change it
|
||||
sudo losetup -fP test_ppc.img
|
||||
sudo ./load_ppc.sh /dev/loop0p2
|
||||
sudo losetup -d /dev/loop0
|
||||
|
||||
qemu-system-ppc -machine mac99,via=pmu -L pc-bios -display sdl -prom-env "auto-boot?=true" -prom-env "boot-device=hd:,lbos" -drive if=ide,format=raw,file=test_ppc.img,index=1 -m 256
|
36
src/arch/mod.rs
Normal file
36
src/arch/mod.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use crate::syscalls::{sc2usize, SysCall};
|
||||
use crate::uart::{Serial};
|
||||
|
||||
#[cfg(feature = "arch_virt")]
|
||||
pub mod virt;
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
pub mod ppc32;
|
||||
#[cfg(any(feature = "arch_ofw", feature = "arch_ppc32"))]
|
||||
pub mod ofw;
|
||||
|
||||
pub fn stall() {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
virt::stall();
|
||||
}
|
||||
|
||||
pub const fn serial_port() -> Option<&'static dyn Serial> {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
virt::serial_port()
|
||||
}
|
||||
#[cfg(feature = "arch_ofw")]
|
||||
{
|
||||
Some(&ofw::ofw_framebuffer::terminal::TerminalLayer)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn syscall(sc: SysCall, a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize) -> usize {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
virt::virt_syscall(sc2usize(sc) as _, a1 as _, a2 as _, a3 as _, a4 as _, a5 as _, a6 as _) as _
|
||||
}
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
{
|
||||
unsafe { ppc32::_ppc32_syscall(sc2usize(sc), a1, a2, a3, a4, a5, a6) }
|
||||
}
|
||||
}
|
50
src/arch/ofw/cell_conv.rs
Normal file
50
src/arch/ofw/cell_conv.rs
Normal file
|
@ -0,0 +1,50 @@
|
|||
/// A cell is a single FORTH stack value, all arguments and return values given to OpenFirmware
|
||||
/// must fit in a cell.
|
||||
pub type Cell = i32;
|
||||
|
||||
/// # DoubleWord
|
||||
/// a structure containing a low and high cell
|
||||
/// # example
|
||||
/// ```
|
||||
/// use ofw::cell_conv::DoubleWord;
|
||||
/// let value: u64 = 0xabcdef0123456789;
|
||||
/// let dw: DoubleWord = value.into();
|
||||
/// assert_eq!(dw.high as u32, 0xabcdef01);
|
||||
/// assert_eq!(dw.low as u32, 0x23456789);
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct DoubleWord {
|
||||
pub low: Cell,
|
||||
pub high: Cell,
|
||||
}
|
||||
|
||||
impl From<i64> for DoubleWord {
|
||||
fn from(dw: i64) -> Self {
|
||||
Self {
|
||||
low: dw as Cell,
|
||||
high: (dw >> 32) as Cell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u64> for DoubleWord {
|
||||
fn from(dw: u64) -> Self {
|
||||
Self {
|
||||
low: dw as Cell,
|
||||
high: (dw >> 32) as Cell,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DoubleWord> for i64 {
|
||||
fn from(dw: DoubleWord) -> Self {
|
||||
(dw.high as i64) << 32 | dw.low as i64
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DoubleWord> for u64 {
|
||||
fn from(dw: DoubleWord) -> Self {
|
||||
(dw.high as u64) << 32 | dw.low as u64
|
||||
}
|
||||
}
|
89
src/arch/ofw/deviceio.rs
Normal file
89
src/arch/ofw/deviceio.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
#![no_std]
|
||||
|
||||
use super::EntryFunction;
|
||||
use super::{IHandle, ntstr};
|
||||
use super::cell_conv::DoubleWord;
|
||||
use super::ntstr::NTSTR;
|
||||
use crate::call;
|
||||
|
||||
/// # io_open
|
||||
/// from ieee 1275, 6.3.2.3 Device I/O
|
||||
/// IN: device-specifier: NTSTR
|
||||
/// OUT: ihandle
|
||||
/// opens the package named by device-specifier as with open-dev, returning the instance identifier
|
||||
/// ihandle. ihandle is zero if the operation fails.
|
||||
/// the same package can be opened more than once if the particular package permits it, in
|
||||
/// which case a distinct ihandle will be returned each time.
|
||||
pub fn io_open(entry_fn: EntryFunction, device_specifier: NTSTR) -> Option<IHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("open"), 1, 1, device_specifier);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as IHandle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # io_close
|
||||
/// from ieee 1275, 6.3.2.3 Device I/O
|
||||
/// IN: ihandle
|
||||
/// OUT: none
|
||||
/// closes the instance identifed by ihandle as with close-dev; subsequent use of that ihandle
|
||||
/// is invalid. a client program should close instances it has opened after the instances are
|
||||
/// no longer needed, in order to release resources and to deactivate any associated devices.
|
||||
pub fn io_close(entry_fn: EntryFunction, ihandle: IHandle) -> bool {
|
||||
let (success, _) = call!(entry_fn, ntstr!("close"), 1, 0, ihandle);
|
||||
|
||||
success
|
||||
}
|
||||
|
||||
/// # io_read
|
||||
/// from ieee 1275, 6.3.2.3 Device I/O
|
||||
/// IN: ihandle, addr: &mut [u8], len
|
||||
/// OUT: actual
|
||||
/// executes the read method in the instance ihandle with arguments addr and len.
|
||||
/// actual is either the value returned by that read method or –1 if that instance
|
||||
/// does not have a read method.
|
||||
pub fn io_read(entry_fn: EntryFunction, ihandle: IHandle, addr: *const u8, len: usize) -> Option<i32> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("read"), 3, 1, ihandle, addr as i32, len as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # io_write
|
||||
/// from ieee 1275, 6.3.2.3 Device I/O
|
||||
/// IN: ihandle, addr: &mut [u8], len
|
||||
/// OUT: actual
|
||||
/// executes the write method in the instance ihandle with arguments addr and len.
|
||||
/// actual is either the value returned by that write method or –1 if that instance
|
||||
/// does not have a write method.
|
||||
pub fn io_write(entry_fn: EntryFunction, ihandle: IHandle, addr: *const u8, len: usize) -> Option<i32> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("write"), 3, 1, ihandle, addr as i32, len as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # io_seek
|
||||
/// from ieee 1275, 6.3.2.3 Device I/O
|
||||
/// IN: ihandle, pos: DoubleWord
|
||||
/// OUT: status
|
||||
/// executes the seek method in the instance ihandle with arguments pos.hi and pos.lo.
|
||||
/// status is either the value returned by that seek method or –1 if that instance
|
||||
/// does not have a seek method.
|
||||
pub fn io_seek(entry_fn: EntryFunction, ihandle: IHandle, pos: DoubleWord) -> Option<i32> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("seek"), 3, 1, ihandle, pos.high, pos.low);
|
||||
|
||||
if success {
|
||||
Some(returns[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
236
src/arch/ofw/devicetree.rs
Normal file
236
src/arch/ofw/devicetree.rs
Normal file
|
@ -0,0 +1,236 @@
|
|||
#![no_std]
|
||||
|
||||
use super::EntryFunction;
|
||||
use super::{PHandle, ntstr, IHandle};
|
||||
use super::ntstr::NTSTR;
|
||||
use crate::call;
|
||||
|
||||
/// # dt_peer
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle
|
||||
/// OUT: sibling-phandle
|
||||
/// sibling-phandle is either the identifier of the device node that is the next sibling of the
|
||||
/// device node identified by phandle, or 0 if there are no more siblings.
|
||||
/// if phandle is 0, sibling-phandle is the identifier of the root node.
|
||||
pub fn dt_peer(entry_fn: EntryFunction, phandle: PHandle) -> Option<PHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("peer"), 1, 1, phandle);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as PHandle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_child
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle
|
||||
/// OUT: child-phandle
|
||||
/// child-phandle is either the identifier of the device node that is the first child of the
|
||||
/// device node identified by phandle or zero if there are no children.
|
||||
pub fn dt_child(entry_fn: EntryFunction, phandle: PHandle) -> Option<PHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("child"), 1, 1, phandle);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as PHandle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_parent
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle
|
||||
/// OUT: parent-phandle
|
||||
/// parent-phandle is either the node identifier of the device node that is the parent of the
|
||||
/// device node identified by phandle or zero if phandle is the identifier of the root node.
|
||||
pub fn dt_parent(entry_fn: EntryFunction, phandle: PHandle) -> Option<PHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("parent"), 1, 1, phandle);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as PHandle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_instance_to_package
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: ihandle
|
||||
/// OUT: phandle
|
||||
/// phandle is either the identifier corresponding to the instance identifier ihandle or –1 if there
|
||||
/// is no instance identifier ihandle.
|
||||
/// if phandle is –1, OpenFirmware was unable to translate ihandle. OpenFirmware may, but is not
|
||||
/// required to, check the validity of an ihandle.
|
||||
pub fn dt_instance_to_package(entry_fn: EntryFunction, ihandle: IHandle) -> Option<PHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("instance-to-package"), 1, 1, ihandle);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as PHandle)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_getproplen
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle, name: NTSTR
|
||||
/// OUT: proplen
|
||||
/// proplen is either the length of the value associated with the property name in the device node
|
||||
/// identified by phandle, zero if the property name exists but has no corresponding value, or –1
|
||||
/// if the property name does not exist.
|
||||
pub fn dt_getproplen(entry_fn: EntryFunction, phandle: PHandle, name: NTSTR) -> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("getproplen"), 2, 1, phandle, name);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_getprop
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle, name: NTSTR, buf: &mut [u8], buflen
|
||||
/// OUT: size
|
||||
/// copies a maximum of buflen bytes of the value of the property name in the device node
|
||||
/// identified by phandle into the memory pointed to by buf. size is either the actual size of
|
||||
/// the property, or –1 if name does not exist.
|
||||
pub fn dt_getprop<T>(
|
||||
entry_fn: EntryFunction, phandle: PHandle, name: NTSTR, buf: *mut T, buflen: usize
|
||||
)-> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("getprop"), 4, 1, phandle, name, buf as i32, buflen as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_nextprop
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle, previous: NTSTR, buf: &mut [u8; 32]
|
||||
/// OUT: flag
|
||||
/// copies the name of the property following previous in the property list of the device node
|
||||
/// identified by phandle into buf, as a null-terminated string. buf is the address of a 32-byte
|
||||
/// region of memory. if previous is zero or a pointer to a null string, copies the name of the
|
||||
/// device node’s first property. if there are no more properties after previous or if previous
|
||||
/// is invalid (i.e., names a property which does not exist in that device node), copies a
|
||||
/// null string. the return value flag is –1 if previous is invalid, zero if there are no
|
||||
/// more properties after previous, or 1 otherwise.
|
||||
pub fn dt_nextprop(
|
||||
entry_fn: EntryFunction, phandle: PHandle, previous: NTSTR, buf: &mut [u8; 32]
|
||||
)-> Option<i32> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("nextprop"), 3, 1, phandle, previous, buf.as_ptr() as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_setprop
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle, name: NTSTR, buf: &mut [u8], len
|
||||
/// OUT: size
|
||||
/// sets the property value of the property name in the device node identified by phandle to the
|
||||
/// value beginning at memory address buf and continuing for len bytes, attempting to create the
|
||||
/// property if it does not exist. size is the actual length of the new value, or –1 if the
|
||||
/// property value could not be set or could not be created.
|
||||
/// ## NOTE
|
||||
/// there may be a length limitation on the property values of the “/options” node,
|
||||
/// which are stored as fields in nonvolatile RAM. in such cases, the property value could be
|
||||
/// truncated to fit the available space.
|
||||
pub fn dt_setprop(
|
||||
entry_fn: EntryFunction, phandle: PHandle, name: NTSTR, buf: *const u8, len: usize
|
||||
)-> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("setprop"), 4, 1, phandle, name, buf as i32, len as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_canon
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: device-specifier: NTSTR, buf: &mut [u8], buflen
|
||||
/// OUT: length
|
||||
/// this service converts the possibly ambiguous device-specifier to a fully qualified pathname,
|
||||
/// storing, at most, buflen bytes as a null-terminated string in the memory buffer starting at the
|
||||
/// address buf. if the length of the null-terminated pathname is greater than buflen, the
|
||||
/// trailing characters and the null terminator are not stored. length is the length of the fully
|
||||
/// qualified pathname excluding any null terminator, or –1 if the pathname is invalid.
|
||||
pub fn dt_canon(
|
||||
entry_fn: EntryFunction, device_specifier: NTSTR, buf: *const u8, buflen: usize
|
||||
)-> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("canon"), 3, 1, device_specifier, buf as i32, buflen as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_finddevice
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: device-specifier: NTSTR
|
||||
/// OUT: phandle
|
||||
/// phandle is the identifier of the device node selected by device-specifier, as with
|
||||
/// find-device, or –1 if device-specifier cannot be matched. in either case, the active package
|
||||
/// is unaffected.
|
||||
pub fn dt_finddevice(entry_fn: EntryFunction, device_specifier: NTSTR) -> Option<PHandle> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("finddevice"), 1, 1, device_specifier);
|
||||
|
||||
if success {
|
||||
Some(returns[0])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_instance_to_path
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: ihandle, buf: &mut [u8], buflen
|
||||
/// OUT: length
|
||||
/// this service returns the fully qualified pathname corresponding to the identifier ihandle,
|
||||
/// storing, at most, buflen bytes as a null-terminated string in the memory buffer starting at
|
||||
/// the address buf. if the length of the null-terminated pathname is greater than buflen, the
|
||||
/// trailing characters and the null terminator are not stored. length is the length of the
|
||||
/// fully qualified pathname excluding any null terminator, or –1 if ihandle is invalid.
|
||||
pub fn dt_instance_to_path(
|
||||
entry_fn: EntryFunction, ihandle: IHandle, buf: *const u8, buflen: usize
|
||||
)-> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("instance-to-path"), 3, 1, ihandle, buf as i32, buflen as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # dt_package_to_path
|
||||
/// from ieee 1275, 6.3.2.2 Device tree
|
||||
/// IN: phandle, buf: &mut [u8], buflen
|
||||
/// OUT: length
|
||||
/// returns the fully qualified pathname corresponding to the node identifier phandle, storing,
|
||||
/// at most, buflen bytes as a null-terminated string in the memory buffer starting at the address
|
||||
/// buf. if the length of the null-terminated pathname is greater than buflen, the trailing characters
|
||||
/// and the null terminator are not stored. length is the length of the fully qualified pathname
|
||||
/// excluding any null terminator, or –1 if phandle is invalid.
|
||||
pub fn dt_package_to_path(
|
||||
entry_fn: EntryFunction, phandle: PHandle, buf: *const u8, buflen: usize
|
||||
)-> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("package-to-path"), 3, 1, phandle, buf as i32, buflen as i32);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
40
src/arch/ofw/memory.rs
Normal file
40
src/arch/ofw/memory.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
#![no_std]
|
||||
|
||||
use super::EntryFunction;
|
||||
use super::{ntstr};
|
||||
use crate::call;
|
||||
|
||||
/// # m_claim
|
||||
/// from ieee 1275, 6.3.2.4 Memory
|
||||
/// IN: virt: usize, size, align
|
||||
/// OUT: baseaddr: usize
|
||||
/// allocates size bytes of memory. if align is zero, the allocated range begins at the
|
||||
/// virtual address virt. otherwise, an aligned address is automatically chosen and the
|
||||
/// input argument virt is ignored. the alignment boundary is the smallest power of two
|
||||
/// greater than or equal to the value of align; an align value of 1 signifies 1-byte alignment.
|
||||
/// base is the beginning address of the allocated memory (equal to virt if align was 0)
|
||||
/// or –1 if the operation fails (for example, if the requested virtual address is unavailable).
|
||||
/// the range of physical memory and virtual addresses affected by this operation will be
|
||||
/// unavailable for subsequent mapping or allocation operations until freed by release.
|
||||
pub fn m_claim(entry_fn: EntryFunction, virt: usize, size: i32, align: i32) -> Option<usize> {
|
||||
let (success, returns) = call!(entry_fn, ntstr!("claim"), 3, 1, virt as i32, size, align);
|
||||
|
||||
if success {
|
||||
Some(returns[0] as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # m_release
|
||||
/// from ieee 1275, 6.3.2.4 Memory
|
||||
/// IN: virt: usize, size
|
||||
/// OUT: none
|
||||
/// frees size bytes of physical memory starting at virtual address virt, making that physical
|
||||
/// memory and the corresponding range of virtual address space available for later use.
|
||||
/// that memory must have been previously allocated by claim.
|
||||
pub fn m_release(entry_fn: EntryFunction, virt: usize, size: i32) -> bool {
|
||||
let (success, _) = call!(entry_fn, ntstr!("release"), 2, 0, virt as i32, size);
|
||||
|
||||
success
|
||||
}
|
242
src/arch/ofw/mod.rs
Normal file
242
src/arch/ofw/mod.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
/// # OFW_TRUE
|
||||
/// a constant representing the OpenFirmware "true" value
|
||||
pub const OFW_TRUE: i32 = -1;
|
||||
|
||||
/// # OFW_FALSE
|
||||
/// a constant representing the OpenFirmware "false" value
|
||||
pub const OFW_FALSE: i32 = 0;
|
||||
|
||||
/// # OFWBoolean
|
||||
/// a type representing the OpenFirmware boolean type
|
||||
/// can be either OFW_TRUE or OFW_FALSE
|
||||
pub type OFWBoolean = i32;
|
||||
|
||||
/// # PHandle
|
||||
/// handle to a "package" in OpenFirmware
|
||||
/// usually used to access properties
|
||||
pub type PHandle = i32;
|
||||
|
||||
/// # IHandle
|
||||
/// handle to a package instance in OpenFirmware
|
||||
/// usually used to call methods from a package
|
||||
pub type IHandle = i32;
|
||||
|
||||
use ntstr::NTSTR;
|
||||
use crate::ntstr;
|
||||
|
||||
pub type EntryFunction = extern "C" fn (*mut Args) -> i32;
|
||||
|
||||
/// # ntstr
|
||||
/// module for handling null-terminated strings
|
||||
pub mod ntstr;
|
||||
|
||||
/// # cell_conv
|
||||
/// module for converting between OpenFirmware cells and Rust integers
|
||||
pub mod cell_conv;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
pub mod runtime;
|
||||
pub mod deviceio;
|
||||
pub mod devicetree;
|
||||
pub mod memory;
|
||||
pub mod ofw_framebuffer;
|
||||
pub mod keyboard;
|
||||
|
||||
/// # Args
|
||||
/// a base structure for all calls to the OpenFirmware entry function,
|
||||
/// shouldn't be used directly but rather used as a base for other "service" structures
|
||||
/// an example "service struct" is as follows
|
||||
/// ```
|
||||
/// use ofw::{Args, OFWBoolean};
|
||||
/// use ofw::ntstr::NTSTR;
|
||||
/// /// # test
|
||||
/// /// nargs: 1
|
||||
/// /// nrets: 1
|
||||
/// /// takes a service name string, and returns OFW_TRUE if the service is missing or OFW_FALSE if it is present
|
||||
/// #[repr(C)]
|
||||
/// pub struct TestService {
|
||||
/// pub args: Args,
|
||||
/// pub service: NTSTR,
|
||||
/// pub missing: OFWBoolean,
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(C)]
|
||||
pub struct Args {
|
||||
pub service: NTSTR,
|
||||
// ieee 1275, 2.4.1 General conventions: "All numbers on the stack are signed integers, _32 bits[...]"
|
||||
pub nargs: i32,
|
||||
pub nrets: i32,
|
||||
// inheriting structs will continue from here
|
||||
}
|
||||
|
||||
/// # call!
|
||||
/// a macro for calling OpenFirmware services
|
||||
/// ## example
|
||||
/// ```
|
||||
/// use ofw::{call, service_result, ServiceResult};
|
||||
/// use ofw::arch::EntryFunction;
|
||||
/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
|
||||
/// // <entry function>, <service name>, <# of args>, <# of rets>, <args...>
|
||||
/// let (success, returns): (bool, [i32; 1]) = call!(entry_fn, ntstr!("test"), 1, 1, ntstr!("test"));
|
||||
/// if success {
|
||||
/// let test_result: i32 = returns[0];
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! call {
|
||||
($entry_fn:expr, $service:expr, $nargs:expr, $nrets:expr, $($args:expr),*) => {
|
||||
{
|
||||
let mut args_and_rets: [i32; $nargs as usize + $nrets as usize] = [0; $nargs as usize + $nrets as usize];
|
||||
let mut rets: [i32; $nrets as usize] = [0; $nrets as usize];
|
||||
let mut i = 0;
|
||||
#[allow(unused_assignments)]
|
||||
{
|
||||
$(
|
||||
args_and_rets[i] = $args.into();
|
||||
i += 1;
|
||||
)*
|
||||
}
|
||||
let success: (bool, [i32; $nargs as usize + $nrets as usize]) = $crate::arch::ofw::ofw_call($entry_fn, $service, $nargs, $nrets, args_and_rets);
|
||||
for i in 0..$nrets as usize {
|
||||
rets[i] = success.1[i + $nargs as usize];
|
||||
}
|
||||
(success.0, rets)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// # call_method!
|
||||
/// a macro for calling package methods
|
||||
/// ## example
|
||||
/// ```
|
||||
/// use ofw::arch::EntryFunction;
|
||||
/// use ofw::{call_method, method_service_result, ServiceResult};
|
||||
///
|
||||
/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
|
||||
/// let package_instance: i32 = unimplemented!("get the package instance from somewhere");
|
||||
/// // <entry function>, <package instance>, <method name>, <# of args>, <# of rets>, <args...>
|
||||
/// let (success, returns): (bool, [i32; 1]) = call_method!(entry_fn, package_instance, ntstr!("seek"), 2, 1, 0, 0);
|
||||
/// if success {
|
||||
/// let seek_result: i32 = returns[0];
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! call_method {
|
||||
($entry_fn:expr, $package_instance:expr, $service:expr, $nargs:expr, $nrets:expr, $($args:expr),*) => {
|
||||
{
|
||||
let mut args_and_rets: [i32; $nargs as usize + $nrets as usize + 1] = [0; $nargs as usize + $nrets as usize + 1];
|
||||
let mut rets: [i32; $nrets as usize] = [0; $nrets as usize];
|
||||
let mut i = 0;
|
||||
#[allow(unused_assignments)]
|
||||
{
|
||||
$(
|
||||
args_and_rets[i] = $args.into();
|
||||
i += 1;
|
||||
)*
|
||||
}
|
||||
let success: (bool, [i32; $nargs as usize + $nrets as usize + 1]) = $crate::arch::ofw::ofw_call_method($entry_fn, $package_instance, $service, $nargs, $nrets, args_and_rets);
|
||||
for i in 0..$nrets as usize {
|
||||
rets[i] = success.1[i + $nargs as usize + 1];
|
||||
}
|
||||
(success.0 && success.1[$nargs as usize] != 0, rets)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// # ofw_call
|
||||
/// a function to call any OpenFirmware-provided service
|
||||
/// usage of the macro `call!` is preferred, but this function can be used directly
|
||||
/// ## example
|
||||
/// ```
|
||||
/// use ofw::arch::EntryFunction;
|
||||
/// use ofw::{ntstr, ofw_call};
|
||||
/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
|
||||
/// let args: [i32; 2] = [ntstr!("test") as i32, 0];
|
||||
/// let (was_service_successfully_called, args): (bool, [i32; 2]) = ofw_call(entry_fn, ntstr!("test"), 1, 1, args);
|
||||
/// let test_result: i32 = args[1];
|
||||
/// ```
|
||||
pub fn ofw_call<T: Sized>(entry_fn: EntryFunction, service: NTSTR, num_args: i32, num_rets: i32, args_and_rets: T) -> (bool, T) {
|
||||
#[repr(C)]
|
||||
struct GenericArgs<U: Sized> {
|
||||
args: Args,
|
||||
buffer: U,
|
||||
}
|
||||
let mut generic_args = GenericArgs {
|
||||
args: Args {
|
||||
service,
|
||||
nrets: num_rets,
|
||||
nargs: num_args,
|
||||
},
|
||||
buffer: args_and_rets,
|
||||
};
|
||||
|
||||
let result: bool;
|
||||
|
||||
#[cfg(target_arch = "powerpc")]
|
||||
{
|
||||
result = entry_fn(&mut generic_args as *mut _ as *mut Args) != -1;
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
result = entry_fn(&mut generic_args as *mut _ as *mut Args) != -1;
|
||||
}
|
||||
|
||||
(result, generic_args.buffer)
|
||||
}
|
||||
|
||||
/// # ofw_call_method
|
||||
/// a function to call a method provided by a package instance
|
||||
/// usage of the `call_method!` macro is preferred, but this function can be used directly
|
||||
/// ## example
|
||||
/// ```
|
||||
/// // we will be calling the "seek" method on an imaginary block device
|
||||
/// // the seek method takes 2 arguments, and returns 1 value
|
||||
/// // addr_hi, addr_lo -- okay?
|
||||
/// // however, we must add another cell in order to account for the catch-result
|
||||
/// // (see ieee 1275, 6.3.2.2 Device tree)
|
||||
/// use ofw::{IHandle, ntstr, ofw_call_method};
|
||||
/// use ofw::arch::EntryFunction;
|
||||
/// let entry_fn: EntryFunction = unimplemented!("get the entry function from somewhere");
|
||||
/// let instance: IHandle = unimplemented!("get the package instance from somewhere");
|
||||
/// // addr_hi, addr_lo, catch-result, okay?
|
||||
/// let args: [i32; 4] = [0, 0, 0, 0];
|
||||
/// // note that num_args and num_rets are not the same as the length of args, this is intentional
|
||||
/// let (was_service_successfully_called, args): (bool, [i32; 4]) = ofw_call_method(entry_fn, instance, ntstr!("seek"), 2, 1, args);
|
||||
/// if was_service_successfully_called && args[2] != 0 {
|
||||
/// let seek_result: i32 = args[3];
|
||||
/// }
|
||||
/// ```
|
||||
pub fn ofw_call_method<T>(entry_fn: EntryFunction, ihandle: IHandle, method: NTSTR, num_args: i32, num_rets: i32, args_and_rets: T) -> (bool, T) {
|
||||
#[repr(C)]
|
||||
struct CallMethodArgs<U> {
|
||||
args: Args,
|
||||
method: NTSTR,
|
||||
ihandle: IHandle,
|
||||
buffer: U,
|
||||
}
|
||||
|
||||
let mut call_method_args = CallMethodArgs {
|
||||
args: Args {
|
||||
service: ntstr!("call-method"),
|
||||
nrets: num_rets + 1,
|
||||
nargs: num_args + 2,
|
||||
},
|
||||
method,
|
||||
ihandle,
|
||||
buffer: args_and_rets,
|
||||
};
|
||||
|
||||
let result: bool;
|
||||
|
||||
#[cfg(target_arch = "powerpc")]
|
||||
{
|
||||
result = entry_fn(&mut call_method_args as *mut _ as *mut Args) != OFW_TRUE;
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
result = entry_fn(&mut call_method_args as *mut _ as *mut Args) != OFW_TRUE;
|
||||
}
|
||||
|
||||
(result, call_method_args.buffer)
|
||||
}
|
94
src/arch/ofw/ntstr.rs
Normal file
94
src/arch/ofw/ntstr.rs
Normal file
|
@ -0,0 +1,94 @@
|
|||
use core::fmt::{Debug, Display, Formatter, Write};
|
||||
|
||||
/// # ntstr!
|
||||
/// macro for creating a null-terminated string
|
||||
/// ## example
|
||||
/// ```
|
||||
/// use ofw::ntstr;
|
||||
/// let ntstr = ntstr!("Hello, world!");
|
||||
/// ```
|
||||
/// becomes
|
||||
/// ```
|
||||
/// let ntstr = ofw::ntstr::NTSTR::new(b"Hello, world!\0".as_ptr());
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! ntstr {
|
||||
($str:expr) => {
|
||||
$crate::arch::ofw::ntstr::NTSTR::new(concat!($str, "\0").as_ptr())
|
||||
};
|
||||
}
|
||||
|
||||
/// # NTSTR
|
||||
/// a null-terminated string, defined as a pointer to an array of u8 characters ending with a null byte
|
||||
/// not mutable
|
||||
#[repr(C)]
|
||||
pub struct NTSTR {
|
||||
ptr: *const u8,
|
||||
}
|
||||
|
||||
impl From<usize> for NTSTR {
|
||||
fn from(ptr: usize) -> Self {
|
||||
Self { ptr: ptr as *const u8 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<*const u8> for NTSTR {
|
||||
fn from(ptr: *const u8) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::from_over_into)]
|
||||
impl Into<i32> for NTSTR {
|
||||
fn into(self) -> i32 {
|
||||
self.ptr as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl NTSTR {
|
||||
pub const fn new(ptr: *const u8) -> Self {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
fn print(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
let a = unsafe { *self.ptr.add(i) };
|
||||
if a == 0 {
|
||||
break;
|
||||
}
|
||||
f.write_char(a as char)?;
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for NTSTR {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
let a = unsafe { *self.ptr.add(i) };
|
||||
let b = unsafe { *other.ptr.add(i) };
|
||||
if a != b {
|
||||
return false;
|
||||
}
|
||||
if a == 0 {
|
||||
return true;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for NTSTR {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
self.print(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NTSTR {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||
self.print(f)
|
||||
}
|
||||
}
|
15
src/arch/ofw/ofw_framebuffer/colors.rs
Normal file
15
src/arch/ofw/ofw_framebuffer/colors.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use crate::arch::ofw::ofw_framebuffer::OFColor;
|
||||
|
||||
pub const DARKBG_DEF: OFColor = OFColor {
|
||||
red: 37,
|
||||
green: 33,
|
||||
blue: 56,
|
||||
};
|
||||
pub const DARKBG: u8 = 1;
|
||||
|
||||
pub const WHITE_DEF: OFColor = OFColor {
|
||||
red: 255,
|
||||
green: 255,
|
||||
blue: 255,
|
||||
};
|
||||
pub const WHITE: u8 = 2;
|
16
src/arch/ofw/ofw_framebuffer/extrachars.rs
Normal file
16
src/arch/ofw/ofw_framebuffer/extrachars.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
pub const C_STAR: char = 128 as char;
|
||||
pub const C_HAPPY: char = 129 as char;
|
||||
pub const C_SAD: char = 130 as char;
|
||||
pub const C_OK: char = 131 as char;
|
||||
pub const C_NG: char = 132 as char;
|
||||
pub const C_VEXKA: char = 133 as char;
|
||||
pub const C_VORE: char = 134 as char;
|
||||
pub const C_VAP: char = 135 as char;
|
||||
pub const C_TRADEMARK: char = 136 as char;
|
||||
pub const C_COPYRIGHT: char = 137 as char;
|
||||
pub const C_REGISTERED: char = 138 as char;
|
||||
pub const C_COLONTHREE: char = 139 as char;
|
||||
pub const C_ENVELOPE: char = 140 as char;
|
||||
pub const C_LOCKED: char = 141 as char;
|
||||
pub const C_UNLOCKED: char = 142 as char;
|
||||
pub const C_KEY: char = 143 as char;
|
289
src/arch/ofw/ofw_framebuffer/mod.rs
Normal file
289
src/arch/ofw/ofw_framebuffer/mod.rs
Normal file
|
@ -0,0 +1,289 @@
|
|||
pub mod colors;
|
||||
pub mod extrachars;
|
||||
pub mod terminal;
|
||||
|
||||
use colors::*;
|
||||
use super::runtime::{OFW_RUNTIME, ofw_malloc};
|
||||
use crate::worm::Worm;
|
||||
use core::alloc::Layout;
|
||||
use core::fmt::Write;
|
||||
use core::mem::size_of;
|
||||
use core::ops::Deref;
|
||||
use super::ntstr;
|
||||
use crate::{call, call_method};
|
||||
use super::deviceio::io_open;
|
||||
use super::devicetree::{dt_finddevice, dt_getprop};
|
||||
|
||||
pub const VAPFONT: &[u8] = include_bytes!("./vapfont.data");
|
||||
pub const VAPFONT_W: usize = 256;
|
||||
pub const VAPFONT_H: usize = 112;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct OFColor {
|
||||
pub red: u8,
|
||||
pub green: u8,
|
||||
pub blue: u8,
|
||||
}
|
||||
|
||||
pub struct OFFramebuffer {
|
||||
pub address: usize,
|
||||
pub width: usize,
|
||||
pub height: usize,
|
||||
pub linebytes: usize,
|
||||
pub depth: usize,
|
||||
pub ofscr_buf: *mut u8,
|
||||
palette: [OFColor; 256],
|
||||
}
|
||||
|
||||
pub static FRAMEBUFFER: Worm<Option<OFFramebuffer>> = Worm::new(None);
|
||||
|
||||
impl OFFramebuffer {
|
||||
pub fn try_attach() -> Option<Self> {
|
||||
let entry = {
|
||||
let rt = &OFW_RUNTIME;
|
||||
rt.entry_fn.unwrap()
|
||||
};
|
||||
|
||||
let screen = dt_finddevice(entry, ntstr!("screen"))?;
|
||||
if screen == -1 {
|
||||
let mut rt = unsafe { OFW_RUNTIME.lock() };
|
||||
rt.write_str("no screen device found!");
|
||||
return None;
|
||||
}
|
||||
let mut address: usize = 0;
|
||||
let mut width: usize = 0;
|
||||
let mut height: usize = 0;
|
||||
let mut linebytes: usize = 0;
|
||||
let mut depth: usize = 0;
|
||||
|
||||
dt_getprop(
|
||||
entry,
|
||||
screen,
|
||||
ntstr!("address"),
|
||||
&mut address,
|
||||
size_of::<u32>(),
|
||||
);
|
||||
if address == 0 {
|
||||
// try getting frame-buffer-adr instead
|
||||
let (success, returns) = call!(
|
||||
entry,
|
||||
ntstr!("interpret"),
|
||||
1,
|
||||
3,
|
||||
ntstr!("frame-buffer-adr"),
|
||||
(&mut address) as *mut _ as usize as i32
|
||||
);
|
||||
if !success {
|
||||
let mut rt = unsafe { OFW_RUNTIME.lock() };
|
||||
rt.write_str("screen.address and frame-buffer-adr failed, no framebuffer");
|
||||
return None;
|
||||
}
|
||||
address = returns[2] as usize;
|
||||
}
|
||||
|
||||
dt_getprop(entry, screen, ntstr!("width"), &mut width, size_of::<u32>());
|
||||
dt_getprop(
|
||||
entry,
|
||||
screen,
|
||||
ntstr!("height"),
|
||||
&mut height,
|
||||
size_of::<u32>(),
|
||||
);
|
||||
dt_getprop(
|
||||
entry,
|
||||
screen,
|
||||
ntstr!("linebytes"),
|
||||
&mut linebytes,
|
||||
size_of::<u32>(),
|
||||
);
|
||||
dt_getprop(entry, screen, ntstr!("depth"), &mut depth, size_of::<u32>());
|
||||
|
||||
let ofscr_buf = {
|
||||
ofw_malloc(Layout::array::<u8>(width * height).unwrap())
|
||||
};
|
||||
|
||||
Some(OFFramebuffer {
|
||||
address,
|
||||
width,
|
||||
height,
|
||||
linebytes,
|
||||
depth,
|
||||
ofscr_buf,
|
||||
palette: [const {
|
||||
OFColor {
|
||||
red: 0,
|
||||
green: 0,
|
||||
blue: 0,
|
||||
}
|
||||
}; 256],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_default_colors() {
|
||||
OFFramebuffer::set_colors(&[DARKBG_DEF, WHITE_DEF]);
|
||||
}
|
||||
|
||||
pub fn set_colors(colors: &[OFColor]) {
|
||||
let color_count = colors.len();
|
||||
let colorbuf = {
|
||||
let ptr = ofw_malloc(Layout::array::<u8>(color_count * 3).unwrap());
|
||||
unsafe { core::slice::from_raw_parts_mut(ptr, color_count * 3) }
|
||||
};
|
||||
for (i, color) in colors.iter().enumerate() {
|
||||
colorbuf[i * 3] = color.red;
|
||||
colorbuf[i * 3 + 1] = color.green;
|
||||
colorbuf[i * 3 + 2] = color.blue;
|
||||
}
|
||||
|
||||
let entry = { OFW_RUNTIME.entry_fn.unwrap() };
|
||||
let screen = io_open(entry, ntstr!("screen")).expect("CANNOT OPEN OFW SCREEN");
|
||||
unsafe {
|
||||
let mut fb = FRAMEBUFFER.lock();
|
||||
let mut fb = fb.as_mut().unwrap();
|
||||
if fb.depth == 8 {
|
||||
let (success, _returns) = call_method!(
|
||||
entry,
|
||||
screen,
|
||||
ntstr!("set-colors"),
|
||||
3,
|
||||
0,
|
||||
color_count as i32,
|
||||
1,
|
||||
colorbuf.as_ptr() as i32
|
||||
);
|
||||
if success {
|
||||
let mut rt = unsafe { OFW_RUNTIME.lock() };
|
||||
rt.write_str("failed to set framebuffer colors!");
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
for (i, color) in colors.into_iter().enumerate() {
|
||||
fb.palette[i] = color.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_screen(color: u8) {
|
||||
if let Some(fb) = FRAMEBUFFER.as_ref() {
|
||||
OFFramebuffer::rectangle(color as usize, 0, 0, fb.width, fb.height);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn swap_buffer() {
|
||||
if let Some(fb) = FRAMEBUFFER.as_ref() {
|
||||
let mem = fb.address as *mut u8;
|
||||
let stride = fb.linebytes;
|
||||
let width = fb.width;
|
||||
let height = fb.height;
|
||||
let mut i = 0;
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let color = unsafe { fb.ofscr_buf.add(i).read_volatile() };
|
||||
unsafe {
|
||||
match fb.depth {
|
||||
8 => {
|
||||
*mem.add((y * stride) + x) = color;
|
||||
}
|
||||
_ => {
|
||||
let clr = &fb.palette[color as usize - 1];
|
||||
let offset = (y * stride) + (x * 4);
|
||||
*mem.add(offset) = 0;
|
||||
*mem.add(offset + 3) = clr.blue;
|
||||
*mem.add(offset + 2) = clr.green;
|
||||
*mem.add(offset + 1) = clr.red;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rectangle(colour: usize, x1: usize, y1: usize, x2: usize, y2: usize) {
|
||||
if let Some(fb) = FRAMEBUFFER.as_ref() {
|
||||
//let mem = fb.address as *mut u8;
|
||||
//let stride = fb.linebytes;
|
||||
let width = fb.width;
|
||||
let height = fb.height;
|
||||
for y in y1..y2 {
|
||||
for x in x1..x2 {
|
||||
if x < width && y < height {
|
||||
unsafe { fb.ofscr_buf.add((y * width) + x).write_volatile(colour as u8) };
|
||||
//unsafe {
|
||||
// match fb.depth {
|
||||
// 8 => {
|
||||
// *mem.add((y * stride) + x) = colour as u8;
|
||||
// }
|
||||
// _ => {
|
||||
// let clr = &fb.palette[colour - 1];
|
||||
// let offset = (y * stride) + (x * 4);
|
||||
// *mem.add(offset) = 0;
|
||||
// *mem.add(offset + 3) = clr.blue;
|
||||
// *mem.add(offset + 2) = clr.green;
|
||||
// *mem.add(offset + 1) = clr.red;
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_char_array(mut x: usize, mut y: usize, s: &[char], color: u8) {
|
||||
let ogx = x;
|
||||
if let Some(fb) = FRAMEBUFFER.as_ref() {
|
||||
const CHAR_SIZE: usize = 16;
|
||||
for c in s {
|
||||
let c = *c;
|
||||
if c == '\n' {
|
||||
y += CHAR_SIZE;
|
||||
x = ogx;
|
||||
continue;
|
||||
} else if c == ' ' {
|
||||
x += CHAR_SIZE;
|
||||
} else if c as u8 > 32 {
|
||||
let c = c as u8 - 32;
|
||||
let cx = (c % 16) as usize * CHAR_SIZE;
|
||||
let cy = (c / 16) as usize * CHAR_SIZE;
|
||||
for row in 0..CHAR_SIZE {
|
||||
for col in 0..CHAR_SIZE {
|
||||
let coff = (VAPFONT_W * (cy + row)) + (cx + col);
|
||||
if coff >= VAPFONT.len() {
|
||||
continue;
|
||||
}
|
||||
let draw = VAPFONT[coff];
|
||||
if draw != 0 {
|
||||
unsafe { fb.ofscr_buf.add((y + row) * fb.width + (x + col)).write_volatile(color) };
|
||||
//match fb.depth {
|
||||
// 8 => {
|
||||
// let mem = fb.address as *mut u8;
|
||||
// let offset = (y + row) * fb.linebytes + (x + col);
|
||||
// unsafe {
|
||||
// *mem.add(offset) = color;
|
||||
// }
|
||||
// }
|
||||
// _ => {
|
||||
// let mem = fb.address as *mut u8;
|
||||
// let offset = (y + row) * fb.linebytes + (x + col) * 4;
|
||||
// if color != 0 {
|
||||
// let color = &fb.palette[color as usize - 1];
|
||||
// unsafe {
|
||||
// *mem.add(offset) = 0;
|
||||
// *mem.add(offset + 3) = color.blue;
|
||||
// *mem.add(offset + 2) = color.green;
|
||||
// *mem.add(offset + 1) = color.red;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
x += CHAR_SIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
103
src/arch/ofw/ofw_framebuffer/terminal.rs
Normal file
103
src/arch/ofw/ofw_framebuffer/terminal.rs
Normal file
|
@ -0,0 +1,103 @@
|
|||
use core::arch::asm;
|
||||
use core::fmt::Write;
|
||||
use crate::arch::ofw::ofw_framebuffer::{colors, OFFramebuffer};
|
||||
use crate::spinlock::Spinlock;
|
||||
use crate::uart::Serial;
|
||||
|
||||
pub struct FramebufferTerminal {
|
||||
pub buffer: [[char; 48]; 30],
|
||||
pub cursor: (usize, usize),
|
||||
}
|
||||
|
||||
pub static FB_TERMINAL: Spinlock<FramebufferTerminal> = Spinlock::new(FramebufferTerminal::empty());
|
||||
|
||||
impl FramebufferTerminal {
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
buffer: [[' '; 48]; 30],
|
||||
cursor: (0, 0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scroll_terminal(&mut self) {
|
||||
for line_num in 0..(self.buffer.len() - 1) {
|
||||
let copy_from = self.buffer[line_num + 1];
|
||||
self.buffer[line_num] = copy_from;
|
||||
}
|
||||
self.buffer[self.buffer.len() - 1] = [' '; 48];
|
||||
}
|
||||
|
||||
pub fn clear_terminal(&mut self) {
|
||||
self.buffer = [[' '; 48]; 30];
|
||||
self.cursor = (0, 0);
|
||||
}
|
||||
|
||||
pub fn printstr(&mut self, str: &str) {
|
||||
for c in str.chars() {
|
||||
if self.cursor.1 >= self.buffer.len() {
|
||||
self.cursor.1 = 0;
|
||||
}
|
||||
if self.cursor.0 >= self.buffer[0].len() {
|
||||
self.cursor.0 = 0;
|
||||
}
|
||||
if c == '\n' || c == '\r' {
|
||||
self.cursor.0 = 0;
|
||||
self.cursor.1 += 1;
|
||||
if self.cursor.1 >= self.buffer.len() {
|
||||
self.cursor.1 -= 1;
|
||||
self.scroll_terminal();
|
||||
}
|
||||
} else {
|
||||
self.buffer[self.cursor.1][self.cursor.0] = c;
|
||||
self.cursor.0 += 1;
|
||||
if self.cursor.0 >= self.buffer[0].len() {
|
||||
self.cursor.0 = 0;
|
||||
self.cursor.1 += 1;
|
||||
if self.cursor.1 >= self.buffer.len() {
|
||||
self.cursor.1 -= 1;
|
||||
self.scroll_terminal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.render();
|
||||
}
|
||||
|
||||
pub fn render(&self) {
|
||||
OFFramebuffer::clear_screen(colors::DARKBG);
|
||||
|
||||
for (i, line) in self.buffer.iter().enumerate() {
|
||||
OFFramebuffer::put_char_array(2, 2 + (i * 18), line, colors::WHITE);
|
||||
}
|
||||
|
||||
OFFramebuffer::swap_buffer();
|
||||
unsafe {
|
||||
asm!("sync");
|
||||
asm!("isync");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for FramebufferTerminal {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
self.printstr(s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TerminalLayer;
|
||||
|
||||
impl Serial for TerminalLayer {
|
||||
fn put(&self, c: u8) {
|
||||
let s = [c];
|
||||
FB_TERMINAL.lock().printstr(unsafe { str::from_utf8_unchecked(&s) })
|
||||
}
|
||||
|
||||
fn putstr(&self, s: &str) {
|
||||
FB_TERMINAL.lock().printstr(s);
|
||||
}
|
||||
|
||||
fn put_bytes(&self, s: &[u8]) {
|
||||
FB_TERMINAL.lock().printstr(unsafe { str::from_utf8_unchecked(s) })
|
||||
}
|
||||
}
|
BIN
src/arch/ofw/ofw_framebuffer/vapfont.data
Normal file
BIN
src/arch/ofw/ofw_framebuffer/vapfont.data
Normal file
Binary file not shown.
149
src/arch/ofw/runtime.rs
Normal file
149
src/arch/ofw/runtime.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
use core::alloc::Layout;
|
||||
use core::fmt::Write;
|
||||
use core::mem::{size_of, transmute};
|
||||
|
||||
use super::{IHandle};
|
||||
use super::deviceio::{io_open, io_write};
|
||||
use super::devicetree::{dt_child, dt_finddevice, dt_getprop};
|
||||
use super::memory::{m_claim, m_release};
|
||||
use crate::arch::ofw::EntryFunction;
|
||||
use crate::ntstr;
|
||||
use crate::worm::Worm;
|
||||
|
||||
pub enum OfwRuntimeStage {
|
||||
None,
|
||||
Stdout,
|
||||
}
|
||||
|
||||
pub struct OfwRuntime {
|
||||
pub stage: OfwRuntimeStage,
|
||||
pub entry_fn: Option<EntryFunction>,
|
||||
stdout: Option<IHandle>,
|
||||
}
|
||||
|
||||
pub static OFW_RUNTIME: Worm<OfwRuntime> = Worm::new(OfwRuntime {
|
||||
stage: OfwRuntimeStage::None,
|
||||
entry_fn: None,
|
||||
stdout: None,
|
||||
});
|
||||
|
||||
pub fn init_ofw_runtime(entry: EntryFunction) -> bool {
|
||||
unsafe {
|
||||
let mut rt = OFW_RUNTIME.lock();
|
||||
rt.entry_fn = Some(entry);
|
||||
drop(rt);
|
||||
}
|
||||
|
||||
let chosen = dt_finddevice(entry, ntstr!("/chosen"));
|
||||
if chosen.is_none() {
|
||||
return false;
|
||||
}
|
||||
let chosen = chosen.unwrap();
|
||||
if chosen == -1 {
|
||||
return false;
|
||||
}
|
||||
let mut buf = [0u8; size_of::<IHandle>()];
|
||||
let _ = dt_getprop(
|
||||
entry,
|
||||
chosen,
|
||||
ntstr!("stdout"),
|
||||
buf.as_mut_ptr(),
|
||||
size_of::<IHandle>(),
|
||||
);
|
||||
let stdout = unsafe { transmute::<_, IHandle>(buf) };
|
||||
if stdout == -1 {
|
||||
// todo: continue without stdout instead of exiting
|
||||
return false;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let mut rt = OFW_RUNTIME.lock();
|
||||
rt.stdout = Some(stdout);
|
||||
rt.stage = OfwRuntimeStage::Stdout;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub fn primary_cpu_freq() -> u32 {
|
||||
let entry = { OFW_RUNTIME.entry_fn }
|
||||
.expect("alloc before ofw init! not like you can see this panic meesage though");
|
||||
let root = dt_finddevice(entry, ntstr!("/cpus")).unwrap();
|
||||
let first = dt_child(entry, root).unwrap();
|
||||
let mut freq = 0;
|
||||
let _ = dt_getprop(
|
||||
entry,
|
||||
first,
|
||||
ntstr!("timebase-frequency"),
|
||||
&mut freq as *mut u32 as *mut u8,
|
||||
size_of::<u32>(),
|
||||
);
|
||||
freq
|
||||
}
|
||||
|
||||
pub fn ofw_malloc(layout: Layout) -> *mut u8 {
|
||||
let entry = { OFW_RUNTIME.entry_fn }
|
||||
.expect("alloc before ofw init! not like you can see this panic meesage though");
|
||||
let addr = m_claim(entry, 0, layout.size() as _, layout.align() as _);
|
||||
addr.expect("out of memory") as _
|
||||
}
|
||||
|
||||
pub fn ofw_free(ptr: *mut u8, layout: Layout) {
|
||||
let entry = { OFW_RUNTIME.entry_fn }
|
||||
.expect("free before ofw init! not like you can see this panic meesage though");
|
||||
m_release(entry, ptr as usize, layout.size() as _);
|
||||
}
|
||||
|
||||
impl Write for OfwRuntime {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
if matches!(self.stage, OfwRuntimeStage::None) {
|
||||
panic!("not like you're going to be able to see this panic message, but oh well");
|
||||
}
|
||||
|
||||
let len = s.len();
|
||||
|
||||
let _ = io_write(
|
||||
self.entry_fn.unwrap(),
|
||||
self.stdout.unwrap(),
|
||||
s.as_ptr(),
|
||||
len,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ofw_print {
|
||||
($($arg:tt)*) => {$crate::ofw_runtime::_ofw_print(format_args!($($arg)*))};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! ofw_println {
|
||||
($($arg:tt)*) => {$crate::ofw_print!("{}\r\n", format_args!($($arg)*))};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn _ofw_print(args: core::fmt::Arguments) {
|
||||
struct Wrapper<'a>(&'a Worm<OfwRuntime>);
|
||||
impl<'a> core::fmt::Write for Wrapper<'a> {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
if matches!(self.0.stage, OfwRuntimeStage::None) {
|
||||
panic!("not like you're going to be able to see this panic message, but oh well");
|
||||
}
|
||||
|
||||
let len = s.len();
|
||||
|
||||
let _ = io_write(
|
||||
self.0.entry_fn.unwrap(),
|
||||
self.0.stdout.unwrap(),
|
||||
s.as_ptr(),
|
||||
len,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
let rt = &OFW_RUNTIME;
|
||||
Wrapper(rt).write_fmt(args).unwrap()
|
||||
}
|
170
src/arch/ofw/tests.rs
Normal file
170
src/arch/ofw/tests.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
use crate::arch::EntryFunction;
|
||||
use crate::{Args, call, call_method, IHandle, ntstr, OFW_FALSE, OFW_TRUE};
|
||||
use crate::cell_conv::DoubleWord;
|
||||
use crate::ntstr::NTSTR;
|
||||
|
||||
#[test]
|
||||
fn ntstr_macro() {
|
||||
let a = ntstr!("Hello, world!");
|
||||
let b = NTSTR::new(b"Hello, world!\0".as_ptr());
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doubleword_conversion() {
|
||||
let a: u64 = 0xabcdef0123456789;
|
||||
let a_dw: DoubleWord = a.into();
|
||||
let b: u64 = a_dw.into();
|
||||
assert_eq!(a, b);
|
||||
assert_eq!(a_dw.high as u32, 0xabcdef01);
|
||||
assert_eq!(a_dw.low as u32, 0x23456789);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_ofw() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct GenericArgs {
|
||||
args: Args,
|
||||
buffer: [i32; 2],
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut GenericArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("test"));
|
||||
assert_eq!(args.args.nargs, 1);
|
||||
assert_eq!(args.args.nrets, 1);
|
||||
args.buffer[1] = OFW_FALSE; // not missing
|
||||
0
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let (success, results) = call!(entry_fn, ntstr!("test"), 1, 1, ntstr!("test"));
|
||||
assert!(success);
|
||||
assert_eq!(results[0], OFW_FALSE);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn call_ofw_missing() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct GenericArgs {
|
||||
args: Args,
|
||||
buffer: [i32; 2],
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut GenericArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("test"));
|
||||
assert_eq!(args.args.nargs, 1);
|
||||
assert_eq!(args.args.nrets, 1);
|
||||
args.buffer[1] = OFW_TRUE; // missing
|
||||
0
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let (success, results) = call!(entry_fn, ntstr!("test"), 1, 1, ntstr!("aioshjfoahwfi"));
|
||||
assert!(success);
|
||||
assert_eq!(results[0], OFW_TRUE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_ofw_failure() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct GenericArgs {
|
||||
args: Args
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut GenericArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("this-method-does-not-exist"));
|
||||
assert_eq!(args.args.nargs, 1);
|
||||
assert_eq!(args.args.nrets, 1);
|
||||
-1
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let (success, _) = call!(entry_fn, ntstr!("this-method-does-not-exist"), 1, 1, ntstr!("aioshjfoahwfi"));
|
||||
assert!(!success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_method_ofw() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct CallMethodArgs {
|
||||
args: Args,
|
||||
method: NTSTR,
|
||||
ihandle: IHandle,
|
||||
buffer: [i32; 4],
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut CallMethodArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("call-method"));
|
||||
assert_eq!(args.method, ntstr!("seek"));
|
||||
assert_eq!(args.ihandle, 0);
|
||||
assert_eq!(args.args.nargs, 2 + 2);
|
||||
assert_eq!(args.args.nrets, 1 + 1);
|
||||
// 2 offset from args in buffer
|
||||
args.buffer[2] = 1; // set catch-result to non-zero
|
||||
args.buffer[2 + 1] = OFW_TRUE; // set okay? to true
|
||||
0
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let ih: IHandle = 0;
|
||||
let (success, returns) = call_method!(entry_fn, ih, ntstr!("seek"), 2, 1, 0, 0);
|
||||
assert!(success);
|
||||
assert_eq!(returns[0], OFW_TRUE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_method_ofw_seek_failure() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct CallMethodArgs {
|
||||
args: Args,
|
||||
method: NTSTR,
|
||||
ihandle: IHandle,
|
||||
buffer: [i32; 4],
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut CallMethodArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("call-method"));
|
||||
assert_eq!(args.method, ntstr!("seek"));
|
||||
assert_eq!(args.ihandle, 0);
|
||||
assert_eq!(args.args.nargs, 2 + 2);
|
||||
assert_eq!(args.args.nrets, 1 + 1);
|
||||
// 2 offset from args in buffer
|
||||
args.buffer[2] = 1; // set catch-result to non-zero
|
||||
args.buffer[2 + 1] = OFW_FALSE; // set okay? to false
|
||||
0
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let ih: IHandle = 0;
|
||||
let (success, returns) = call_method!(entry_fn, ih, ntstr!("seek"), 2, 1, 0, 0);
|
||||
assert!(success);
|
||||
assert_eq!(returns[0], OFW_FALSE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn call_method_ofw_failure() {
|
||||
pub extern "C" fn test_entry_function(args: *mut Args) -> i32 {
|
||||
#[repr(C)]
|
||||
struct CallMethodArgs {
|
||||
args: Args,
|
||||
method: NTSTR,
|
||||
ihandle: IHandle,
|
||||
buffer: [i32; 4],
|
||||
}
|
||||
let args = unsafe { &mut *(args as *mut CallMethodArgs) };
|
||||
assert_eq!(args.args.service, ntstr!("call-method"));
|
||||
assert_eq!(args.method, ntstr!("asiofhawoptgh"));
|
||||
assert_eq!(args.ihandle, 0);
|
||||
assert_eq!(args.args.nargs, 2 + 2);
|
||||
assert_eq!(args.args.nrets, 1 + 1);
|
||||
// 2 offset from args in buffer
|
||||
args.buffer[2] = 0; // set catch-result to 0 (caught error, package doesn't have this method)
|
||||
0
|
||||
}
|
||||
|
||||
let entry_fn: EntryFunction = test_entry_function;
|
||||
let ih: IHandle = 0;
|
||||
let (success, _) = call_method!(entry_fn, ih, ntstr!("asiofhawoptgh"), 2, 1, 0, 0);
|
||||
assert!(!success);
|
||||
}
|
38
src/arch/ppc32/asm/linker.ld
Normal file
38
src/arch/ppc32/asm/linker.ld
Normal file
|
@ -0,0 +1,38 @@
|
|||
OUTPUT_FORMAT("elf32-powerpc")
|
||||
OUTPUT_ARCH("powerpc:common")
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS {
|
||||
|
||||
.text 0x100000 : AT(0x100000) {
|
||||
*(.text .text.*)
|
||||
}
|
||||
|
||||
.vector_container : {
|
||||
VAP_VECTOR_LOCATION = .;
|
||||
*(V_TEXT_GENESIS)
|
||||
*(V_VECTOR_DEFS)
|
||||
}
|
||||
|
||||
.stack : {
|
||||
*(VAP_STACK_SECTION)
|
||||
}
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(COMMON)
|
||||
*(.bss .bss.*)
|
||||
}
|
||||
|
||||
.other : {
|
||||
*(*)
|
||||
}
|
||||
}
|
39
src/arch/ppc32/asm/realmode.S
Normal file
39
src/arch/ppc32/asm/realmode.S
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "regnames.h"
|
||||
|
||||
.section .text
|
||||
|
||||
.global _get_real
|
||||
_get_real:
|
||||
mflr r5
|
||||
|
||||
mfmsr r3
|
||||
// disable interrupts
|
||||
rlwinm r3, r3, 0, 17, 15
|
||||
mtmsr r3
|
||||
|
||||
lis r4, _real_mode@ha
|
||||
addi r4, r4, _real_mode@l
|
||||
|
||||
mtspr srr0, r4
|
||||
|
||||
rlwinm r3, r3, 0, 28, 25 // disable IR/DR
|
||||
mtspr srr1, r3
|
||||
rfi
|
||||
_real_mode:
|
||||
mtlr r5
|
||||
blr
|
||||
|
||||
.global _set_msr
|
||||
// r3 = msr value, returns old msr
|
||||
_set_msr:
|
||||
mtsrr1 r3
|
||||
mflr r12
|
||||
lis r3, _set_msr_next@ha
|
||||
addi r3, r3, _set_msr_next@l
|
||||
mtsrr0 r3
|
||||
mfmsr r3
|
||||
rfi
|
||||
_set_msr_next:
|
||||
mtlr r12
|
||||
blr
|
||||
|
175
src/arch/ppc32/asm/regnames.h
Normal file
175
src/arch/ppc32/asm/regnames.h
Normal file
|
@ -0,0 +1,175 @@
|
|||
#define cr0 0
|
||||
#define cr1 1
|
||||
#define cr2 2
|
||||
#define cr3 3
|
||||
#define cr4 4
|
||||
#define cr5 5
|
||||
#define cr6 6
|
||||
#define cr7 7
|
||||
|
||||
#define r0 0
|
||||
#define r1 1
|
||||
#define r2 2
|
||||
#define r3 3
|
||||
#define r4 4
|
||||
#define r5 5
|
||||
#define r6 6
|
||||
#define r7 7
|
||||
#define r8 8
|
||||
#define r9 9
|
||||
#define r10 10
|
||||
#define r11 11
|
||||
#define r12 12
|
||||
#define r13 13
|
||||
#define r14 14
|
||||
#define r15 15
|
||||
#define r16 16
|
||||
#define r17 17
|
||||
#define r18 18
|
||||
#define r19 19
|
||||
#define r20 20
|
||||
#define r21 21
|
||||
#define r22 22
|
||||
#define r23 23
|
||||
#define r24 24
|
||||
#define r25 25
|
||||
#define r26 26
|
||||
#define r27 27
|
||||
#define r28 28
|
||||
#define r29 29
|
||||
#define r30 30
|
||||
#define r31 31
|
||||
|
||||
#define sp 1
|
||||
|
||||
#define fr0 0
|
||||
#define fr1 1
|
||||
#define fr2 2
|
||||
#define fr3 3
|
||||
#define fr4 4
|
||||
#define fr5 5
|
||||
#define fr6 6
|
||||
#define fr7 7
|
||||
#define fr8 8
|
||||
#define fr9 9
|
||||
#define fr10 10
|
||||
#define fr11 11
|
||||
#define fr12 12
|
||||
#define fr13 13
|
||||
#define fr14 14
|
||||
#define fr15 15
|
||||
#define fr16 16
|
||||
#define fr17 17
|
||||
#define fr18 18
|
||||
#define fr19 19
|
||||
#define fr20 20
|
||||
#define fr21 21
|
||||
#define fr22 22
|
||||
#define fr23 23
|
||||
#define fr24 24
|
||||
#define fr25 25
|
||||
#define fr26 26
|
||||
#define fr27 27
|
||||
#define fr28 28
|
||||
#define fr29 29
|
||||
#define fr30 30
|
||||
#define fr31 31
|
||||
|
||||
#define vr0 0
|
||||
#define vr1 1
|
||||
#define vr2 2
|
||||
#define vr3 3
|
||||
#define vr4 4
|
||||
#define vr5 5
|
||||
#define vr6 6
|
||||
#define vr7 7
|
||||
#define vr8 8
|
||||
#define vr9 9
|
||||
#define vr10 10
|
||||
#define vr11 11
|
||||
#define vr12 12
|
||||
#define vr13 13
|
||||
#define vr14 14
|
||||
#define vr15 15
|
||||
#define vr16 16
|
||||
#define vr17 17
|
||||
#define vr18 18
|
||||
#define vr19 19
|
||||
#define vr20 20
|
||||
#define vr21 21
|
||||
#define vr22 22
|
||||
#define vr23 23
|
||||
#define vr24 24
|
||||
#define vr25 25
|
||||
#define vr26 26
|
||||
#define vr27 27
|
||||
#define vr28 28
|
||||
#define vr29 29
|
||||
#define vr30 30
|
||||
#define vr31 31
|
||||
|
||||
#define evr0 0
|
||||
#define evr1 1
|
||||
#define evr2 2
|
||||
#define evr3 3
|
||||
#define evr4 4
|
||||
#define evr5 5
|
||||
#define evr6 6
|
||||
#define evr7 7
|
||||
#define evr8 8
|
||||
#define evr9 9
|
||||
#define evr10 10
|
||||
#define evr11 11
|
||||
#define evr12 12
|
||||
#define evr13 13
|
||||
#define evr14 14
|
||||
#define evr15 15
|
||||
#define evr16 16
|
||||
#define evr17 17
|
||||
#define evr18 18
|
||||
#define evr19 19
|
||||
#define evr20 20
|
||||
#define evr21 21
|
||||
#define evr22 22
|
||||
#define evr23 23
|
||||
#define evr24 24
|
||||
#define evr25 25
|
||||
#define evr26 26
|
||||
#define evr27 27
|
||||
#define evr28 28
|
||||
#define evr29 29
|
||||
#define evr30 30
|
||||
#define evr31 31
|
||||
|
||||
#define xer 1
|
||||
#define lr 8
|
||||
#define ctr 9
|
||||
#define dec 22
|
||||
#define sdr1 25
|
||||
#define srr0 26
|
||||
#define srr1 27
|
||||
#define sprg0 272
|
||||
#define sprg1 273
|
||||
#define sprg2 274
|
||||
#define sprg3 275
|
||||
#define prv 287
|
||||
#define ibat0u 528
|
||||
#define ibat0l 529
|
||||
#define ibat1u 530
|
||||
#define ibat1l 531
|
||||
#define ibat2u 532
|
||||
#define ibat2l 533
|
||||
#define ibat3u 534
|
||||
#define ibat3l 535
|
||||
#define dbat0u 536
|
||||
#define dbat0l 537
|
||||
#define dbat1u 538
|
||||
#define dbat1l 539
|
||||
#define dbat2u 540
|
||||
#define dbat2l 541
|
||||
#define dbat3u 542
|
||||
#define dbat3l 543
|
||||
#define tlbmiss 980
|
||||
#define ptehi 981
|
||||
#define ptelo 982
|
||||
#define hid0 1008
|
345
src/arch/ppc32/asm/trap.S
Normal file
345
src/arch/ppc32/asm/trap.S
Normal file
|
@ -0,0 +1,345 @@
|
|||
#include "regnames.h"
|
||||
|
||||
#define ISTATE_OFFSET_SP_FRAME 0x00
|
||||
#define ISTATE_OFFSET_LR_FRAME 0x04
|
||||
#define ISTATE_OFFSET_R0 0x08
|
||||
#define ISTATE_OFFSET_R2 0x0c
|
||||
#define ISTATE_OFFSET_R3 0x10
|
||||
#define ISTATE_OFFSET_R4 0x14
|
||||
#define ISTATE_OFFSET_R5 0x18
|
||||
#define ISTATE_OFFSET_R6 0x1c
|
||||
#define ISTATE_OFFSET_R7 0x20
|
||||
#define ISTATE_OFFSET_R8 0x24
|
||||
#define ISTATE_OFFSET_R9 0x28
|
||||
#define ISTATE_OFFSET_R10 0x2c
|
||||
#define ISTATE_OFFSET_R11 0x30
|
||||
#define ISTATE_OFFSET_R13 0x34
|
||||
#define ISTATE_OFFSET_R14 0x38
|
||||
#define ISTATE_OFFSET_R15 0x3c
|
||||
#define ISTATE_OFFSET_R16 0x40
|
||||
#define ISTATE_OFFSET_R17 0x44
|
||||
#define ISTATE_OFFSET_R18 0x48
|
||||
#define ISTATE_OFFSET_R19 0x4c
|
||||
#define ISTATE_OFFSET_R20 0x50
|
||||
#define ISTATE_OFFSET_R21 0x54
|
||||
#define ISTATE_OFFSET_R22 0x58
|
||||
#define ISTATE_OFFSET_R23 0x5c
|
||||
#define ISTATE_OFFSET_R24 0x60
|
||||
#define ISTATE_OFFSET_R25 0x64
|
||||
#define ISTATE_OFFSET_R26 0x68
|
||||
#define ISTATE_OFFSET_R27 0x6c
|
||||
#define ISTATE_OFFSET_R28 0x70
|
||||
#define ISTATE_OFFSET_R29 0x74
|
||||
#define ISTATE_OFFSET_R30 0x78
|
||||
#define ISTATE_OFFSET_R31 0x7c
|
||||
#define ISTATE_OFFSET_CR 0x80
|
||||
#define ISTATE_OFFSET_SRR0 0x84
|
||||
#define ISTATE_OFFSET_SRR1 0x88
|
||||
#define ISTATE_OFFSET_LR 0x8c
|
||||
#define ISTATE_OFFSET_CTR 0x90
|
||||
#define ISTATE_OFFSET_XER 0x94
|
||||
#define ISTATE_OFFSET_DAR 0x98
|
||||
#define ISTATE_OFFSET_R12 0x9c
|
||||
#define ISTATE_OFFSET_SP 0xa0
|
||||
#define ISTATE_SIZE 0xa4
|
||||
|
||||
#define ALIGN_UP(s, a) (((s) + ((a) - 1)) & ~((a) - 1))
|
||||
|
||||
.section V_TEXT_GENESIS, "ax"
|
||||
|
||||
.macro CTX_SAVE
|
||||
mtsprg1 r12
|
||||
mfcr r12
|
||||
mtsprg2 sp
|
||||
|
||||
lis sp, EXCEPTION_STACK@ha
|
||||
addi sp, sp, EXCEPTION_STACK@l
|
||||
|
||||
subi sp, sp, ALIGN_UP(ISTATE_SIZE, 16)
|
||||
stw r0, ISTATE_OFFSET_R0(sp)
|
||||
stw r2, ISTATE_OFFSET_R2(sp)
|
||||
stw r3, ISTATE_OFFSET_R3(sp)
|
||||
stw r4, ISTATE_OFFSET_R4(sp)
|
||||
stw r5, ISTATE_OFFSET_R5(sp)
|
||||
stw r6, ISTATE_OFFSET_R6(sp)
|
||||
stw r7, ISTATE_OFFSET_R7(sp)
|
||||
stw r8, ISTATE_OFFSET_R8(sp)
|
||||
stw r9, ISTATE_OFFSET_R9(sp)
|
||||
stw r10, ISTATE_OFFSET_R10(sp)
|
||||
stw r11, ISTATE_OFFSET_R11(sp)
|
||||
stw r13, ISTATE_OFFSET_R13(sp)
|
||||
stw r14, ISTATE_OFFSET_R14(sp)
|
||||
stw r15, ISTATE_OFFSET_R15(sp)
|
||||
stw r16, ISTATE_OFFSET_R16(sp)
|
||||
stw r17, ISTATE_OFFSET_R17(sp)
|
||||
stw r18, ISTATE_OFFSET_R18(sp)
|
||||
stw r19, ISTATE_OFFSET_R19(sp)
|
||||
stw r20, ISTATE_OFFSET_R20(sp)
|
||||
stw r21, ISTATE_OFFSET_R21(sp)
|
||||
stw r22, ISTATE_OFFSET_R22(sp)
|
||||
stw r23, ISTATE_OFFSET_R23(sp)
|
||||
stw r24, ISTATE_OFFSET_R24(sp)
|
||||
stw r25, ISTATE_OFFSET_R25(sp)
|
||||
stw r26, ISTATE_OFFSET_R26(sp)
|
||||
stw r27, ISTATE_OFFSET_R27(sp)
|
||||
stw r28, ISTATE_OFFSET_R28(sp)
|
||||
stw r29, ISTATE_OFFSET_R29(sp)
|
||||
stw r30, ISTATE_OFFSET_R30(sp)
|
||||
stw r31, ISTATE_OFFSET_R31(sp)
|
||||
|
||||
stw r12, ISTATE_OFFSET_CR(sp)
|
||||
|
||||
mfsrr0 r12
|
||||
stw r12, ISTATE_OFFSET_SRR0(sp)
|
||||
|
||||
mfsrr1 r12
|
||||
stw r12, ISTATE_OFFSET_SRR1(sp)
|
||||
|
||||
mflr r12
|
||||
stw r12, ISTATE_OFFSET_LR(sp)
|
||||
|
||||
mfctr r12
|
||||
stw r12, ISTATE_OFFSET_CTR(sp)
|
||||
|
||||
mfxer r12
|
||||
stw r12, ISTATE_OFFSET_XER(sp)
|
||||
|
||||
mfdar r12
|
||||
stw r12, ISTATE_OFFSET_DAR(sp)
|
||||
|
||||
mfsprg1 r12
|
||||
stw r12, ISTATE_OFFSET_R12(sp)
|
||||
|
||||
mfsprg2 r12
|
||||
stw r12, ISTATE_OFFSET_SP(sp)
|
||||
|
||||
li r12, 0
|
||||
stw r12, ISTATE_OFFSET_LR_FRAME(sp)
|
||||
stw r12, ISTATE_OFFSET_SP_FRAME(sp)
|
||||
.endm
|
||||
|
||||
// CTX_SAVE is 232 bytes, you get 6 more instructions
|
||||
|
||||
// _decrementer_exception (1)
|
||||
// called when the decrementer exception occurs (i.e. when it reaches 0)
|
||||
.global _decrementer_exception
|
||||
_decrementer_exception:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_de@ha
|
||||
addi r12, r12, _trap_de@l
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _decrementer_exception
|
||||
.global _decrementer_exception_end
|
||||
_decrementer_exception_end:
|
||||
.global _trap_de
|
||||
_trap_de:
|
||||
lis r12, trap@ha
|
||||
addi r12, r12, trap@l
|
||||
mr r3, sp
|
||||
li r4, 1 // 1 = dec
|
||||
mtsrr0 r12
|
||||
mfmsr r12
|
||||
rlwinm r12, r12, 0, 28, 25 // unset IR and DR
|
||||
andi. r12, r12, ~(1 << 15)@l // disable interrupts
|
||||
mtsrr1 r12
|
||||
rfi
|
||||
|
||||
// _external_interrupt (2)
|
||||
// called on all external hardware interrupts
|
||||
.global _external_interrupt
|
||||
_external_interrupt:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_ex@ha
|
||||
addi r12, r12, _trap_ex@l
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _external_interrupt
|
||||
.global _external_interrupt_end
|
||||
_external_interrupt_end:
|
||||
.global _trap_ex
|
||||
_trap_ex:
|
||||
lis r12, trap@ha
|
||||
addi r12, r12, trap@l
|
||||
mr r3, sp
|
||||
li r4, 2 // 2 = external
|
||||
mtsrr0 r12
|
||||
mfmsr r12
|
||||
rlwinm r12, r12, 0, 28, 25 // unset IR and DR
|
||||
andi. r12, r12, ~(1 << 15)@l // disable interrupts
|
||||
mtsrr1 r12
|
||||
rfi
|
||||
|
||||
// _syscall_exception (3)
|
||||
// called when sc instruction is executed
|
||||
.global _syscall_exception
|
||||
_syscall_exception:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_sc@ha
|
||||
addi r12, r12, _trap_sc@l
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _syscall_exception
|
||||
.global _syscall_exception_end
|
||||
_syscall_exception_end:
|
||||
.global _trap_sc
|
||||
_trap_sc:
|
||||
lis r12, trap@ha
|
||||
addi r12, r12, trap@l
|
||||
mr r3, sp
|
||||
li r4, 3 // 3 = syscall
|
||||
mtsrr0 r12
|
||||
mfmsr r12
|
||||
rlwinm r12, r12, 0, 28, 25 // unset IR and DR
|
||||
andi. r12, r12, ~(1 << 15)@l // disable interrupts
|
||||
mtsrr1 r12
|
||||
rfi
|
||||
|
||||
// _bad_exception (4)
|
||||
// generic handler for when something bad happens
|
||||
.global _be_ds
|
||||
_be_ds:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_be@ha
|
||||
addi r12, r12, _trap_be@l
|
||||
li r5, 3 // data storage
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _be_ds
|
||||
.global _be_ds_end
|
||||
_be_ds_end:
|
||||
.global _be_is
|
||||
_be_is:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_be@ha
|
||||
addi r12, r12, _trap_be@l
|
||||
li r5, 4 // instruction storage
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _be_is
|
||||
.global _be_is_end
|
||||
_be_is_end:
|
||||
.global _be_al
|
||||
_be_al:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_be@ha
|
||||
addi r12, r12, _trap_be@l
|
||||
li r5, 6 // alignment
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _be_al
|
||||
.global _be_al_end
|
||||
_be_al_end:
|
||||
.global _be_pr
|
||||
_be_pr:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_be@ha
|
||||
addi r12, r12, _trap_be@l
|
||||
li r5, 7 // program (illegal instruction)
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _be_pr
|
||||
.global _be_pr_end
|
||||
_be_pr_end:
|
||||
.global _be_fp
|
||||
_be_fp:
|
||||
CTX_SAVE
|
||||
|
||||
lis r12, _trap_be@ha
|
||||
addi r12, r12, _trap_be@l
|
||||
li r5, 8 // floating point unavailable
|
||||
mtlr r12
|
||||
blr
|
||||
// end of _be_fp
|
||||
.global _be_fp_end
|
||||
_be_fp_end:
|
||||
|
||||
.global _trap_be
|
||||
_trap_be:
|
||||
lis r12, trap@ha
|
||||
addi r12, r12, trap@l
|
||||
mr r3, sp
|
||||
li r4, 4 // 4 = bad exception
|
||||
mtsrr0 r12
|
||||
rlwinm r12, r12, 0, 28, 25 // unset IR and DR
|
||||
andi. r12, r12, ~(1 << 15)@l // disable interrupts
|
||||
mtsrr1 r12
|
||||
rfi
|
||||
|
||||
.global _vap_return_from_exception
|
||||
_vap_return_from_exception:
|
||||
// load r3 into sp
|
||||
mr sp, r3
|
||||
sync
|
||||
isync
|
||||
|
||||
// restore context
|
||||
lwz r0, ISTATE_OFFSET_R0(sp)
|
||||
lwz r2, ISTATE_OFFSET_R2(sp)
|
||||
lwz r3, ISTATE_OFFSET_R3(sp)
|
||||
lwz r4, ISTATE_OFFSET_R4(sp)
|
||||
lwz r5, ISTATE_OFFSET_R5(sp)
|
||||
lwz r6, ISTATE_OFFSET_R6(sp)
|
||||
lwz r7, ISTATE_OFFSET_R7(sp)
|
||||
lwz r8, ISTATE_OFFSET_R8(sp)
|
||||
lwz r9, ISTATE_OFFSET_R9(sp)
|
||||
lwz r10, ISTATE_OFFSET_R10(sp)
|
||||
lwz r11, ISTATE_OFFSET_R11(sp)
|
||||
lwz r13, ISTATE_OFFSET_R13(sp)
|
||||
lwz r14, ISTATE_OFFSET_R14(sp)
|
||||
lwz r15, ISTATE_OFFSET_R15(sp)
|
||||
lwz r16, ISTATE_OFFSET_R16(sp)
|
||||
lwz r17, ISTATE_OFFSET_R17(sp)
|
||||
lwz r18, ISTATE_OFFSET_R18(sp)
|
||||
lwz r19, ISTATE_OFFSET_R19(sp)
|
||||
lwz r20, ISTATE_OFFSET_R20(sp)
|
||||
lwz r21, ISTATE_OFFSET_R21(sp)
|
||||
lwz r22, ISTATE_OFFSET_R22(sp)
|
||||
lwz r23, ISTATE_OFFSET_R23(sp)
|
||||
lwz r24, ISTATE_OFFSET_R24(sp)
|
||||
lwz r25, ISTATE_OFFSET_R25(sp)
|
||||
lwz r26, ISTATE_OFFSET_R26(sp)
|
||||
lwz r27, ISTATE_OFFSET_R27(sp)
|
||||
lwz r28, ISTATE_OFFSET_R28(sp)
|
||||
lwz r29, ISTATE_OFFSET_R29(sp)
|
||||
lwz r30, ISTATE_OFFSET_R30(sp)
|
||||
lwz r31, ISTATE_OFFSET_R31(sp)
|
||||
|
||||
lwz r12, ISTATE_OFFSET_CR(sp)
|
||||
mtcr r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_SRR0(sp)
|
||||
mtsrr0 r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_SRR1(sp)
|
||||
mtsrr1 r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_LR(sp)
|
||||
mtlr r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_CTR(sp)
|
||||
mtctr r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_XER(sp)
|
||||
mtxer r12
|
||||
|
||||
lwz r12, ISTATE_OFFSET_R12(sp)
|
||||
lwz sp, ISTATE_OFFSET_SP(sp)
|
||||
|
||||
// return to task
|
||||
rfi
|
||||
|
||||
.global _hung_system
|
||||
_hung_system:
|
||||
b _hung_system
|
||||
|
||||
.align 4
|
||||
.space 0x2000
|
||||
EXCEPTION_STACK:
|
0
src/arch/ppc32/memory.rs
Normal file
0
src/arch/ppc32/memory.rs
Normal file
242
src/arch/ppc32/mod.rs
Normal file
242
src/arch/ppc32/mod.rs
Normal file
|
@ -0,0 +1,242 @@
|
|||
pub mod trap;
|
||||
mod trap_patch;
|
||||
mod memory;
|
||||
|
||||
use crate::arch::ofw::EntryFunction;
|
||||
use crate::arch::ofw::ofw_framebuffer::terminal::FB_TERMINAL;
|
||||
use crate::arch::ofw::ofw_framebuffer::{FRAMEBUFFER, OFFramebuffer};
|
||||
use crate::arch::ofw::runtime::{OFW_RUNTIME, init_ofw_runtime, primary_cpu_freq};
|
||||
use crate::arch::ppc32::trap::TrapFrame;
|
||||
use core::arch::{asm};
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
|
||||
use crate::{arch, ktask};
|
||||
use crate::memory::TOTAL_MEMORY;
|
||||
use crate::syscalls::create_task;
|
||||
use crate::trafficcontrol::TaskSetup;
|
||||
|
||||
pub static PPC_HEAP_START: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn eh_personality() {}
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn rust_eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(info: &PanicInfo) -> ! {
|
||||
let msr = MSRSetup {
|
||||
external_interrupt_enable: false,
|
||||
usermode: false,
|
||||
floating_point_enable: false,
|
||||
machine_check_enable: false,
|
||||
instruction_translation: false,
|
||||
data_translation: false,
|
||||
};
|
||||
unsafe {
|
||||
set_msr(msr_value(&msr));
|
||||
}
|
||||
unsafe {
|
||||
FB_TERMINAL.force_unlock();
|
||||
FRAMEBUFFER.force_unlock();
|
||||
OFW_RUNTIME.force_unlock();
|
||||
}
|
||||
let mut serial = FB_TERMINAL.lock();
|
||||
serial.write_fmt(format_args!("panic! {}", info.message()));
|
||||
serial.write_fmt(format_args!("{:?}\n", info));
|
||||
loop {
|
||||
unsafe { asm!("nop") }
|
||||
}
|
||||
}
|
||||
|
||||
pub static DEC_SPEED: AtomicU32 = AtomicU32::new(1000000);
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _get_real();
|
||||
fn _set_msr(msr: usize) -> usize;
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
#[unsafe(link_section = ".text")]
|
||||
extern "C" fn _start(_r3: usize, _r4: usize, entry: EntryFunction) -> isize {
|
||||
if !init_ofw_runtime(entry) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// init framebuffer
|
||||
{
|
||||
let fb = OFFramebuffer::try_attach();
|
||||
if fb.is_none() {
|
||||
return -1;
|
||||
}
|
||||
let fb = fb.unwrap();
|
||||
unsafe {
|
||||
*FRAMEBUFFER.lock() = Some(fb);
|
||||
}
|
||||
OFFramebuffer::set_default_colors();
|
||||
}
|
||||
|
||||
let serial = crate::arch::serial_port().unwrap();
|
||||
|
||||
serial.putstr("lbos PowerPC kernel boot...\n");
|
||||
|
||||
// setup DEC
|
||||
{
|
||||
let freq = primary_cpu_freq();
|
||||
let decspeed = freq / 100;
|
||||
DEC_SPEED.store(decspeed, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
// setup heap
|
||||
{
|
||||
let rt = unsafe { &OFW_RUNTIME };
|
||||
let heap = arch::ofw::memory::m_claim(rt.entry_fn.unwrap(), 0, TOTAL_MEMORY as i32, 512);
|
||||
if heap.is_none() {
|
||||
panic!("failed to claim heap");
|
||||
}
|
||||
PPC_HEAP_START.store(heap.unwrap(), Ordering::Relaxed);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
_get_real();
|
||||
}
|
||||
|
||||
serial.putstr("realmode asserted\n");
|
||||
|
||||
serial.putstr("patching trap vecs...\n");
|
||||
trap_patch::patch_trap_handlers();
|
||||
serial.putstr("done\n");
|
||||
|
||||
let msr = MSRSetup {
|
||||
external_interrupt_enable: true,
|
||||
usermode: false,
|
||||
floating_point_enable: false,
|
||||
machine_check_enable: false,
|
||||
instruction_translation: false,
|
||||
data_translation: false,
|
||||
};
|
||||
unsafe {
|
||||
set_msr(msr_value(&msr));
|
||||
}
|
||||
unsafe {
|
||||
set_dec(DEC_SPEED.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
create_task(TaskSetup {
|
||||
epc: ktask as usize,
|
||||
});
|
||||
|
||||
loop {
|
||||
unsafe { asm!("nop") }
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn set_dec(dec: u32) {
|
||||
asm!("mtdec {}", in(reg) dec);
|
||||
}
|
||||
|
||||
pub struct MSRSetup {
|
||||
pub external_interrupt_enable: bool,
|
||||
pub usermode: bool,
|
||||
pub floating_point_enable: bool,
|
||||
pub machine_check_enable: bool,
|
||||
pub instruction_translation: bool,
|
||||
pub data_translation: bool,
|
||||
}
|
||||
|
||||
pub fn msr_value(setup: &MSRSetup) -> u32 {
|
||||
let mut value = 0;
|
||||
if setup.external_interrupt_enable {
|
||||
value |= 1 << 15;
|
||||
}
|
||||
if setup.usermode {
|
||||
value |= 1 << 14;
|
||||
}
|
||||
if setup.floating_point_enable {
|
||||
value |= 1 << 13;
|
||||
}
|
||||
if setup.machine_check_enable {
|
||||
value |= 1 << 12;
|
||||
}
|
||||
if setup.instruction_translation {
|
||||
value |= 1 << 5;
|
||||
}
|
||||
if setup.data_translation {
|
||||
value |= 1 << 4;
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe fn set_msr(msr: u32) -> u32 {
|
||||
//asm!("mtmsr {}", in(reg) msr);
|
||||
_set_msr(msr as usize) as u32
|
||||
}
|
||||
|
||||
pub fn user_program_initial_trapframe(ep: usize, sp: usize) -> TrapFrame {
|
||||
TrapFrame {
|
||||
sp_frame: 0,
|
||||
lr_frame: 0,
|
||||
r0: 0,
|
||||
r2: 0,
|
||||
r3: 0,
|
||||
r4: 0,
|
||||
r5: 0,
|
||||
r6: 0,
|
||||
r7: 0,
|
||||
r8: 0,
|
||||
r9: 0,
|
||||
r10: 0,
|
||||
r11: 0,
|
||||
r13: 0,
|
||||
r14: 0,
|
||||
r15: 0,
|
||||
r16: 0,
|
||||
r17: 0,
|
||||
r18: 0,
|
||||
r19: 0,
|
||||
r20: 0,
|
||||
r21: 0,
|
||||
r22: 0,
|
||||
r23: 0,
|
||||
r24: 0,
|
||||
r25: 0,
|
||||
r26: 0,
|
||||
r27: 0,
|
||||
r28: 0,
|
||||
r29: 0,
|
||||
r30: 0,
|
||||
r31: 0,
|
||||
cr: 0,
|
||||
srr0: ep,
|
||||
srr1: msr_value(&MSRSetup {
|
||||
external_interrupt_enable: true,
|
||||
usermode: false,
|
||||
floating_point_enable: false,
|
||||
machine_check_enable: false,
|
||||
instruction_translation: false,
|
||||
data_translation: false,
|
||||
}) as usize,
|
||||
lr: 0,
|
||||
ctr: 0,
|
||||
xer: 0,
|
||||
dar: 0,
|
||||
r12: 0,
|
||||
sp,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn _ppc32_syscall(
|
||||
syscall_number: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize,
|
||||
arg5: usize,
|
||||
arg6: usize,
|
||||
) -> usize {
|
||||
let mut res;
|
||||
unsafe { asm!("sc", in("r3") syscall_number, in("r4") arg1, in("r5") arg2, in("r6") arg3, in("r7") arg4, in("r8") arg5, in("r9") arg6, lateout("r3") res) };
|
||||
res
|
||||
}
|
202
src/arch/ppc32/trap.rs
Normal file
202
src/arch/ppc32/trap.rs
Normal file
|
@ -0,0 +1,202 @@
|
|||
use core::sync::atomic::Ordering;
|
||||
use crate::arch::ppc32::{set_dec, user_program_initial_trapframe, DEC_SPEED};
|
||||
use crate::arch::{ofw, serial_port};
|
||||
use crate::strprint::u32_hex;
|
||||
use crate::syscalls::usize2sc;
|
||||
use crate::trafficcontrol::{context_switch, handle_syscall, MAX_TASKS, TC};
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Clone)]
|
||||
pub struct TrapFrame {
|
||||
pub sp_frame: usize,
|
||||
pub lr_frame: usize,
|
||||
pub r0: usize,
|
||||
pub r2: usize,
|
||||
pub r3: usize,
|
||||
pub r4: usize,
|
||||
pub r5: usize,
|
||||
pub r6: usize,
|
||||
pub r7: usize,
|
||||
pub r8: usize,
|
||||
pub r9: usize,
|
||||
pub r10: usize,
|
||||
pub r11: usize,
|
||||
pub r13: usize,
|
||||
pub r14: usize,
|
||||
pub r15: usize,
|
||||
pub r16: usize,
|
||||
pub r17: usize,
|
||||
pub r18: usize,
|
||||
pub r19: usize,
|
||||
pub r20: usize,
|
||||
pub r21: usize,
|
||||
pub r22: usize,
|
||||
pub r23: usize,
|
||||
pub r24: usize,
|
||||
pub r25: usize,
|
||||
pub r26: usize,
|
||||
pub r27: usize,
|
||||
pub r28: usize,
|
||||
pub r29: usize,
|
||||
pub r30: usize,
|
||||
pub r31: usize,
|
||||
pub cr: usize,
|
||||
pub srr0: usize,
|
||||
pub srr1: usize,
|
||||
pub lr: usize,
|
||||
pub ctr: usize,
|
||||
pub xer: usize,
|
||||
pub dar: usize,
|
||||
pub r12: usize,
|
||||
pub sp: usize,
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Debug)]
|
||||
pub enum TrapCode {
|
||||
Decrementer = 1,
|
||||
External = 2,
|
||||
Syscall = 3,
|
||||
Bad = 4,
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(Debug)]
|
||||
pub enum BadExtra {
|
||||
DataStorage = 3,
|
||||
InstructionStorage = 4,
|
||||
Alignment = 6,
|
||||
Program = 7,
|
||||
FloatingPointUnavail = 8,
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _vap_return_from_exception(frame: *mut TrapFrame) -> !;
|
||||
fn _hung_system() -> !;
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn trap(frame_ptr: *mut TrapFrame, code: usize, extra: usize) -> ! {
|
||||
{
|
||||
let mut frame = unsafe { frame_ptr.read_volatile().clone() };
|
||||
let code = match code {
|
||||
x if x == TrapCode::Decrementer as usize => TrapCode::Decrementer,
|
||||
x if x == TrapCode::External as usize => TrapCode::External,
|
||||
x if x == TrapCode::Syscall as usize => TrapCode::Syscall,
|
||||
x if x == TrapCode::Bad as usize => TrapCode::Bad,
|
||||
_ => panic!("bad trap code {}", code),
|
||||
};
|
||||
|
||||
|
||||
match code {
|
||||
TrapCode::Decrementer => {
|
||||
//frame = crate::trafficcontrol::context_switch(frame);
|
||||
//unsafe { frame_ptr.write_volatile(frame) }
|
||||
|
||||
// context switch
|
||||
let mut tc = TC.lock();
|
||||
if !tc.first_task_setup {
|
||||
let mut first_task = MAX_TASKS + 1;
|
||||
for i in 0..MAX_TASKS {
|
||||
if tc.tasks[i].is_some() {
|
||||
first_task = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if first_task < MAX_TASKS {
|
||||
if let Some(task) = tc.tasks[first_task].take() {
|
||||
tc.first_task_setup = true;
|
||||
unsafe { frame_ptr.write_volatile(task.trap_frame.clone()) }
|
||||
tc.tasks[first_task] = Some(task);
|
||||
tc.current = first_task;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let ci = tc.current;
|
||||
if tc.tasks[ci].is_some() {
|
||||
// only switch if we have a task
|
||||
let mut curtask = unsafe { tc.tasks[ci].take().unwrap() };
|
||||
if !tc.hung_system {
|
||||
curtask.trap_frame = frame;
|
||||
}
|
||||
let next = context_switch(&mut tc, curtask);
|
||||
if let Some(next) = next {
|
||||
unsafe { frame_ptr.write_volatile(next.trap_frame.clone()) };
|
||||
tc.hung_system = false;
|
||||
} else {
|
||||
tc.hung_system = true;
|
||||
unsafe { frame_ptr.write_volatile(user_program_initial_trapframe(_hung_system as usize, 0)) };
|
||||
}
|
||||
} else {
|
||||
panic!("no task to switch to!");
|
||||
}
|
||||
}
|
||||
|
||||
let decspeed = DEC_SPEED.load(Ordering::Relaxed);
|
||||
if decspeed > 0 {
|
||||
unsafe {
|
||||
set_dec(decspeed);
|
||||
}
|
||||
} else {
|
||||
panic!("decspeed is 0!!! this is bad!!!\n");
|
||||
}
|
||||
}
|
||||
TrapCode::External => {
|
||||
}
|
||||
TrapCode::Syscall => {
|
||||
/*
|
||||
if log_would_block() {
|
||||
panic!("syscall while log would block!")
|
||||
}
|
||||
logln("syscall trap\n");
|
||||
let ret = {
|
||||
let syscall_number = frame.r3;
|
||||
let a1 = frame.r4;
|
||||
let a2 = frame.r5;
|
||||
let a3 = frame.r6;
|
||||
let a4 = frame.r7;
|
||||
let syscall = syscall_from_number(syscall_number);
|
||||
|
||||
crate::trafficcontrol::syscall(syscall, a1, a2, a3, a4)
|
||||
};
|
||||
frame.r3 = ret;
|
||||
*/
|
||||
let (ret, sus) = handle_syscall(
|
||||
usize2sc(frame.r3),
|
||||
frame.r4,
|
||||
frame.r5,
|
||||
frame.r6,
|
||||
frame.r7,
|
||||
frame.r8,
|
||||
frame.r9,
|
||||
);
|
||||
frame.r3 = ret;
|
||||
if sus {
|
||||
// move to another task
|
||||
let mut tc = TC.lock();
|
||||
let ci = tc.current;
|
||||
let mut curtask = unsafe { tc.tasks[ci].take().unwrap() };
|
||||
if !tc.hung_system {
|
||||
curtask.trap_frame = frame.clone();
|
||||
}
|
||||
let next = context_switch(&mut tc, curtask);
|
||||
if let Some(next) = next {
|
||||
unsafe { frame_ptr.write_volatile(next.trap_frame.clone()) };
|
||||
tc.hung_system = false;
|
||||
} else {
|
||||
tc.hung_system = true;
|
||||
unsafe { frame_ptr.write_volatile(user_program_initial_trapframe(_hung_system as usize, 0)) };
|
||||
}
|
||||
} else {
|
||||
unsafe { frame_ptr.write_volatile(frame.clone()) };
|
||||
}
|
||||
}
|
||||
TrapCode::Bad => {
|
||||
unsafe { panic!("bad trap: {}\npc: {:x}, msr: {:x}, sp: {:x}", extra, frame.srr0, frame.srr1, frame.sp); }
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
_vap_return_from_exception(frame_ptr)
|
||||
}
|
||||
}
|
57
src/arch/ppc32/trap_patch.rs
Normal file
57
src/arch/ppc32/trap_patch.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
unsafe extern "C" {
|
||||
// 0x900
|
||||
fn _decrementer_exception();
|
||||
fn _decrementer_exception_end();
|
||||
|
||||
// 0x500
|
||||
fn _external_interrupt();
|
||||
fn _external_interrupt_end();
|
||||
|
||||
// 0xC00
|
||||
fn _syscall_exception();
|
||||
fn _syscall_exception_end();
|
||||
|
||||
// 0x300
|
||||
fn _be_ds();
|
||||
fn _be_ds_end();
|
||||
// 0x400
|
||||
fn _be_is();
|
||||
fn _be_is_end();
|
||||
|
||||
// 0x600
|
||||
fn _be_al();
|
||||
fn _be_al_end();
|
||||
|
||||
// 0x700
|
||||
fn _be_pr();
|
||||
fn _be_pr_end();
|
||||
|
||||
// 0x800
|
||||
fn _be_fp();
|
||||
fn _be_fp_end();
|
||||
}
|
||||
|
||||
fn patch_trap(vector: u32, handler: usize, handler_end: usize) {
|
||||
if handler_end - handler >= 256 {
|
||||
panic!("trap handler too large for vector {:x}!", vector);
|
||||
}
|
||||
|
||||
let src = handler as *const u8;
|
||||
let dst = vector as *mut u8;
|
||||
for i in 0..(handler_end - handler) {
|
||||
unsafe {
|
||||
dst.add(i).write_volatile(src.add(i).read_volatile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn patch_trap_handlers() {
|
||||
patch_trap(0x900, _decrementer_exception as usize, _decrementer_exception_end as usize);
|
||||
patch_trap(0x500, _external_interrupt as usize, _external_interrupt_end as usize);
|
||||
patch_trap(0xC00, _syscall_exception as usize, _syscall_exception_end as usize);
|
||||
patch_trap(0x300, _be_ds as usize, _be_ds_end as usize);
|
||||
patch_trap(0x400, _be_is as usize, _be_is_end as usize);
|
||||
patch_trap(0x600, _be_al as usize, _be_al_end as usize);
|
||||
patch_trap(0x700, _be_pr as usize, _be_pr_end as usize);
|
||||
patch_trap(0x800, _be_fp as usize, _be_fp_end as usize);
|
||||
}
|
43
src/arch/virt/asm/boot.s
Normal file
43
src/arch/virt/asm/boot.s
Normal file
|
@ -0,0 +1,43 @@
|
|||
.option pic
|
||||
.option norvc
|
||||
.section .data
|
||||
.section .text.init
|
||||
|
||||
.global _start
|
||||
_start:
|
||||
csrr t0, mhartid
|
||||
bnez t0, 2f
|
||||
csrw satp, zero
|
||||
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, _global_pointer
|
||||
.option pop
|
||||
|
||||
la a0, _bss_start
|
||||
la a1, _bss_end
|
||||
1:
|
||||
sw zero, (a0)
|
||||
addi a0, a0, 4
|
||||
bltu a0, a1, 1b
|
||||
|
||||
la sp, _stack_end
|
||||
li t0, (0b11 << 11) | (1 << 7) | (1 << 3)
|
||||
csrw mstatus, t0
|
||||
la t1, _virt_init
|
||||
csrw mepc, t1
|
||||
la t2, _trap
|
||||
csrw mtvec, t2
|
||||
csrw mie, zero
|
||||
la ra, 2f
|
||||
mret
|
||||
|
||||
2:
|
||||
wfi
|
||||
j 2b
|
||||
|
||||
.global asm_enable_traps
|
||||
asm_enable_traps:
|
||||
csrw mie, t3
|
||||
csrw mepc, ra
|
||||
mret
|
58
src/arch/virt/asm/linker.ld
Normal file
58
src/arch/virt/asm/linker.ld
Normal file
|
@ -0,0 +1,58 @@
|
|||
OUTPUT_ARCH( "riscv" )
|
||||
|
||||
ENTRY( _start )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
rom (wxa) : ORIGIN = 0x80000000, LENGTH = 64 * 1024
|
||||
ram (wxa) : ORIGIN = 0x80010000, LENGTH = 1048510
|
||||
}
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD;
|
||||
data PT_LOAD;
|
||||
bss PT_LOAD;
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
*(.text.init)
|
||||
*(.text .text.*)
|
||||
} >rom AT>rom :text
|
||||
|
||||
PROVIDE(_global_pointer = .);
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
PROVIDE(_kernel_rom_end = .);
|
||||
} >rom AT>rom :text
|
||||
|
||||
.data : {
|
||||
*(.sdata .sdata.*) *(.data .data.*)
|
||||
} >ram AT>ram :data
|
||||
|
||||
.bss : {
|
||||
PROVIDE(_bss_start = .);
|
||||
*(.sbss .sbss.*)
|
||||
*(.bss .bss.*)
|
||||
. = ALIGN(4096);
|
||||
PROVIDE(_bss_end = .);
|
||||
} >ram AT>ram :bss
|
||||
|
||||
PROVIDE(_MEM_START = ORIGIN(ram));
|
||||
PROVIDE(_MEM_END = ORIGIN(ram) + LENGTH(ram));
|
||||
|
||||
PROVIDE(_SROM_START = _kernel_rom_end);
|
||||
PROVIDE(_SROM_SIZE = ORIGIN(rom) + LENGTH(rom) - _kernel_rom_end);
|
||||
PROVIDE(_SROM_END = _SROM_START + _SROM_SIZE);
|
||||
|
||||
PROVIDE(_stack_start = _bss_end);
|
||||
PROVIDE(_stack_end = _stack_start + 16384);
|
||||
PROVIDE(_tstack_start = _stack_end);
|
||||
PROVIDE(_tstack_end = _tstack_start + 16384);
|
||||
PROVIDE(_virtio_virtqueue_start = _tstack_end);
|
||||
PROVIDE(_virtio_virtqueue_end = _virtio_virtqueue_start + 32768);
|
||||
PROVIDE(_heap_start = _virtio_virtqueue_end);
|
||||
PROVIDE(_heap_size = _MEM_END - _heap_start);
|
||||
}
|
62
src/arch/virt/asm/trap.s
Normal file
62
src/arch/virt/asm/trap.s
Normal file
|
@ -0,0 +1,62 @@
|
|||
.option pic
|
||||
.option norvc
|
||||
.section .text
|
||||
.global _trap
|
||||
|
||||
# these macros are stolen from https://osblog.stephenmarz.com/ch4.html
|
||||
# i am too lazy to write my own register saving macros
|
||||
.altmacro
|
||||
.set NUM_GP_REGS, 32 # Number of registers per context
|
||||
.set REG_SIZE, 4 # Register size (in bytes)
|
||||
|
||||
# Use macros for saving and restoring multiple registers
|
||||
.macro save_gp i, basereg=t6
|
||||
sw x\i, ((\i)*REG_SIZE)(\basereg)
|
||||
.endm
|
||||
.macro load_gp i, basereg=t6
|
||||
lw x\i, ((\i)*REG_SIZE)(\basereg)
|
||||
.endm
|
||||
|
||||
.align 4
|
||||
_trap:
|
||||
csrrw t6, mscratch, t6
|
||||
|
||||
.set i, 1
|
||||
.rept 30
|
||||
save_gp %i
|
||||
.set i, i+1
|
||||
.endr
|
||||
|
||||
mv t5, t6
|
||||
csrr t6, mscratch
|
||||
save_gp 31, t5
|
||||
|
||||
csrw mscratch, t5
|
||||
|
||||
csrr a0, mepc
|
||||
csrr a1, mtval
|
||||
csrr a2, mcause
|
||||
csrr a3, mhartid
|
||||
csrr a4, mstatus
|
||||
mv a5, t5
|
||||
lw sp, 132(a5)
|
||||
|
||||
call ktrap
|
||||
|
||||
# returned ra into a0
|
||||
csrw mepc, a0
|
||||
|
||||
csrr t6, mscratch
|
||||
.set i, 1
|
||||
.rept 31
|
||||
load_gp %i
|
||||
.set i, i+1
|
||||
.endr
|
||||
|
||||
mret
|
||||
|
||||
.global _hung_system
|
||||
_hung_system:
|
||||
1:
|
||||
wfi
|
||||
j 1b
|
113
src/arch/virt/mod.rs
Normal file
113
src/arch/virt/mod.rs
Normal file
|
@ -0,0 +1,113 @@
|
|||
mod plic;
|
||||
pub mod tasks;
|
||||
pub mod trap;
|
||||
|
||||
use crate::ktask;
|
||||
use crate::syscalls::{create_task};
|
||||
use crate::uart::{Serial, UART};
|
||||
use core::arch::{asm, global_asm};
|
||||
#[cfg(feature = "debug_messages")]
|
||||
use core::fmt::Write;
|
||||
use liblbos::TaskSetup;
|
||||
|
||||
global_asm!(include_str!("asm/boot.s"));
|
||||
global_asm!(include_str!("asm/trap.s"));
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn eh_personality() {}
|
||||
|
||||
#[panic_handler]
|
||||
//#[cfg(feature = "debug_messages")]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
use core::fmt::Write;
|
||||
let mut uart = UART::new(0x1000_0000);
|
||||
let _ = writeln!(uart, "abort");
|
||||
let _ = writeln!(uart, "message: {}", info.message());
|
||||
if let Some(p) = info.location() {
|
||||
let _ = writeln!(
|
||||
uart,
|
||||
"line {}, column {}, file {}",
|
||||
p.line(),
|
||||
p.column(),
|
||||
p.file()
|
||||
);
|
||||
}
|
||||
loop {
|
||||
unsafe {
|
||||
asm!("wfi");
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
#[panic_handler]
|
||||
#[cfg(not(feature = "debug_messages"))]
|
||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||
let uart = UART::new(0x1000_0000);
|
||||
for c in b"PANIC".iter() {
|
||||
uart.put(*c);
|
||||
}
|
||||
loop {
|
||||
unsafe {
|
||||
asm!("wfi");
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
pub fn virt_rough_panic(errcode: [char; 3]) -> ! {
|
||||
let uart = UART::new(0x1000_0000);
|
||||
for c in b"PANIC".iter() {
|
||||
uart.put(*c);
|
||||
}
|
||||
for c in errcode.iter() {
|
||||
uart.put(*c as _);
|
||||
}
|
||||
|
||||
loop {
|
||||
unsafe {
|
||||
asm!("wfi");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn _virt_init() -> ! {
|
||||
UART::new_and_init(0x1000_0000);
|
||||
|
||||
plic::set_threshold(0);
|
||||
for i in 1..=10 {
|
||||
plic::enable(i);
|
||||
plic::set_priority(i, 1);
|
||||
}
|
||||
|
||||
trap::enable_traps();
|
||||
|
||||
create_task(TaskSetup {
|
||||
epc: ktask as usize,
|
||||
ddi_first_addr: 0,
|
||||
ddi_size: 0,
|
||||
wait_for_task_exit: false,
|
||||
});
|
||||
|
||||
loop {
|
||||
stall()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stall() {
|
||||
unsafe {
|
||||
asm!("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn serial_port() -> Option<&'static dyn Serial> {
|
||||
static UART_STATIC: UART = UART::new(0x1000_0000);
|
||||
Some(&UART_STATIC)
|
||||
}
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _syscall(syscall_num: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32) -> u32;
|
||||
}
|
||||
|
||||
pub fn virt_syscall(num: u32, a1: u32, a2: u32, a3: u32, a4: u32, a5: u32, a6: u32) -> u32 {
|
||||
unsafe { _syscall(num, a1, a2, a3, a4, a5, a6) }
|
||||
}
|
42
src/arch/virt/plic.rs
Normal file
42
src/arch/virt/plic.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
pub fn enable(id: u32) {
|
||||
let enables = 0x0c00_2000 as *mut u32;
|
||||
let actual_id = 1 << id;
|
||||
unsafe {
|
||||
enables.write_volatile(enables.read_volatile() | actual_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_priority(id: u32, priority: u8) {
|
||||
let actual_priority = priority as u32 & 7;
|
||||
let priority_reg = 0x0c00_0000 as *mut u32;
|
||||
unsafe {
|
||||
priority_reg.add(id as usize).write_volatile(actual_priority);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_threshold(threshold: u8) {
|
||||
let actual_threshold = threshold as u32 & 7;
|
||||
let threshold_reg = 0x0c20_0000 as *mut u32;
|
||||
unsafe {
|
||||
threshold_reg.write_volatile(actual_threshold);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next() -> Option<u32> {
|
||||
let claim_reg = 0x0c20_0004 as *const u32;
|
||||
|
||||
let claim_num = unsafe { claim_reg.read_volatile() };
|
||||
|
||||
if claim_num == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(claim_num)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn complete(id: u32) {
|
||||
let complete_reg = 0x0c20_0004 as *mut u32;
|
||||
unsafe {
|
||||
complete_reg.write_volatile(id);
|
||||
}
|
||||
}
|
13
src/arch/virt/tasks.rs
Normal file
13
src/arch/virt/tasks.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
use crate::arch::virt::trap::{TrapFrame, _tstack_end};
|
||||
|
||||
pub fn setup_task(sp: usize) -> TrapFrame {
|
||||
let mut tf = TrapFrame {
|
||||
regs: [0; 32],
|
||||
satp: 0,
|
||||
trap_stack: _tstack_end as usize,
|
||||
hartid: 0,
|
||||
};
|
||||
tf.regs[1] = crate::trafficcontrol::program_default_exit as usize;
|
||||
tf.regs[2] = sp;
|
||||
tf
|
||||
}
|
251
src/arch/virt/trap.rs
Normal file
251
src/arch/virt/trap.rs
Normal file
|
@ -0,0 +1,251 @@
|
|||
use crate::arch::virt::{plic, serial_port};
|
||||
use crate::rough_panic;
|
||||
use crate::syscalls::usize2sc;
|
||||
use crate::trafficcontrol::{TC, context_switch, handle_syscall, MAX_TASKS};
|
||||
use core::arch::asm;
|
||||
use crate::spinlock::Spinlock;
|
||||
|
||||
unsafe extern "C" {
|
||||
pub fn _tstack_end();
|
||||
pub fn _hung_system() -> !;
|
||||
}
|
||||
|
||||
pub static KERNEL_TRAP_FRAME: Spinlock<TrapFrame> = Spinlock::new(TrapFrame {
|
||||
regs: [0; 32],
|
||||
satp: 0,
|
||||
trap_stack: 0,
|
||||
hartid: 0,
|
||||
});
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct TrapFrame {
|
||||
pub regs: [usize; 32], // 0 - 128
|
||||
pub satp: usize, // 128 - 132
|
||||
pub trap_stack: usize, // 132 - 136
|
||||
pub hartid: usize, // 136 - 140
|
||||
}
|
||||
|
||||
pub fn enable_traps() {
|
||||
let tf = {
|
||||
let mut lock = KERNEL_TRAP_FRAME.lock();
|
||||
lock.trap_stack = _tstack_end as usize;
|
||||
unsafe { KERNEL_TRAP_FRAME.leak() }
|
||||
};
|
||||
// setup mscratch to hold trap frame
|
||||
unsafe {
|
||||
let addr = tf;
|
||||
asm!("csrw mscratch, {}", in(reg) addr);
|
||||
|
||||
let mie: u32 = (1 << 3) | (1 << 7) | (1 << 11);
|
||||
asm!("csrw mie, {}", in(reg) mie);
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn ktrap(
|
||||
epc: usize,
|
||||
tval: usize,
|
||||
cause: usize,
|
||||
hart: usize,
|
||||
status: usize,
|
||||
frame: &mut TrapFrame,
|
||||
) -> usize {
|
||||
#[allow(arithmetic_overflow)]
|
||||
let is_async = { (cause >> 63) & 1 == 1 };
|
||||
|
||||
let cause_num = cause & 0xfff;
|
||||
let mut return_pc = epc;
|
||||
if is_async {
|
||||
match cause_num {
|
||||
3 => {
|
||||
//send_ka(KernelAlert::MachineSoftwareInterrupt);
|
||||
}
|
||||
7 => {
|
||||
let mtimecmp = 0x0200_4000 as *mut u64;
|
||||
let mtime = 0x0200_bff8 as *const u64;
|
||||
|
||||
// one sec
|
||||
const FREQ: u64 = 10_000_000;
|
||||
const CSPS: u64 = 5000;
|
||||
unsafe { mtimecmp.write_volatile(mtime.read_volatile().wrapping_add(FREQ / CSPS)) };
|
||||
//send_ka(KernelAlert::TimerInterrupt);
|
||||
|
||||
// context switch
|
||||
let mut tc = TC.lock();
|
||||
if !tc.first_task_setup {
|
||||
let mut first_task = MAX_TASKS + 1;
|
||||
for i in 0..MAX_TASKS {
|
||||
if tc.tasks[i].is_some() {
|
||||
first_task = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if first_task < MAX_TASKS {
|
||||
if let Some(task) = tc.tasks[first_task].take() {
|
||||
tc.first_task_setup = true;
|
||||
return_pc = task.epc;
|
||||
*frame = task.trap_frame.clone();
|
||||
tc.tasks[first_task] = Some(task);
|
||||
tc.current = first_task;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let ci = tc.current;
|
||||
if tc.tasks.get(ci).is_some() {
|
||||
// only switch if we have a task
|
||||
let mut curtask = unsafe { tc.tasks[ci].take().unwrap_unchecked() };
|
||||
if !tc.hung_system {
|
||||
curtask.epc = epc;
|
||||
curtask.trap_frame = frame.clone();
|
||||
}
|
||||
let next = context_switch(&mut tc, curtask);
|
||||
if let Some(next) = next {
|
||||
return_pc = next.epc;
|
||||
*frame = next.trap_frame.clone();
|
||||
tc.hung_system = false;
|
||||
} else {
|
||||
return_pc = _hung_system as usize;
|
||||
tc.hung_system = true;
|
||||
}
|
||||
} else {
|
||||
rough_panic(['n', 'm', 'p']);
|
||||
}
|
||||
}
|
||||
}
|
||||
11 => {
|
||||
//send_ka(KernelAlert::MachineExternalInterrupt);
|
||||
if let Some(interrupt) = plic::next() {
|
||||
#[cfg(feature = "dev_virtio")]
|
||||
if (1..=8).contains(&interrupt) {
|
||||
// virtio
|
||||
let mut tc = TC.lock();
|
||||
crate::dev::virtio::handle_interrupt(interrupt, &mut tc);
|
||||
}
|
||||
if interrupt == 10 {
|
||||
// uart
|
||||
let uart = unsafe { serial_port().unwrap_unchecked() };
|
||||
if let Some(c) = uart.get() {
|
||||
if c != 0 {
|
||||
let mut tc = TC.lock();
|
||||
tc.write_inbuf(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
plic::complete(interrupt);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
#[cfg(feature = "debug_messages")]
|
||||
panic!("unhandled async trap cpu{} -> {}", hart, cause_num);
|
||||
#[cfg(not(feature = "debug_messages"))]
|
||||
rough_panic(['t', 'r', 'a'])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match cause_num {
|
||||
2 => {
|
||||
#[cfg(feature = "debug_messages")]
|
||||
panic!(
|
||||
"illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\n",
|
||||
hart, epc, tval
|
||||
);
|
||||
#[cfg(not(feature = "debug_messages"))]
|
||||
rough_panic(['t', 'i', 'l'])
|
||||
}
|
||||
x if x == 8 || x == 9 || x == 11 => {
|
||||
let (ret, sus) = handle_syscall(
|
||||
usize2sc(frame.regs[10]),
|
||||
frame.regs[11],
|
||||
frame.regs[12],
|
||||
frame.regs[13],
|
||||
frame.regs[14],
|
||||
frame.regs[15],
|
||||
frame.regs[16],
|
||||
);
|
||||
frame.regs[10] = ret;
|
||||
|
||||
return_pc += 4;
|
||||
if sus {
|
||||
// move to another task
|
||||
let mut tc = TC.lock();
|
||||
let ci = tc.current;
|
||||
let mut curtask = unsafe { tc.tasks[ci].take().unwrap_unchecked() };
|
||||
if !tc.hung_system {
|
||||
curtask.epc = return_pc;
|
||||
curtask.trap_frame = frame.clone();
|
||||
}
|
||||
let next = context_switch(&mut tc, curtask);
|
||||
if let Some(next) = next {
|
||||
return_pc = next.epc;
|
||||
*frame = next.trap_frame.clone();
|
||||
tc.hung_system = false;
|
||||
} else {
|
||||
return_pc = _hung_system as usize;
|
||||
tc.hung_system = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
9 => {
|
||||
// super
|
||||
//send_ka(KernelAlert::SysCall);
|
||||
let ret = handle_syscall(
|
||||
usize2sc(frame.regs[10]),
|
||||
frame.regs[11],
|
||||
frame.regs[12],
|
||||
frame.regs[13],
|
||||
frame.regs[14],
|
||||
frame.regs[15],
|
||||
frame.regs[16],
|
||||
);
|
||||
frame.regs[10] = ret;
|
||||
return_pc += 4;
|
||||
}
|
||||
11 => {
|
||||
// mach
|
||||
//send_ka(KernelAlert::SysCall);
|
||||
let ret = handle_syscall(
|
||||
usize2sc(frame.regs[10]),
|
||||
frame.regs[11],
|
||||
frame.regs[12],
|
||||
frame.regs[13],
|
||||
frame.regs[14],
|
||||
frame.regs[15],
|
||||
frame.regs[16],
|
||||
);
|
||||
frame.regs[10] = ret;
|
||||
return_pc += 4;
|
||||
}
|
||||
*/
|
||||
12 => {
|
||||
//send_ka(KernelAlert::IPageFault);
|
||||
return_pc += 4;
|
||||
}
|
||||
13 => {
|
||||
//send_ka(KernelAlert::LPageFault);
|
||||
return_pc += 4;
|
||||
}
|
||||
15 => {
|
||||
//send_ka(KernelAlert::SPageFault);
|
||||
return_pc += 4;
|
||||
}
|
||||
_ => {
|
||||
#[cfg(feature = "debug_messages")]
|
||||
panic!("unhandled sync trap cpu{} -> {}", hart, cause_num);
|
||||
#[cfg(not(feature = "debug_messages"))]
|
||||
{
|
||||
let uart = crate::uart::UART::new(0x1000_0000);
|
||||
uart.put(b'0' + cause_num as u8);
|
||||
rough_panic(['t', 'r', 's'])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if return_pc < 0x8000_0000 {
|
||||
rough_panic(['r', 'a', 'n'])
|
||||
}
|
||||
|
||||
return_pc
|
||||
}
|
165
src/ddi.rs
Normal file
165
src/ddi.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
use ddi::{read_ddi_header, read_ddi_lbos_header, read_ddi_relocation_header, read_ddi_segment_header, DDIType};
|
||||
use crate::rough_panic;
|
||||
use crate::strprint::u32_hex;
|
||||
|
||||
pub enum DDILoadError {
|
||||
InvalidMagic,
|
||||
UnsupportedDDI,
|
||||
UnsupportedDDIVersion,
|
||||
CorruptDDI,
|
||||
OutOfMemory,
|
||||
}
|
||||
|
||||
|
||||
/// takes a ddi file and loads the segments into memory
|
||||
/// returns (entrypoint, first addr, amount of bytes)
|
||||
/// performs relocations
|
||||
pub fn load_ddi(file_contents: &[u8]) -> Result<(usize, usize, usize), DDILoadError> {
|
||||
let header = read_ddi_header(file_contents);
|
||||
if let Some(header) = header {
|
||||
if header.magic != *b"ddI\0" {
|
||||
return Err(DDILoadError::InvalidMagic);
|
||||
}
|
||||
if header.ddi_type != DDIType::LBOSRiscV as u8 {
|
||||
return Err(DDILoadError::UnsupportedDDI);
|
||||
}
|
||||
if header.version != 1 {
|
||||
return Err(DDILoadError::UnsupportedDDIVersion);
|
||||
}
|
||||
let header = read_ddi_lbos_header(file_contents);
|
||||
if let Some(header) = header {
|
||||
// figure out the size
|
||||
let mut total_size = 0;
|
||||
for i in 0..header.segment_header_count as usize {
|
||||
let seg = read_ddi_segment_header(file_contents, &header, i);
|
||||
if let Some(seg) = seg {
|
||||
let size = seg.segment_size as usize;
|
||||
total_size += size;
|
||||
} else {
|
||||
return Err(DDILoadError::CorruptDDI);
|
||||
}
|
||||
if total_size % 512 != 0 {
|
||||
// align up to 512 for the next segment
|
||||
total_size += 512 - (total_size % 512);
|
||||
}
|
||||
}
|
||||
if total_size % 512 != 0 {
|
||||
rough_panic(['d', 'n', 'a' as char]);
|
||||
}
|
||||
// allocate memory
|
||||
let addr = liblbos::syscalls::alloc_blocks(total_size / 512);
|
||||
// copy segments
|
||||
let mut cursor = 0;
|
||||
for i in 0..header.segment_header_count as usize {
|
||||
let seg = read_ddi_segment_header(file_contents, &header, i);
|
||||
if let Some(seg) = seg {
|
||||
let offset = seg.segment_pointer;
|
||||
let slice = &file_contents[offset as usize..offset as usize + seg.segment_size as usize];
|
||||
let target_slice = unsafe { core::slice::from_raw_parts_mut((addr + cursor) as *mut u8, seg.segment_size as usize) };
|
||||
target_slice.copy_from_slice(slice);
|
||||
cursor += seg.segment_size as usize;
|
||||
} else {
|
||||
rough_panic(['d', 'b', 's' as char]);
|
||||
}
|
||||
if cursor % 512 != 0 {
|
||||
// align up cursor for next segment just like above
|
||||
cursor += 512 - (cursor % 512);
|
||||
}
|
||||
}
|
||||
// apply relocations
|
||||
let cursor = 0;
|
||||
for i in 0..header.relocation_header_count as usize {
|
||||
let reloc = read_ddi_relocation_header(file_contents, &header, i);
|
||||
if let Some(reloc) = reloc {
|
||||
let segment = read_ddi_segment_header(file_contents, &header, reloc.relocation_segment as usize);
|
||||
if let Some(segment) = segment {
|
||||
let mut current_segment_base = 0;
|
||||
for i in 0..header.segment_header_count as usize {
|
||||
if i == reloc.relocation_segment as usize {
|
||||
break;
|
||||
}
|
||||
let seg = read_ddi_segment_header(file_contents, &header, i);
|
||||
if let Some(seg) = seg {
|
||||
current_segment_base += seg.segment_size as usize;
|
||||
}
|
||||
if current_segment_base % 512 != 0 {
|
||||
current_segment_base += 512 - (current_segment_base % 512);
|
||||
}
|
||||
}
|
||||
let current_segment_base = current_segment_base + addr;
|
||||
|
||||
let mut target_segment_base = 0;
|
||||
for i in 0..header.segment_header_count as usize {
|
||||
if i == reloc.target_segment as usize {
|
||||
break;
|
||||
}
|
||||
let seg = read_ddi_segment_header(file_contents, &header, i);
|
||||
if let Some(seg) = seg {
|
||||
target_segment_base += seg.segment_size as usize;
|
||||
}
|
||||
if target_segment_base % 512 != 0 {
|
||||
target_segment_base += 512 - (target_segment_base % 512);
|
||||
}
|
||||
}
|
||||
let target_slice = unsafe {
|
||||
core::slice::from_raw_parts_mut((addr + target_segment_base) as *mut u8, segment.segment_size as usize)
|
||||
};
|
||||
|
||||
let target_segment_base = target_segment_base + addr;
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
//let prev_instr = u32::from_le_bytes(target_slice[reloc.relocation_pointer as usize..reloc.relocation_pointer as usize + 4].try_into().unwrap());
|
||||
//liblbos::syscalls::write_terminal(b"prev instruction ");
|
||||
//liblbos::syscalls::write_terminal(&u32_hex(prev_instr));
|
||||
//liblbos::syscalls::write_terminal(b"\n");
|
||||
//if reloc.relocation_type == ddi::arch::riscv32::RiscVRelocationType::CallPLT as u16 || reloc.relocation_type == ddi::arch::riscv32::RiscVRelocationType::Call as u16 {
|
||||
// let prev_instr_2 = u32::from_le_bytes(target_slice[reloc.relocation_pointer as usize + 4..reloc.relocation_pointer as usize + 8].try_into().unwrap());
|
||||
// liblbos::syscalls::write_terminal(&u32_hex(prev_instr_2));
|
||||
// liblbos::syscalls::write_terminal(b"\n");
|
||||
//}
|
||||
//liblbos::syscalls::write_terminal(b"\n");
|
||||
ddi::arch::riscv32::apply_relocation(
|
||||
target_slice,
|
||||
current_segment_base as u32,
|
||||
target_segment_base as u32,
|
||||
&reloc,
|
||||
|x| {
|
||||
liblbos::syscalls::write_terminal(b"unhandled relocation type: ");
|
||||
liblbos::syscalls::write_terminal(&u32_hex(x as u32));
|
||||
liblbos::syscalls::write_terminal(b"\n");
|
||||
}
|
||||
);
|
||||
//let final_instr = u32::from_le_bytes(target_slice[reloc.relocation_pointer as usize..reloc.relocation_pointer as usize + 4].try_into().unwrap());
|
||||
//liblbos::syscalls::write_terminal(b"final instruction ");
|
||||
//liblbos::syscalls::write_terminal(&u32_hex(final_instr));
|
||||
//liblbos::syscalls::write_terminal(b"\n");
|
||||
//if reloc.relocation_type == ddi::arch::riscv32::RiscVRelocationType::CallPLT as u16 || reloc.relocation_type == ddi::arch::riscv32::RiscVRelocationType::Call as u16 {
|
||||
// let prev_instr_2 = u32::from_le_bytes(target_slice[reloc.relocation_pointer as usize + 4..reloc.relocation_pointer as usize + 8].try_into().unwrap());
|
||||
// liblbos::syscalls::write_terminal(&u32_hex(prev_instr_2));
|
||||
// liblbos::syscalls::write_terminal(b"\n");
|
||||
//}
|
||||
//liblbos::syscalls::write_terminal(b"\n");
|
||||
}
|
||||
//liblbos::syscalls::write_terminal(b"relocated segment ");
|
||||
//liblbos::syscalls::write_terminal(&u32_hex(reloc.relocation_segment));
|
||||
//liblbos::syscalls::write_terminal(b" at ");
|
||||
//liblbos::syscalls::write_terminal(&u32_hex(target_segment_base as u32 + reloc.relocation_pointer));
|
||||
//liblbos::syscalls::write_terminal(b"\n");
|
||||
} else {
|
||||
rough_panic(['d', 'r', 's' as char]);
|
||||
}
|
||||
} else {
|
||||
rough_panic(['d', 'b', 'r' as char]);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate entrypoint
|
||||
let entrypoint = header.entrypoint as usize + addr;
|
||||
Ok((entrypoint, addr, total_size))
|
||||
} else {
|
||||
Err(DDILoadError::InvalidMagic)
|
||||
}
|
||||
} else {
|
||||
Err(DDILoadError::InvalidMagic)
|
||||
}
|
||||
}
|
15
src/dev/mod.rs
Normal file
15
src/dev/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use crate::trafficcontrol::TrafficControl;
|
||||
|
||||
#[cfg(feature = "dev_virtio")]
|
||||
pub mod virtio;
|
||||
|
||||
pub fn probe_devices(tc: &mut TrafficControl) {
|
||||
#[cfg(feature = "dev_virtio")]
|
||||
virtio::probe_virtio_devices(tc);
|
||||
}
|
||||
|
||||
pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u64) -> bool {
|
||||
#[cfg(feature = "dev_virtio")]
|
||||
return virtio::read_sector(tc, buffer, size, sector);
|
||||
false
|
||||
}
|
223
src/dev/virtio/block.rs
Normal file
223
src/dev/virtio/block.rs
Normal file
|
@ -0,0 +1,223 @@
|
|||
use crate::dev::virtio::{Descriptor, VIRTIO_DESC_F_NEXT, VIRTIO_DESC_F_WRITE, VIRTIO_MMIO_GUEST_FEATURES, VIRTIO_MMIO_GUEST_PAGE_SIZE, VIRTIO_MMIO_HOST_FEATURES, VIRTIO_MMIO_QUEUE_NOTIFY, VIRTIO_MMIO_QUEUE_NUM, VIRTIO_MMIO_QUEUE_NUM_MAX, VIRTIO_MMIO_QUEUE_PFN, VIRTIO_MMIO_QUEUE_SEL, VIRTIO_MMIO_STATUS, VIRTIO_MMIO_STATUS_ACKNOWLEDGE, VIRTIO_MMIO_STATUS_DRIVER, VIRTIO_MMIO_STATUS_DRIVER_OK, VIRTIO_MMIO_STATUS_FAILED, VIRTIO_MMIO_STATUS_FEATURES_OK, VIRTIO_QUEUE_SIZE, VirtQueue, Used};
|
||||
use crate::trafficcontrol::{TaskWait, TrafficControl};
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _virtio_virtqueue_start();
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct Status {
|
||||
pub status: u8,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Request {
|
||||
// 23 bytes
|
||||
pub blktype: u32,
|
||||
pub reserved: u32,
|
||||
pub sector: u64,
|
||||
pub data: usize,
|
||||
pub status: Status,
|
||||
pub head: u16,
|
||||
|
||||
pub tid: u8,
|
||||
}
|
||||
|
||||
pub struct VirtIoBlockDevice {
|
||||
pub addr: usize,
|
||||
pub queue: usize,
|
||||
pub read_only: bool,
|
||||
pub idx: u16,
|
||||
pub ack_used_idx: u16,
|
||||
}
|
||||
|
||||
pub enum VirtIoBlockDeviceError {
|
||||
FeatureSetMismatch,
|
||||
QueueSetupFailed,
|
||||
}
|
||||
|
||||
impl VirtIoBlockDevice {
|
||||
pub fn new_and_init(
|
||||
tc: &mut TrafficControl,
|
||||
addr: usize,
|
||||
) -> Result<Self, VirtIoBlockDeviceError> {
|
||||
// reset device (write 0 to status)
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(0);
|
||||
}
|
||||
// set ack bit
|
||||
let mut status = VIRTIO_MMIO_STATUS_ACKNOWLEDGE;
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||
}
|
||||
// set driver bit
|
||||
status |= VIRTIO_MMIO_STATUS_DRIVER;
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||
}
|
||||
// read host features
|
||||
let host_features =
|
||||
unsafe { ((addr + VIRTIO_MMIO_HOST_FEATURES) as *const u32).read_volatile() };
|
||||
let guest_features = host_features & !(1 << 5);
|
||||
let read_only = host_features & (1 << 5) != 0;
|
||||
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_GUEST_FEATURES) as *mut u32).write_volatile(guest_features);
|
||||
}
|
||||
|
||||
// set features ok
|
||||
status |= VIRTIO_MMIO_STATUS_FEATURES_OK;
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||
}
|
||||
// make sure features ok is still set, otherwise failed
|
||||
if unsafe { ((addr + VIRTIO_MMIO_STATUS) as *const u32).read_volatile() }
|
||||
& VIRTIO_MMIO_STATUS_FEATURES_OK
|
||||
== 0
|
||||
{
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED);
|
||||
}
|
||||
return Err(VirtIoBlockDeviceError::FeatureSetMismatch);
|
||||
}
|
||||
|
||||
// setup queue
|
||||
let queue_max_by_device =
|
||||
unsafe { ((addr + VIRTIO_MMIO_QUEUE_NUM_MAX) as *const u32).read_volatile() };
|
||||
if queue_max_by_device < VIRTIO_QUEUE_SIZE as _ {
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(VIRTIO_MMIO_STATUS_FAILED);
|
||||
}
|
||||
return Err(VirtIoBlockDeviceError::QueueSetupFailed);
|
||||
}
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_QUEUE_NUM) as *mut u32).write_volatile(VIRTIO_QUEUE_SIZE as _);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_QUEUE_SEL) as *mut u32).write_volatile(0);
|
||||
}
|
||||
let queue_ptr= _virtio_virtqueue_start as usize;
|
||||
unsafe { ((addr + VIRTIO_MMIO_GUEST_PAGE_SIZE) as *mut u32).write_volatile(4096) }; // who knows if this actually works :p
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_QUEUE_PFN) as *mut u32).write_volatile(queue_ptr as u32 / 4096)
|
||||
};
|
||||
|
||||
let block_device = VirtIoBlockDevice {
|
||||
addr,
|
||||
queue: queue_ptr,
|
||||
read_only,
|
||||
idx: 0,
|
||||
ack_used_idx: 0,
|
||||
};
|
||||
status |= VIRTIO_MMIO_STATUS_DRIVER_OK;
|
||||
unsafe {
|
||||
((addr + VIRTIO_MMIO_STATUS) as *mut u32).write_volatile(status);
|
||||
}
|
||||
|
||||
Ok(block_device)
|
||||
}
|
||||
|
||||
pub fn fill_next_descriptor(&mut self, desc: Descriptor) -> u16 {
|
||||
self.idx = (self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
||||
unsafe {
|
||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize] = desc;
|
||||
}
|
||||
if unsafe { (*(self.queue as *mut VirtQueue)).desc[self.idx as usize].flags }
|
||||
& VIRTIO_DESC_F_NEXT
|
||||
!= 0
|
||||
{
|
||||
unsafe {
|
||||
(*(self.queue as *mut VirtQueue)).desc[self.idx as usize].next =
|
||||
(self.idx + 1) % VIRTIO_QUEUE_SIZE as u16;
|
||||
}
|
||||
}
|
||||
self.idx
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn operation(
|
||||
&mut self,
|
||||
tc: &mut TrafficControl,
|
||||
buffer: usize,
|
||||
size: u32,
|
||||
sector: u64,
|
||||
write: bool,
|
||||
) {
|
||||
if self.read_only && write {
|
||||
return;
|
||||
}
|
||||
|
||||
let blk_request = {
|
||||
let block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.alloc_one_block();
|
||||
unsafe { tc.memory_manager.as_mut().unwrap_unchecked().block_to_addr(block) }
|
||||
} as *mut Request;
|
||||
let desc = Descriptor {
|
||||
addr: unsafe { &(*blk_request) as *const _ as u64 },
|
||||
len: size_of::<Request>() as u32,
|
||||
flags: VIRTIO_DESC_F_NEXT,
|
||||
next: 0,
|
||||
};
|
||||
let head = self.fill_next_descriptor(desc);
|
||||
unsafe {
|
||||
(*blk_request).blktype = if write { 1 } else { 0 };
|
||||
}
|
||||
unsafe {
|
||||
(*blk_request).sector = sector;
|
||||
}
|
||||
unsafe {
|
||||
(*blk_request).data = buffer;
|
||||
}
|
||||
unsafe {
|
||||
(*blk_request).status.status = 111;
|
||||
}
|
||||
unsafe {
|
||||
(*blk_request).tid = tc.current as u8;
|
||||
}
|
||||
|
||||
let desc = Descriptor {
|
||||
addr: buffer as u64,
|
||||
len: size,
|
||||
flags: VIRTIO_DESC_F_NEXT | if !write { VIRTIO_DESC_F_WRITE } else { 0 },
|
||||
next: 0,
|
||||
};
|
||||
self.fill_next_descriptor(desc);
|
||||
let desc = Descriptor {
|
||||
addr: unsafe { &(*blk_request).status as *const Status as u64 },
|
||||
len: size_of::<Status>() as u32,
|
||||
flags: VIRTIO_DESC_F_WRITE,
|
||||
next: 0,
|
||||
};
|
||||
self.fill_next_descriptor(desc);
|
||||
unsafe {
|
||||
(*(self.queue as *mut VirtQueue)).avail.ring
|
||||
[(*(self.queue as *mut VirtQueue)).avail.idx as usize % VIRTIO_QUEUE_SIZE] = head;
|
||||
}
|
||||
unsafe {
|
||||
(*(self.queue as *mut VirtQueue)).avail.idx =
|
||||
(*(self.queue as *mut VirtQueue)).avail.idx.wrapping_add(1);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
((self.addr + VIRTIO_MMIO_QUEUE_NOTIFY) as *mut u32).write_volatile(0);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pending(&mut self, tc: &mut TrafficControl) {
|
||||
let queue = unsafe { &(*(self.queue as *mut VirtQueue)) };
|
||||
while self.ack_used_idx != queue.used.idx {
|
||||
let elem = &queue.used.ring[self.ack_used_idx as usize % VIRTIO_QUEUE_SIZE];
|
||||
self.ack_used_idx = self.ack_used_idx.wrapping_add(1);
|
||||
let rq = queue.desc[elem.id as usize].addr as *mut Request;
|
||||
|
||||
let tid = unsafe { (*rq).tid };
|
||||
|
||||
let rq_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked() }.addr_to_block(rq as usize);
|
||||
unsafe { tc.memory_manager.as_mut().unwrap_unchecked().free_one_block(rq_block); }
|
||||
|
||||
// awaken
|
||||
if let Some(Some(task)) = tc.tasks.get_mut(tid as usize) {
|
||||
task.wait &= !(TaskWait::HardBlockDevOperation as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
175
src/dev/virtio/mod.rs
Normal file
175
src/dev/virtio/mod.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
//! virtio devices
|
||||
//! WARNING: virtio is currently completely broken! don't use it!
|
||||
|
||||
use crate::dev::virtio::block::{VirtIoBlockDevice, VirtIoBlockDeviceError};
|
||||
use crate::spinlock::Spinlock;
|
||||
use crate::strprint::twodigit;
|
||||
use crate::trafficcontrol::{TrafficControl};
|
||||
|
||||
mod block;
|
||||
|
||||
pub const VIRTIO_MMIO_START: usize = 0x1000_1000;
|
||||
pub const VIRTIO_MMIO_END: usize = 0x1000_8000;
|
||||
pub const VIRTIO_MMIO_DEVSIZE: usize = 0x1000;
|
||||
pub const VIRTIO_MMIO_DEVCOUNT: usize = ((VIRTIO_MMIO_END - VIRTIO_MMIO_START) / VIRTIO_MMIO_DEVSIZE) + 1;
|
||||
|
||||
pub const VIRTIO_MMIO_STATUS: usize = 0x70;
|
||||
pub const VIRTIO_MMIO_HOST_FEATURES: usize = 0x10;
|
||||
pub const VIRTIO_MMIO_GUEST_FEATURES: usize = 0x20;
|
||||
pub const VIRTIO_MMIO_GUEST_PAGE_SIZE: usize = 0x28;
|
||||
pub const VIRTIO_MMIO_QUEUE_SEL: usize = 0x30;
|
||||
pub const VIRTIO_MMIO_QUEUE_NUM_MAX: usize = 0x34;
|
||||
pub const VIRTIO_MMIO_QUEUE_NUM: usize = 0x38;
|
||||
pub const VIRTIO_MMIO_QUEUE_PFN: usize = 0x40;
|
||||
pub const VIRTIO_MMIO_QUEUE_NOTIFY: usize = 0x50;
|
||||
|
||||
pub const VIRTIO_MMIO_STATUS_ACKNOWLEDGE: u32 = 1 << 0;
|
||||
pub const VIRTIO_MMIO_STATUS_DRIVER: u32 = 1 << 1;
|
||||
pub const VIRTIO_MMIO_STATUS_DRIVER_OK: u32 = 1 << 2;
|
||||
pub const VIRTIO_MMIO_STATUS_FEATURES_OK: u32 = 1 << 3;
|
||||
pub const VIRTIO_MMIO_STATUS_FAILED: u32 = 1 << 7;
|
||||
|
||||
pub const VIRTIO_DESC_F_NEXT: u16 = 1 << 0;
|
||||
pub const VIRTIO_DESC_F_WRITE: u16 = 1 << 1;
|
||||
|
||||
pub const VIRTIO_QUEUE_SIZE: usize = 4;
|
||||
|
||||
pub static VIRTIO_DEVICES: Spinlock<[Option<VirtIoDevice>; VIRTIO_MMIO_DEVCOUNT]> = Spinlock::new([const { None }; VIRTIO_MMIO_DEVCOUNT]);
|
||||
pub static VIRTIO_HARD_BLOCK_DEVICE: Spinlock<Option<u8>> = Spinlock::new(None);
|
||||
|
||||
pub enum VirtIoDevice {
|
||||
BlockDevice(VirtIoBlockDevice),
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Descriptor { // 16 bytes
|
||||
pub addr: u64,
|
||||
pub len: u32,
|
||||
pub flags: u16,
|
||||
pub next: u16,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct Available { // 14 bytes
|
||||
pub flags: u16,
|
||||
pub idx: u16,
|
||||
pub ring: [u16; VIRTIO_QUEUE_SIZE],
|
||||
pub event: u16,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct UsedElem { // 8 bytes
|
||||
pub id: u32,
|
||||
pub len: u32,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct Used { // 38 bytes
|
||||
pub flags: u16,
|
||||
pub idx: u16,
|
||||
pub ring: [UsedElem; VIRTIO_QUEUE_SIZE],
|
||||
pub event: u16,
|
||||
}
|
||||
#[repr(C)]
|
||||
pub struct VirtQueue {
|
||||
pub desc: [Descriptor; VIRTIO_QUEUE_SIZE],
|
||||
pub avail: Available,
|
||||
pub pad: [u8; 4096 - (size_of::<Descriptor>() * VIRTIO_QUEUE_SIZE) - size_of::<Available>()],
|
||||
pub used: Used,
|
||||
}
|
||||
|
||||
pub fn probe_virtio_devices(tc: &mut TrafficControl) {
|
||||
let serial_port = crate::arch::serial_port();
|
||||
let mut devices = VIRTIO_DEVICES.lock();
|
||||
for (i, addr) in (VIRTIO_MMIO_START..=VIRTIO_MMIO_END).step_by(VIRTIO_MMIO_DEVSIZE).enumerate() {
|
||||
let magic = unsafe { (addr as *const u32).read_volatile() };
|
||||
const VIRTIO_MAGIC: u32 = 0x7472_6976;
|
||||
if magic != VIRTIO_MAGIC {
|
||||
continue;
|
||||
}
|
||||
|
||||
let device_type = unsafe { ((addr + 8) as *const u32).read_volatile() };
|
||||
if device_type == 0 {
|
||||
// not connected
|
||||
continue;
|
||||
}
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
match device_type {
|
||||
2 => {
|
||||
// block device
|
||||
let block_device = VirtIoBlockDevice::new_and_init(tc, addr);
|
||||
if let Ok(block_device) = block_device {
|
||||
devices[i] = Some(VirtIoDevice::BlockDevice(block_device));
|
||||
*VIRTIO_HARD_BLOCK_DEVICE.lock() = Some(i as u8);
|
||||
if let Some(serial_port) = &serial_port {
|
||||
serial_port.putstr("virtio block device found\n");
|
||||
}
|
||||
} else if let Err(e) = block_device {
|
||||
match e {
|
||||
VirtIoBlockDeviceError::FeatureSetMismatch => {
|
||||
if let Some(serial_port) = &serial_port {
|
||||
serial_port.putstr("virtio block device feature mismatch\n");
|
||||
}
|
||||
}
|
||||
VirtIoBlockDeviceError::QueueSetupFailed => {
|
||||
if let Some(serial_port) = &serial_port {
|
||||
serial_port.putstr("virtio block device queue setup failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
x => {
|
||||
if let Some(serial_port) = &serial_port {
|
||||
serial_port.putstr("unsupported device type ");
|
||||
serial_port.put_bytes(&twodigit(x as u8));
|
||||
serial_port.putstr("\n");
|
||||
}
|
||||
} // don't handle unsupported devices yet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_interrupt(interrupt: u32, tc: &mut TrafficControl) {
|
||||
let idx = interrupt as usize - 1;
|
||||
let mut devices = VIRTIO_DEVICES.lock();
|
||||
if let Some(Some(dev)) = devices.get_mut(idx) {
|
||||
match dev {
|
||||
VirtIoDevice::BlockDevice(blockdev) => {
|
||||
let hbd = {
|
||||
let lock = VIRTIO_HARD_BLOCK_DEVICE.lock();
|
||||
*lock
|
||||
};
|
||||
if let Some(hbd) = hbd {
|
||||
if hbd as usize == idx {
|
||||
blockdev.pending(tc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_sector(tc: &mut TrafficControl, buffer: usize, size: u32, sector: u64) -> bool {
|
||||
let idx = {
|
||||
let lock = VIRTIO_HARD_BLOCK_DEVICE.lock();
|
||||
*lock
|
||||
};
|
||||
let mut devices = VIRTIO_DEVICES.lock();
|
||||
if let Some(idx) = idx {
|
||||
if let Some(device) = devices[idx as usize].as_mut() {
|
||||
match device {
|
||||
VirtIoDevice::BlockDevice(device) => {
|
||||
device.operation(tc, buffer, size, sector, false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let uart = crate::uart::UART::new(0x1000_0000);
|
||||
uart.putstr("no device")
|
||||
}
|
||||
} else {
|
||||
let uart = crate::uart::UART::new(0x1000_0000);
|
||||
uart.putstr("no idx")
|
||||
}
|
||||
|
||||
false
|
||||
}
|
418
src/fs/fat32.rs
Normal file
418
src/fs/fat32.rs
Normal file
|
@ -0,0 +1,418 @@
|
|||
use crate::fs::{Record, RecordType};
|
||||
|
||||
#[repr(packed(1), C)]
|
||||
pub struct BPB {
|
||||
pub bootjmp: [u8; 3],
|
||||
pub oem_name: [u8; 8],
|
||||
pub bytes_per_sector: u16,
|
||||
pub sectors_per_cluster: u8,
|
||||
pub reserved_sectors: u16,
|
||||
pub num_fats: u8,
|
||||
pub root_entry_count: u16,
|
||||
pub total_sectors_16: u16,
|
||||
pub media_type: u8,
|
||||
pub sectors_per_fat16: u16,
|
||||
pub sectors_per_track: u16,
|
||||
pub head_count: u16,
|
||||
pub hidden_sectors: u32,
|
||||
pub large_sector_count: u32,
|
||||
pub sectors_per_fat: u32,
|
||||
pub flags: u16,
|
||||
pub version: u16,
|
||||
pub root_cluster: u32,
|
||||
pub fsinfo_sector: u16,
|
||||
pub backup_boot_sector: u16,
|
||||
pub reserved0: [u8; 12],
|
||||
pub drive_number: u8,
|
||||
pub reserved1: u8,
|
||||
pub signature: u8,
|
||||
pub serial_number: u32,
|
||||
pub label: [u8; 11],
|
||||
pub identifier: [u8; 8],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BPBUseful {
|
||||
pub oem_name: [u8; 8],
|
||||
pub reserved_sectors: u16,
|
||||
pub num_fats: u8,
|
||||
pub sectors_per_fat_16: u32,
|
||||
pub sectors_per_fat: u32,
|
||||
pub sectors_per_cluster: u8,
|
||||
pub root_cluster: u32,
|
||||
pub padding: [u8; 1],
|
||||
}
|
||||
|
||||
impl BPBUseful {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
oem_name: [0; 8],
|
||||
reserved_sectors: 0,
|
||||
num_fats: 0,
|
||||
sectors_per_fat_16: 0,
|
||||
sectors_per_fat: 0,
|
||||
sectors_per_cluster: 0,
|
||||
root_cluster: 0,
|
||||
padding: [0; 1],
|
||||
}
|
||||
}
|
||||
pub fn fat_size(&self) -> usize {
|
||||
self.sectors_per_fat as usize
|
||||
}
|
||||
pub fn first_data_sector(&self) -> usize {
|
||||
self.reserved_sectors as usize + (self.num_fats as usize * self.fat_size())
|
||||
}
|
||||
|
||||
pub fn first_fat_sector(&self) -> usize {
|
||||
self.reserved_sectors as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(packed(1), C)]
|
||||
pub struct DirectoryEntry {
|
||||
pub name: [u8; 8],
|
||||
pub ext: [u8; 3],
|
||||
pub attributes: u8,
|
||||
pub reserved: u8,
|
||||
pub creation_time_tenth: u8,
|
||||
pub creation_time: u16,
|
||||
pub creation_date: u16,
|
||||
pub last_access_date: u16,
|
||||
pub first_cluster_high: u16,
|
||||
pub last_modified_time: u16,
|
||||
pub last_modified_date: u16,
|
||||
pub first_cluster_low: u16,
|
||||
pub file_size: u32,
|
||||
}
|
||||
|
||||
/*
|
||||
#[repr(packed(1), C)]
|
||||
pub struct LongFileNameEntry {
|
||||
pub order: u8,
|
||||
pub first_five_two_byte_characters: [u16; 5], // [u8; 10]
|
||||
pub attributes: u8, // always 0x0F
|
||||
pub long_entry_type: u8,
|
||||
pub checksum: u8,
|
||||
pub next_six_two_byte_characters: [u16; 6], // [u8; 12]
|
||||
pub reserved: u16,
|
||||
pub final_two_two_byte_characters: [u16; 2], // [u8; 4]
|
||||
pub directory_entry: DirectoryEntry,
|
||||
}
|
||||
*/
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Fat32DirectoryReader {
|
||||
pub underlying: Fat32FileReader,
|
||||
}
|
||||
|
||||
impl Fat32DirectoryReader {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
underlying: Fat32FileReader::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Fat32FileReader {
|
||||
pub cluster: usize,
|
||||
pub sector: usize,
|
||||
pub cluster_idx: usize,
|
||||
pub sector_offset: usize,
|
||||
pub eof: u8,
|
||||
pub implementation: u32,
|
||||
pub padding: [u8; 32 - 24],
|
||||
}
|
||||
|
||||
impl Fat32FileReader {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
cluster: 0,
|
||||
sector: 0,
|
||||
cluster_idx: 0,
|
||||
sector_offset: 0,
|
||||
eof: 0,
|
||||
implementation: 0,
|
||||
padding: [0; 32 - 24],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Fat32Error {
|
||||
CouldNotReadBPB,
|
||||
}
|
||||
|
||||
pub fn read_bpb() -> Result<BPBUseful, Fat32Error> {
|
||||
let bpb_addr = crate::syscalls::alloc_blocks(1);
|
||||
if crate::syscalls::read_hbd(0, bpb_addr, 1) != 0 {
|
||||
return Err(Fat32Error::CouldNotReadBPB);
|
||||
}
|
||||
let bpb = unsafe { (bpb_addr as *const BPB).read_volatile() };
|
||||
crate::syscalls::free_blocks(bpb_addr, 1);
|
||||
Ok(BPBUseful {
|
||||
oem_name: bpb.oem_name,
|
||||
reserved_sectors: bpb.reserved_sectors,
|
||||
num_fats: bpb.num_fats,
|
||||
sectors_per_fat_16: bpb.sectors_per_fat16 as u32,
|
||||
sectors_per_fat: bpb.sectors_per_fat,
|
||||
sectors_per_cluster: bpb.sectors_per_cluster,
|
||||
root_cluster: bpb.root_cluster,
|
||||
padding: [0; 1],
|
||||
})
|
||||
}
|
||||
|
||||
pub fn reader_from_cluster(bpb: &BPBUseful, cluster: usize) -> Fat32FileReader {
|
||||
let first_sector_of_cluster =
|
||||
((cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector();
|
||||
Fat32FileReader {
|
||||
cluster,
|
||||
sector: first_sector_of_cluster,
|
||||
cluster_idx: 0,
|
||||
sector_offset: 0,
|
||||
eof: 0,
|
||||
implementation: 0,
|
||||
padding: [0; 32 - 24],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_reader(bpb: &BPBUseful) -> Fat32DirectoryReader {
|
||||
let cluster = bpb.root_cluster as usize;
|
||||
Fat32DirectoryReader {
|
||||
underlying: reader_from_cluster(bpb, cluster),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns true if the record was read successfully, false if the end of the directory was reached
|
||||
pub fn read_one_record(
|
||||
bpb: &BPBUseful,
|
||||
reader: &mut Fat32DirectoryReader,
|
||||
record: &mut Record,
|
||||
) -> bool {
|
||||
let block_addr = crate::syscalls::alloc_blocks(1);
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(block_addr as *mut u8, 512) };
|
||||
// read without seeking
|
||||
let old_reader = reader.underlying;
|
||||
if !read_file(bpb, &mut reader.underlying, buf) {
|
||||
crate::syscalls::free_blocks(block_addr, 1);
|
||||
crate::syscalls::write_terminal(b"eof on dir");
|
||||
return false;
|
||||
}
|
||||
reader.underlying = old_reader;
|
||||
let mut done = false;
|
||||
while !done {
|
||||
let entry = unsafe {
|
||||
((buf.as_ptr() as usize + reader.underlying.implementation as usize)
|
||||
as *const DirectoryEntry)
|
||||
.read_volatile()
|
||||
};
|
||||
if entry.name[0] == 0x00 {
|
||||
// end of directory
|
||||
crate::syscalls::free_blocks(block_addr, 1);
|
||||
return false;
|
||||
}
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if entry.name[0] != 0xE5 {
|
||||
if entry.attributes != 0x0F {
|
||||
// for now we do not support long filenames
|
||||
let mut namelen = 0;
|
||||
let mut last_was_space = false;
|
||||
for i in 0..8 {
|
||||
if last_was_space {
|
||||
if entry.name[i] != b' ' {
|
||||
last_was_space = false;
|
||||
}
|
||||
} else if entry.name[i] == b' ' {
|
||||
namelen = i;
|
||||
last_was_space = true;
|
||||
}
|
||||
}
|
||||
if namelen == 0 {
|
||||
namelen = 8;
|
||||
}
|
||||
record.name = [0; 12];
|
||||
record.name[..namelen].copy_from_slice(&entry.name[..namelen]);
|
||||
if entry.attributes & 0x10 != 0 {
|
||||
record.record_type = RecordType::Directory as u8;
|
||||
} else {
|
||||
record.record_type = RecordType::File as u8;
|
||||
record.name[namelen] = b'.';
|
||||
record.name[namelen + 1] = entry.ext[0];
|
||||
record.name[namelen + 2] = entry.ext[1];
|
||||
record.name[namelen + 3] = entry.ext[2];
|
||||
}
|
||||
record.target =
|
||||
(entry.first_cluster_high as u32) << 16 | (entry.first_cluster_low as u32);
|
||||
record.total_size_bytes = entry.file_size;
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
reader.underlying.implementation += 32;
|
||||
if reader.underlying.implementation >= 512 {
|
||||
reader.underlying.implementation = 0;
|
||||
seek_forward_one_sector(bpb, &mut reader.underlying);
|
||||
if !read_file(bpb, &mut reader.underlying, buf) {
|
||||
crate::syscalls::free_blocks(block_addr, 1);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::syscalls::free_blocks(block_addr, 1);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn seek_forward_one_sector(bpb: &BPBUseful, reader: &mut Fat32FileReader) -> bool {
|
||||
if reader.eof != 0 {
|
||||
return false;
|
||||
}
|
||||
reader.sector += 1;
|
||||
reader.sector_offset = 0;
|
||||
reader.cluster_idx += 1;
|
||||
// if we reached the end of the cluster, let's consult the FAT to see where to go next
|
||||
if reader.cluster_idx >= bpb.sectors_per_cluster as usize {
|
||||
let fat_addr = crate::syscalls::alloc_blocks(1);
|
||||
let fat_ofs = reader.cluster * 4;
|
||||
let fat_sector = bpb.first_fat_sector() + (fat_ofs / 512);
|
||||
let ent_offset = fat_ofs % 512;
|
||||
if crate::syscalls::read_hbd(fat_sector, fat_addr, 1) != 0 {
|
||||
crate::syscalls::free_blocks(fat_addr, 1);
|
||||
crate::syscalls::write_terminal(b"fatfailure");
|
||||
return false;
|
||||
}
|
||||
let table_value =
|
||||
unsafe { ((fat_addr+(ent_offset)) as *const u32).read_volatile() } & 0x0FFFFFFF;
|
||||
if table_value >= 0x0FFFFFF8 {
|
||||
// no more clusters, whole file has been read
|
||||
crate::syscalls::free_blocks(fat_addr, 1);
|
||||
reader.eof = 1;
|
||||
return false;
|
||||
} else if table_value >= 0x00000002 {
|
||||
reader.cluster = table_value as usize;
|
||||
reader.cluster_idx = 0;
|
||||
reader.sector =
|
||||
((reader.cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector();
|
||||
reader.sector_offset = 0;
|
||||
if table_value == 0x00000000 || table_value == 0x00000001 {
|
||||
crate::syscalls::free_blocks(fat_addr, 1);
|
||||
reader.eof = 1;
|
||||
return false;
|
||||
} else if table_value == 0x0FFFFFF7 {
|
||||
// bad cluster, stop reading
|
||||
crate::syscalls::free_blocks(fat_addr, 1);
|
||||
crate::syscalls::write_terminal(b"badcluster");
|
||||
reader.eof = 1;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// cluster is free, keep going
|
||||
reader.eof = 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// continue reading
|
||||
true
|
||||
}
|
||||
|
||||
pub fn read_file(bpb: &BPBUseful, reader: &mut Fat32FileReader, buffer: &mut [u8]) -> bool {
|
||||
if reader.eof != 0 {
|
||||
return false;
|
||||
}
|
||||
let mut buffer_idx = 0;
|
||||
let sector_addr = crate::syscalls::alloc_blocks(1);
|
||||
loop {
|
||||
if reader.eof != 0 {
|
||||
crate::syscalls::free_blocks(sector_addr, 1);
|
||||
return false;
|
||||
}
|
||||
// read current sector
|
||||
if crate::syscalls::read_hbd(reader.sector, sector_addr, 1) != 0 {
|
||||
crate::syscalls::free_blocks(sector_addr, 1);
|
||||
crate::syscalls::write_terminal(b"badhdb");
|
||||
return false;
|
||||
}
|
||||
let mut left_in_sector = 512 - reader.sector_offset;
|
||||
if left_in_sector == 0 {
|
||||
crate::syscalls::write_terminal(b"SECTORNULL?");
|
||||
return false;
|
||||
}
|
||||
while buffer_idx < buffer.len() && left_in_sector > 0 {
|
||||
buffer[buffer_idx] = unsafe {
|
||||
(sector_addr as *const u8)
|
||||
.add(reader.sector_offset)
|
||||
.read_volatile()
|
||||
};
|
||||
buffer_idx += 1;
|
||||
left_in_sector -= 1;
|
||||
reader.sector_offset += 1;
|
||||
}
|
||||
|
||||
// if sector is full, make sure next read will start at the correct position
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if left_in_sector == 0 {
|
||||
// we don't care about the return value because if it fails, it'll be handled by the next iteration
|
||||
seek_forward_one_sector(bpb, reader);
|
||||
}
|
||||
|
||||
// either buffer got filled, or we need to read the next sector
|
||||
if buffer_idx < buffer.len() {
|
||||
// we must've reached the end of the sector, let's read the next one
|
||||
continue;
|
||||
} else {
|
||||
// buffer got filled, we're done
|
||||
crate::syscalls::free_blocks(sector_addr, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn read_directory(bpb: &BPBUseful, cluster: usize) {
|
||||
let mut first_sector_of_cluster =
|
||||
((cluster - 2) * bpb.sectors_per_cluster as usize) + bpb.first_data_sector();
|
||||
let mut fsoc_addr = crate::syscalls::alloc_blocks(1);
|
||||
if crate::syscalls::read_hbd(first_sector_of_cluster, fsoc_addr, 1) != 0 {
|
||||
crate::syscalls::free_blocks(fsoc_addr, 1);
|
||||
crate::syscalls::write_terminal(b"fsoc");
|
||||
return;
|
||||
}
|
||||
let mut traveled_in_sector = 0;
|
||||
loop {
|
||||
let entry = unsafe { (fsoc_addr as *const DirectoryEntry).read_volatile() };
|
||||
if entry.name[0] == 0x00 {
|
||||
break;
|
||||
}
|
||||
if entry.name[0] != 0xE5 {
|
||||
if entry.attributes == 0x0F {
|
||||
//crate::syscalls::write_terminal(b" unsupported utf16");
|
||||
} else {
|
||||
crate::syscalls::write_terminal(b" - ");
|
||||
let namelen = entry.name.iter().position(|&x| x == 0).unwrap_or(8);
|
||||
crate::syscalls::write_terminal(&entry.name[..namelen]);
|
||||
if entry.attributes & 0x10 != 0 {
|
||||
crate::syscalls::write_terminal(b" (dir)");
|
||||
} else {
|
||||
let extlen = entry.ext.iter().position(|&x| x == 0).unwrap_or(3);
|
||||
crate::syscalls::write_terminal(b".");
|
||||
crate::syscalls::write_terminal(&entry.ext[..extlen]);
|
||||
}
|
||||
crate::syscalls::write_terminal(b"\n");
|
||||
}
|
||||
}
|
||||
|
||||
fsoc_addr += 32;
|
||||
traveled_in_sector += 32;
|
||||
if traveled_in_sector >= 512 {
|
||||
fsoc_addr -= traveled_in_sector;
|
||||
traveled_in_sector = 0;
|
||||
first_sector_of_cluster += 1;
|
||||
if crate::syscalls::read_hbd(first_sector_of_cluster, fsoc_addr, 1) != 0 {
|
||||
crate::syscalls::free_blocks(fsoc_addr, 1);
|
||||
crate::syscalls::write_terminal(b"fsoc");
|
||||
return;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*/
|
140
src/fs/mod.rs
Normal file
140
src/fs/mod.rs
Normal file
|
@ -0,0 +1,140 @@
|
|||
//! file system code
|
||||
//! everything here runs on the KTASK!!!
|
||||
//! there are many things that rely on syscalls, so this code cannot be ran under the kernel
|
||||
|
||||
use liblbos::fs::{Record, RecordType};
|
||||
|
||||
/// ALWAYS 32 BYTES
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
pub type FileSystem = fat32::BPBUseful;
|
||||
pub const _FS_SIZE_CHECK: usize = (size_of::<FileSystem>() == 32) as usize - 1;
|
||||
|
||||
/// ALWAYS 32 BYTES
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
pub type DirectoryReader = fat32::Fat32DirectoryReader;
|
||||
pub const _DIRREADER_SIZE_CHECK: usize = (size_of::<DirectoryReader>() == 32) as usize - 1;
|
||||
|
||||
/// ALWAYS 32 BYTES
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
pub type FileReader = fat32::Fat32FileReader;
|
||||
pub const _FILEREADER_SIZE_CHECK: usize = (size_of::<FileReader>() == 32) as usize - 1;
|
||||
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
pub mod fat32;
|
||||
|
||||
pub fn open_fs(fs: &mut FileSystem) -> bool {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
match fat32::read_bpb() {
|
||||
Ok(f) => {
|
||||
*fs = f;
|
||||
true
|
||||
}
|
||||
Err(e) => {
|
||||
//match e { Fat32Error::CouldNotReadBPB => {} }
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn root_reader(fs: &FileSystem) -> DirectoryReader {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
fat32::root_reader(fs)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn directory_reader(fs: &FileSystem, reader: &mut DirectoryReader, target: u32) {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
let new_reader = fat32::reader_from_cluster(fs, target as usize);
|
||||
*reader = DirectoryReader {
|
||||
underlying: new_reader,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_file(fs: &FileSystem, record: &Record, reader: &mut FileReader) {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
let new_reader = fat32::reader_from_cluster(fs, record.target as usize);
|
||||
*reader = new_reader;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn seek_forward(fs: &FileSystem, reader: &mut FileReader, mut sectors: usize) -> bool {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
while sectors > 0 {
|
||||
if !fat32::seek_forward_one_sector(fs, reader) {
|
||||
return false;
|
||||
}
|
||||
sectors -= 1;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_file(fs: &FileSystem, reader: &mut FileReader, buffer: &mut [u8]) -> usize {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
if fat32::read_file(fs, reader, buffer) {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_one_record(fs: &FileSystem, reader: &mut DirectoryReader, record: &mut Record) -> bool {
|
||||
#[cfg(feature = "fs_fat32")]
|
||||
{
|
||||
fat32::read_one_record(fs, reader, record)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_directory(fs: &FileSystem, path: &mut [u8], reader: &mut DirectoryReader) -> usize {
|
||||
path.make_ascii_uppercase();
|
||||
if path[0] != b'/' {
|
||||
return 3;
|
||||
}
|
||||
let root_reader = root_reader(fs);
|
||||
if path == b"/" {
|
||||
*reader = root_reader;
|
||||
0
|
||||
} else {
|
||||
let mut record = Record {
|
||||
name: [0; 12],
|
||||
record_type: 0,
|
||||
target: 0,
|
||||
total_size_bytes: 0,
|
||||
};
|
||||
let mut current_reader = root_reader;
|
||||
let mut next_path_start = path[1..].iter().position(|&x| x == b'/').unwrap_or(path.len());
|
||||
let mut search_for = &path[1..next_path_start];
|
||||
loop {
|
||||
if !read_one_record(fs, &mut current_reader, &mut record) {
|
||||
return 1;
|
||||
}
|
||||
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
||||
if record_name == search_for {
|
||||
// this is the next record we want
|
||||
if record.record_type == RecordType::Directory as u8 {
|
||||
let new_path_start = next_path_start + 1; // skip the slash
|
||||
if new_path_start >= path.len() {
|
||||
// we're at the end of the path
|
||||
directory_reader(fs, &mut current_reader, record.target);
|
||||
*reader = current_reader;
|
||||
return 0;
|
||||
}
|
||||
next_path_start = path[new_path_start..].iter().position(|&x| x == b'/').unwrap_or(path.len()) + new_path_start;
|
||||
search_for = &path[next_path_start..next_path_start];
|
||||
directory_reader(fs, &mut current_reader, record.target);
|
||||
} else {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
219
src/main.rs
Normal file
219
src/main.rs
Normal file
|
@ -0,0 +1,219 @@
|
|||
#![cfg_attr(feature = "arch_ppc32", feature(asm_experimental_arch))]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
extern crate compiler_builtins;
|
||||
|
||||
use liblbos::ktask::{KTaskFSOpenDirRequest, KTaskFSOpenFileRequest, KTaskFSOpenRequest, KTaskFSReadDirRequest, KTaskFSReadFileRequest, KTaskFSSeek, KTaskLoadDDIFile, KTaskRequest, KTaskRequestType};
|
||||
use liblbos::{ktask, syscalls, TaskSetup};
|
||||
use liblbos::syscalls::{create_task};
|
||||
|
||||
mod arch;
|
||||
mod uart;
|
||||
mod trafficcontrol;
|
||||
mod memory;
|
||||
mod strprint;
|
||||
//mod turntable;
|
||||
mod spinlock;
|
||||
mod dev;
|
||||
mod fs;
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
mod worm;
|
||||
mod ddi;
|
||||
|
||||
pub extern "C" fn ktask() -> ! {
|
||||
fn print(bytes: &[u8]) {
|
||||
syscalls::write_terminal(bytes);
|
||||
}
|
||||
|
||||
print(b"ktask spawned! welcome to lifeblood os!\n");
|
||||
print(b"initializing kernel...\n");
|
||||
syscalls::init_kernel();
|
||||
print(b"kernel ready!\n");
|
||||
|
||||
//let kinfo = kinfo();
|
||||
//print(b"\nkinfo\n");
|
||||
//print(b"task count ");
|
||||
//print(&u32_hex(kinfo.current_process_count as u32));
|
||||
//print(b"\ntotal mem ");
|
||||
//print(&u32_hex(kinfo.total_mem_blocks as u32));
|
||||
//print(b"\nfree mem ");
|
||||
//print(&u32_hex(kinfo.free_mem_blocks as u32));
|
||||
//print(b"\n");
|
||||
|
||||
// load TURNTBL.DDI
|
||||
|
||||
{
|
||||
fn fserr() {
|
||||
print(b"FILESYSTEM ERROR DURING STARTUP ):");
|
||||
rough_panic(['f', 's', 'e']);
|
||||
}
|
||||
|
||||
let mut fs = liblbos::fs::FileSystem::empty();
|
||||
if !fs::open_fs(unsafe { &mut *(&mut fs as *mut _ as *mut _) }) {
|
||||
fserr();
|
||||
}
|
||||
let mut path = [0; 1];
|
||||
path[0] = b'/';
|
||||
let mut dir = liblbos::fs::DirectoryReader::empty();
|
||||
if fs::open_directory(unsafe { &*(&fs as *const _ as *const _) }, path.as_mut(), unsafe { &mut *(&mut dir as *mut _ as *mut _) }) != 0 {
|
||||
fserr();
|
||||
}
|
||||
let mut record = liblbos::fs::Record {
|
||||
name: [0; 12],
|
||||
record_type: 0,
|
||||
target: 0,
|
||||
total_size_bytes: 0,
|
||||
};
|
||||
fn namecmp(record: &liblbos::fs::Record, name: &[u8]) -> bool {
|
||||
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
||||
record_name == name
|
||||
}
|
||||
|
||||
while !namecmp(&record, b"TURNTBL.DDI") {
|
||||
if !fs::read_one_record(
|
||||
unsafe { &*(&fs as *const _ as *const _) },
|
||||
unsafe { &mut *(&mut dir as *mut _ as *mut _) },
|
||||
unsafe { &mut *(&mut record as *mut _ as *mut _) }
|
||||
) {
|
||||
print(b"TURNTBL.DDI NOT FOUND ):");
|
||||
print(b"\n");
|
||||
rough_panic(['t', 't', 'b']);
|
||||
}
|
||||
}
|
||||
|
||||
// found turntable, load it
|
||||
let mut reader = liblbos::fs::FileReader::empty();
|
||||
fs::open_file(
|
||||
unsafe { &*(&fs as *const _ as *const _) },
|
||||
unsafe { &*(&mut record as *mut _ as *mut _) },
|
||||
unsafe { &mut *(&mut reader as *mut _ as *mut _) }
|
||||
);
|
||||
let size = record.total_size_bytes as usize;
|
||||
let buf = syscalls::alloc_blocks(size.div_ceil(512));
|
||||
|
||||
if fs::read_file(
|
||||
unsafe { &*(&fs as *const _ as *const _) },
|
||||
unsafe { &mut *(&mut reader as *mut _ as *mut _) },
|
||||
unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }
|
||||
) != 0 {
|
||||
print(b"TURNTBL.DDI READ ERROR ):");
|
||||
print(b"\n");
|
||||
rough_panic(['f', 'r', 'e']);
|
||||
}
|
||||
|
||||
// load the ddi
|
||||
let file_contents = unsafe { core::slice::from_raw_parts(buf as *const _, size) };
|
||||
if let Ok((entrypoint, first_addr, size)) = ddi::load_ddi(&file_contents) {
|
||||
create_task(TaskSetup {
|
||||
epc: entrypoint,
|
||||
ddi_first_addr: first_addr,
|
||||
ddi_size: size,
|
||||
wait_for_task_exit: false,
|
||||
});
|
||||
} else {
|
||||
print(b"TURNTBL.DDI INVALID DDI ):");
|
||||
print(b"\n");
|
||||
rough_panic(['t', 't', 'b']);
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let req = syscalls::wait_for_notification();
|
||||
let header = unsafe { &*(req as *const KTaskRequest) };
|
||||
let replyto = header.replyto;
|
||||
let reqtype = ktask::request_type_from_req(header.req);
|
||||
match reqtype {
|
||||
KTaskRequestType::DoNothing => {}
|
||||
KTaskRequestType::SayHi => {
|
||||
print(b"hi from lifeblood os ktask!\n");
|
||||
}
|
||||
KTaskRequestType::FSOpen => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenRequest) };
|
||||
if fs::open_fs(unsafe { &mut *(req.fs as *mut _) }) {
|
||||
// success
|
||||
req.base.retval = 0;
|
||||
} else {
|
||||
// failure
|
||||
req.base.retval = 1;
|
||||
}
|
||||
}
|
||||
KTaskRequestType::FSOpenDir => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenDirRequest) };
|
||||
req.base.retval = fs::open_directory(
|
||||
unsafe { &*(req.fs as *mut _) },
|
||||
unsafe { core::slice::from_raw_parts_mut(req.path as *mut _, req.pathlen as usize) },
|
||||
unsafe { &mut *(req.dir as *mut _) }
|
||||
) as u32;
|
||||
}
|
||||
KTaskRequestType::FSReadDir => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadDirRequest) };
|
||||
if fs::read_one_record(
|
||||
unsafe { &*(req.fs as *mut _) },
|
||||
unsafe { &mut *(req.dir as *mut _) },
|
||||
unsafe { &mut *(req.record as *mut _) }
|
||||
) {
|
||||
req.base.retval = 0;
|
||||
} else {
|
||||
req.base.retval = 1;
|
||||
}
|
||||
}
|
||||
KTaskRequestType::FSOpenFile => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSOpenFileRequest) };
|
||||
fs::open_file(
|
||||
unsafe { &*(req.fs as *mut _) },
|
||||
unsafe { &*(req.record as *mut _) },
|
||||
unsafe { &mut *(req.reader as *mut _) }
|
||||
);
|
||||
req.base.retval = 0;
|
||||
}
|
||||
KTaskRequestType::FSReadFile => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSReadFileRequest) };
|
||||
req.base.retval = fs::read_file(
|
||||
unsafe { &*(req.fs as *mut _) },
|
||||
unsafe { &mut *(req.reader as *mut _) },
|
||||
unsafe { core::slice::from_raw_parts_mut(req.buffer, req.bufferlen as usize) }
|
||||
) as u32;
|
||||
}
|
||||
KTaskRequestType::FSSeek => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskFSSeek) };
|
||||
if fs::seek_forward(
|
||||
unsafe { &*(req.fs as *mut _) },
|
||||
unsafe { &mut *(req.reader as *mut _) },
|
||||
req.sectors as usize,
|
||||
) {
|
||||
req.base.retval = 0;
|
||||
} else {
|
||||
req.base.retval = 1;
|
||||
}
|
||||
}
|
||||
KTaskRequestType::LoadDDIFile => {
|
||||
let req = unsafe { &mut *(req as *mut KTaskLoadDDIFile) };
|
||||
let file_buf = unsafe { core::slice::from_raw_parts(req.file_buf_pointer as *const u8, req.file_buf_len) };
|
||||
let task_setup = unsafe { &mut *(req.task_setup_ptr as *mut TaskSetup) };
|
||||
if let Ok((entrypoint, first_addr, size)) = ddi::load_ddi(&file_buf) {
|
||||
task_setup.epc = entrypoint;
|
||||
task_setup.ddi_first_addr = first_addr;
|
||||
task_setup.ddi_size = size;
|
||||
req.base.retval = 0;
|
||||
} else {
|
||||
req.base.retval = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
syscalls::send_notification(replyto, req);
|
||||
syscalls::wait_for_notif_ack(replyto);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rough_panic(errcode: [char; 3]) -> ! {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
arch::virt::virt_rough_panic(errcode)
|
||||
}
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
{
|
||||
panic!("PANIC{}{}{}", errcode[0], errcode[1], errcode[2])
|
||||
}
|
||||
}
|
130
src/memory.rs
Normal file
130
src/memory.rs
Normal file
|
@ -0,0 +1,130 @@
|
|||
use crate::rough_panic;
|
||||
|
||||
#[cfg(feature = "arch_virt")]
|
||||
|
||||
unsafe extern "C" {
|
||||
fn _heap_start();
|
||||
fn _heap_size();
|
||||
}
|
||||
|
||||
pub const BLOCK_SIZE: usize = 512;
|
||||
#[cfg(feature = "arch_virt")]
|
||||
pub const TOTAL_MEMORY: usize = 1048510;
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
pub const TOTAL_MEMORY: usize = 1024 * 1024; // 1MiB;
|
||||
pub const MEM_BLOCKS: usize = TOTAL_MEMORY / BLOCK_SIZE;
|
||||
|
||||
pub struct MemoryManager {
|
||||
pub heap_start: usize,
|
||||
pub heap_size: usize,
|
||||
pub blockmap: [u8; (MEM_BLOCKS+7) / 8],
|
||||
}
|
||||
|
||||
impl MemoryManager {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
pub fn init() -> Self {
|
||||
Self {
|
||||
heap_start: _heap_start as _,
|
||||
heap_size: _heap_size as _,
|
||||
blockmap: [0; (MEM_BLOCKS+7) / 8],
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
pub fn init(heap_start: usize, heap_size: usize) -> Self {
|
||||
Self {
|
||||
heap_start: heap_start as _,
|
||||
heap_size: heap_size as _,
|
||||
blockmap: [0; (MEM_BLOCKS+7) / 8],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn block_to_addr(&self, block: usize) -> usize {
|
||||
block * BLOCK_SIZE + self.heap_start
|
||||
}
|
||||
|
||||
pub fn addr_to_block(&self, addr: usize) -> usize {
|
||||
(addr - self.heap_start) / BLOCK_SIZE
|
||||
}
|
||||
|
||||
pub fn alloc_one_block(&mut self) -> usize {
|
||||
/*
|
||||
for (i, v) in self.blockmap.iter_mut().enumerate() {
|
||||
for j in 0..8 {
|
||||
let block = i * 8 + j;
|
||||
let val = (1 << j) & *v;
|
||||
if val == 0 {
|
||||
// free
|
||||
*v |= 1 << j;
|
||||
return block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rough_panic(['o', 'o', 'm'])
|
||||
*/
|
||||
self.alloc_n_blocks(1)
|
||||
}
|
||||
pub fn free_one_block(&mut self, block: usize) {
|
||||
self.blockmap[block / 8] &= !(1 << (block % 8));
|
||||
}
|
||||
|
||||
// can easily fail if too many blocks are requested, will return 0 on failure
|
||||
pub fn alloc_n_blocks(&mut self, n: usize) -> usize {
|
||||
if n == 0 {
|
||||
return 0;
|
||||
}
|
||||
let mut first_block = None;
|
||||
let mut found = 0;
|
||||
for i in 0..self.blockmap.len() {
|
||||
for j in 0..8 {
|
||||
let block = i * 8 + j;
|
||||
let val = (1 << j) & self.blockmap[i];
|
||||
if val == 0 {
|
||||
// this is free
|
||||
self.blockmap[i] |= 1 << j;
|
||||
if first_block.is_none() {
|
||||
first_block = Some(block);
|
||||
}
|
||||
found += 1;
|
||||
if found >= n {
|
||||
return first_block.unwrap();
|
||||
}
|
||||
} else {
|
||||
// used, restart search
|
||||
let mut i = 0;
|
||||
while found > 0 {
|
||||
found -= 1;
|
||||
self.free_one_block(first_block.unwrap() + i);
|
||||
i += 1;
|
||||
}
|
||||
first_block = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
|
||||
pub fn free_n_blocks(&mut self, block: usize, n: usize) {
|
||||
if n == 0 || block >= MEM_BLOCKS || block + n > MEM_BLOCKS {
|
||||
return;
|
||||
}
|
||||
for i in 0..n {
|
||||
self.free_one_block(block + i);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn used_blocks(&self) -> usize {
|
||||
let mut used_blocks = 0;
|
||||
for v in self.blockmap.iter() {
|
||||
for j in 0..8 {
|
||||
let val = (1 << j) & *v;
|
||||
if val != 0 {
|
||||
// used
|
||||
used_blocks += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
used_blocks
|
||||
}
|
||||
}
|
58
src/spinlock.rs
Normal file
58
src/spinlock.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::sync::atomic::AtomicBool;
|
||||
use crate::rough_panic;
|
||||
|
||||
|
||||
pub struct Spinlock<T> {
|
||||
inner: UnsafeCell<T>,
|
||||
active: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for Spinlock<T> {}
|
||||
|
||||
pub struct SpinlockGuard<'a, T: 'a>(&'a Spinlock<T>);
|
||||
|
||||
impl<'a, T: 'a> Drop for SpinlockGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.active.store(false, core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Deref for SpinlockGuard<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.0.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> DerefMut for SpinlockGuard<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.0.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Spinlock<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner: UnsafeCell::new(inner),
|
||||
active: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> SpinlockGuard<'_, T> {
|
||||
if self.active.swap(true, core::sync::atomic::Ordering::SeqCst) {
|
||||
rough_panic(['s', 'p', 'n'])
|
||||
}
|
||||
SpinlockGuard(self)
|
||||
}
|
||||
|
||||
pub unsafe fn leak(&self) -> usize {
|
||||
self.inner.get() as usize
|
||||
}
|
||||
|
||||
pub fn force_unlock(&self) {
|
||||
self.active.store(false, core::sync::atomic::Ordering::SeqCst);
|
||||
}
|
||||
}
|
21
src/strprint.rs
Normal file
21
src/strprint.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
pub fn twodigit(v: u8) -> [u8; 2] {
|
||||
let tens = v / 10;
|
||||
let ones = v % 10;
|
||||
let tens_c = tens + b'0';
|
||||
let ones_c = ones + b'0';
|
||||
[tens_c, ones_c]
|
||||
}
|
||||
|
||||
pub fn u32_hex(mut v: u32) -> [u8; 8] {
|
||||
let mut buf = [0u8; 8];
|
||||
for i in (0..8).rev() {
|
||||
let num = v & 0b1111;
|
||||
v >>= 4;
|
||||
if num < 10 {
|
||||
buf[i] = b'0' + num as u8;
|
||||
} else {
|
||||
buf[i] = b'A' + (num as u8 - 10);
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
483
src/trafficcontrol.rs
Normal file
483
src/trafficcontrol.rs
Normal file
|
@ -0,0 +1,483 @@
|
|||
use core::sync::atomic::Ordering;
|
||||
use liblbos::TaskSetup;
|
||||
use crate::arch::serial_port;
|
||||
use crate::memory::{BLOCK_SIZE, MemoryManager};
|
||||
use crate::spinlock::Spinlock;
|
||||
use crate::syscalls::SysCall;
|
||||
use crate::{arch};
|
||||
|
||||
#[repr(u32)]
|
||||
pub enum TaskWait {
|
||||
HardBlockDevOperation = 1 << 0,
|
||||
WaitForNotification = 1 << 1,
|
||||
WaitForTaskExit = 1 << 2,
|
||||
}
|
||||
|
||||
pub const MAX_TASKS: usize = 8;
|
||||
|
||||
pub static TC: Spinlock<TrafficControl> = Spinlock::new(TrafficControl::empty());
|
||||
|
||||
pub const INBUF_LEN: usize = 32;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct KernelInfo {
|
||||
pub current_process_count: usize,
|
||||
pub total_mem_blocks: usize,
|
||||
pub free_mem_blocks: usize,
|
||||
pub input_task: u8,
|
||||
pub output_task: u8,
|
||||
}
|
||||
|
||||
/// the bool indicates if the task is now suspended
|
||||
pub fn handle_syscall(
|
||||
sc: SysCall,
|
||||
a1: usize,
|
||||
a2: usize,
|
||||
a3: usize,
|
||||
a4: usize,
|
||||
a5: usize,
|
||||
a6: usize,
|
||||
) -> (usize, bool) {
|
||||
let mut suspend = false;
|
||||
let mut tc = TC.lock();
|
||||
(
|
||||
match sc {
|
||||
SysCall::NoAction => 0,
|
||||
SysCall::KernelInfo => {
|
||||
let ki = a1 as *mut KernelInfo;
|
||||
|
||||
let mut process_count = 0;
|
||||
for ts in &tc.tasks {
|
||||
if ts.is_some() {
|
||||
process_count += 1;
|
||||
}
|
||||
}
|
||||
unsafe { (*ki).current_process_count = process_count };
|
||||
|
||||
let heap_size = unsafe { tc.memory_manager.as_ref().unwrap_unchecked().heap_size };
|
||||
|
||||
unsafe {
|
||||
(*ki).total_mem_blocks = heap_size / BLOCK_SIZE;
|
||||
(*ki).free_mem_blocks = (heap_size / BLOCK_SIZE)
|
||||
- tc.memory_manager.as_ref().unwrap_unchecked().used_blocks();
|
||||
}
|
||||
0
|
||||
}
|
||||
SysCall::CreateTask => {
|
||||
let add = a1 as *mut TaskSetup;
|
||||
|
||||
tc.init_mem_if_not();
|
||||
let blockalloc = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.alloc_n_blocks(16)
|
||||
};
|
||||
let sp = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.block_to_addr(blockalloc)
|
||||
+ BLOCK_SIZE
|
||||
};
|
||||
|
||||
#[cfg(feature = "arch_virt")]
|
||||
let t = Some(Task {
|
||||
epc: unsafe { (*add).epc },
|
||||
want_exit: false,
|
||||
sp,
|
||||
wait: 0,
|
||||
incoming_notifications: [const { None }; MAX_TASKS],
|
||||
ackwait: [0; MAX_TASKS],
|
||||
task_wait: 0,
|
||||
ddi_mem_start_block: unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut().unwrap_unchecked()
|
||||
.addr_to_block((*add).ddi_first_addr)
|
||||
},
|
||||
ddi_mem_blocks_count: unsafe { (*add).ddi_size / BLOCK_SIZE },
|
||||
trap_frame: arch::virt::tasks::setup_task(sp),
|
||||
});
|
||||
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
let t = Some(Task {
|
||||
want_exit: false,
|
||||
sp,
|
||||
wait: 0,
|
||||
incoming_notifications: [const { None }; MAX_TASKS],
|
||||
ackwait: [0; MAX_TASKS],
|
||||
trap_frame: arch::ppc32::user_program_initial_trapframe(
|
||||
unsafe { (*add).epc },
|
||||
sp,
|
||||
)
|
||||
});
|
||||
|
||||
for (i, ts) in tc.tasks.iter_mut().enumerate() {
|
||||
if ts.is_none() {
|
||||
*ts = t;
|
||||
if unsafe { (*add).wait_for_task_exit } {
|
||||
let ci = tc.current;
|
||||
let current_task = tc.tasks[ci].as_mut().unwrap();
|
||||
current_task.wait |= TaskWait::WaitForTaskExit as u32;
|
||||
current_task.task_wait = i as u8;
|
||||
return (i, true);
|
||||
} else {
|
||||
return (i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.free_one_block(blockalloc);
|
||||
}
|
||||
|
||||
MAX_TASKS + 1
|
||||
}
|
||||
SysCall::ExitTask => {
|
||||
let current = tc.current;
|
||||
if let Some(Some(task)) = &mut tc.tasks.get_mut(current) {
|
||||
task.want_exit = true;
|
||||
}
|
||||
|
||||
0
|
||||
}
|
||||
SysCall::CurrentTask => tc.current,
|
||||
SysCall::ReadInbuf => {
|
||||
let buf = unsafe { core::slice::from_raw_parts_mut(a1 as *mut u8, a2) };
|
||||
let mut read = 0;
|
||||
while let Some(c) = tc.read_inbuf() {
|
||||
buf[read] = c;
|
||||
read += 1;
|
||||
if read >= buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
read
|
||||
}
|
||||
SysCall::AllocBlocks => {
|
||||
let count = a1;
|
||||
let block = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.alloc_n_blocks(count)
|
||||
};
|
||||
let addr = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.block_to_addr(block)
|
||||
};
|
||||
addr
|
||||
}
|
||||
SysCall::FreeBlocks => {
|
||||
let addr = a1;
|
||||
let count = a2;
|
||||
let block = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.addr_to_block(addr)
|
||||
};
|
||||
unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.free_n_blocks(block, count)
|
||||
};
|
||||
|
||||
0
|
||||
}
|
||||
SysCall::WriteTerminal => {
|
||||
let addr = a1;
|
||||
let count = a2;
|
||||
if let Some(uart) = serial_port() {
|
||||
uart.put_bytes(unsafe {
|
||||
core::slice::from_raw_parts(addr as *const u8, count)
|
||||
});
|
||||
}
|
||||
0
|
||||
}
|
||||
SysCall::ReadHBD => {
|
||||
let sector = a1;
|
||||
let buf_addr = a2;
|
||||
let count = a3;
|
||||
|
||||
suspend = true;
|
||||
let ci = tc.current;
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
task.wait |= TaskWait::HardBlockDevOperation as u32;
|
||||
}
|
||||
|
||||
if crate::dev::read_sector(&mut tc, buf_addr, count as u32 * 512, sector as u64) {
|
||||
0
|
||||
} else {
|
||||
suspend = false;
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
task.wait &= !(TaskWait::HardBlockDevOperation as u32);
|
||||
}
|
||||
1
|
||||
}
|
||||
}
|
||||
SysCall::InitKernel => {
|
||||
tc.init_mem_if_not();
|
||||
crate::dev::probe_devices(&mut tc);
|
||||
0
|
||||
}
|
||||
SysCall::SendNotification => {
|
||||
let taskid = a1;
|
||||
let addr = a2;
|
||||
let addr_block = unsafe { tc.memory_manager.as_mut().unwrap_unchecked().addr_to_block(addr) };
|
||||
let ci = tc.current;
|
||||
if let Some(Some(task)) = tc.tasks.get_mut(taskid).as_mut() {
|
||||
let waiting = task.wait & TaskWait::WaitForNotification as u32 != 0;
|
||||
if waiting {
|
||||
// they should stop waiting
|
||||
task.wait &= !(TaskWait::WaitForNotification as u32);
|
||||
// setup the task's frame
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
task.trap_frame.regs[10] = addr;
|
||||
}
|
||||
// they preemptively acked, so lets note that down
|
||||
if let Some(current_task) = tc.tasks[ci].as_mut() {
|
||||
current_task.ackwait[taskid] = 1;
|
||||
}
|
||||
} else {
|
||||
task.incoming_notifications[ci] = Some(addr_block); // queue it for them
|
||||
}
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
SysCall::WaitForNotification => {
|
||||
let ci = tc.current;
|
||||
let mut retaddr = 0;
|
||||
suspend = true;
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
// is there already a pending notification?
|
||||
for (i, block) in task.incoming_notifications.iter_mut().enumerate() {
|
||||
if let Some(block) = block.take() {
|
||||
// yes, there is
|
||||
let addr = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.block_to_addr(block as usize)
|
||||
};
|
||||
if let Some(sender_task) = tc.tasks[i].as_mut() {
|
||||
if sender_task.ackwait[ci] == 3 { // they are waiting for us to ack
|
||||
sender_task.ackwait[ci] = 0;
|
||||
} else { // they are not waiting for us to ack, so we need to preemptively ack them
|
||||
sender_task.ackwait[ci] = 1;
|
||||
}
|
||||
}
|
||||
retaddr = addr;
|
||||
suspend = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if suspend {
|
||||
// no, set wait flag and suspend
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
task.wait |= TaskWait::WaitForNotification as u32;
|
||||
}
|
||||
}
|
||||
|
||||
retaddr
|
||||
}
|
||||
SysCall::PendingNotifications => {
|
||||
let ci = tc.current;
|
||||
let mut pending = 0;
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
for block in task.incoming_notifications.iter() {
|
||||
if block.is_some() {
|
||||
pending += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
pending
|
||||
}
|
||||
SysCall::WaitForNotifAck => {
|
||||
let taskid = a1;
|
||||
let ci = tc.current;
|
||||
if let Some(task) = tc.tasks[ci].as_mut() {
|
||||
let ack = task.ackwait[taskid];
|
||||
if ack == 1 {
|
||||
task.ackwait[taskid] = 0;
|
||||
} else {
|
||||
suspend = true;
|
||||
task.ackwait[taskid] = 3;
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
},
|
||||
suspend,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn context_switch<'a>(tc: &'a mut TrafficControl, current: Task) -> Option<&'a Task> {
|
||||
let ci = tc.current;
|
||||
tc.tasks[ci] = Some(current);
|
||||
|
||||
for i in 0..tc.tasks.len() {
|
||||
let want_exit = tc.tasks[i]
|
||||
.as_ref()
|
||||
.map(|task| task.want_exit)
|
||||
.unwrap_or(false);
|
||||
if want_exit {
|
||||
tc.init_mem_if_not();
|
||||
let sp = tc.tasks[i].as_ref().map(|v| v.sp).unwrap_or(0);
|
||||
let ddi_start_block = tc.tasks[i].as_ref().map(|v| v.ddi_mem_start_block).unwrap_or(0);
|
||||
let ddi_blocks_count = tc.tasks[i].as_ref().map(|v| v.ddi_mem_blocks_count).unwrap_or(0);
|
||||
if sp != 0 {
|
||||
let stackblock = unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.addr_to_block(sp - BLOCK_SIZE)
|
||||
};
|
||||
unsafe {
|
||||
tc.memory_manager
|
||||
.as_mut()
|
||||
.unwrap_unchecked()
|
||||
.free_n_blocks(stackblock, 4)
|
||||
};
|
||||
unsafe {
|
||||
tc.memory_manager.as_mut().unwrap_unchecked()
|
||||
.free_n_blocks(ddi_start_block as usize, ddi_blocks_count as usize)
|
||||
}
|
||||
}
|
||||
tc.tasks[i] = None;
|
||||
// check if any tasks are waiting on this one to exit
|
||||
for task in &mut tc.tasks {
|
||||
if let Some(task) = task {
|
||||
if task.wait & TaskWait::WaitForTaskExit as u32 != 0 && task.task_wait == i as u8 {
|
||||
task.wait &= !(TaskWait::WaitForTaskExit as u32);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut next_task = tc.current + 1;
|
||||
if next_task >= MAX_TASKS {
|
||||
next_task = 0;
|
||||
}
|
||||
|
||||
for _ in 0..(MAX_TASKS + 1) {
|
||||
if let Some(task) = tc.tasks[next_task].as_ref() {
|
||||
if task.wait == 0 && {
|
||||
let mut waiting = false;
|
||||
for v in &task.ackwait {
|
||||
if *v == 3 {
|
||||
waiting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
!waiting
|
||||
} {
|
||||
// don't switch to a task that is waiting for something
|
||||
tc.current = next_task;
|
||||
return Some(task);
|
||||
}
|
||||
}
|
||||
next_task += 1;
|
||||
if next_task >= MAX_TASKS {
|
||||
next_task = 0;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub struct TrafficControl {
|
||||
pub memory_manager: Option<MemoryManager>,
|
||||
pub tasks: [Option<Task>; MAX_TASKS],
|
||||
pub first_task_setup: bool,
|
||||
pub current: usize,
|
||||
pub inbuf: [u8; INBUF_LEN],
|
||||
pub inbuf_read: u8,
|
||||
pub inbuf_write: u8,
|
||||
pub hung_system: bool,
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
pub epc: usize,
|
||||
pub want_exit: bool,
|
||||
/// THE ORIGINAL STACK POINTER THAT THE TASK STARTED WITH
|
||||
pub sp: usize,
|
||||
pub wait: u32,
|
||||
pub incoming_notifications: [Option<usize>; MAX_TASKS],
|
||||
pub ackwait: [u8; MAX_TASKS], // 3 = they have not yet received the notification, 1 = they have received the notification, 0 = we know they received the notification, or don't care
|
||||
pub task_wait: u8,
|
||||
pub ddi_mem_start_block: usize,
|
||||
pub ddi_mem_blocks_count: usize,
|
||||
#[cfg(feature = "arch_virt")]
|
||||
pub trap_frame: arch::virt::trap::TrapFrame,
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
pub trap_frame: arch::ppc32::trap::TrapFrame,
|
||||
}
|
||||
|
||||
impl TrafficControl {
|
||||
pub const fn empty() -> Self {
|
||||
TrafficControl {
|
||||
memory_manager: None,
|
||||
tasks: [const { None }; MAX_TASKS],
|
||||
first_task_setup: false,
|
||||
current: MAX_TASKS + 1,
|
||||
inbuf: [0; INBUF_LEN],
|
||||
inbuf_read: 0,
|
||||
inbuf_write: 0,
|
||||
hung_system: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_mem_if_not(&mut self) {
|
||||
if self.memory_manager.is_none() {
|
||||
#[cfg(feature = "arch_virt")]
|
||||
{
|
||||
self.memory_manager = Some(MemoryManager::init())
|
||||
}
|
||||
#[cfg(feature = "arch_ppc32")]
|
||||
{
|
||||
use arch::ppc32::PPC_HEAP_START;
|
||||
self.memory_manager = Some(MemoryManager::init(PPC_HEAP_START.load(Ordering::Relaxed), TOTAL_MEMORY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_inbuf(&mut self, c: u8) {
|
||||
self.inbuf[self.inbuf_write as usize] = c;
|
||||
self.inbuf_write += 1;
|
||||
if self.inbuf_write >= INBUF_LEN as u8 {
|
||||
self.inbuf_write = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_inbuf(&mut self) -> Option<u8> {
|
||||
if self.inbuf_read == self.inbuf_write {
|
||||
return None;
|
||||
}
|
||||
let c = self.inbuf[self.inbuf_read as usize];
|
||||
self.inbuf_read += 1;
|
||||
if self.inbuf_read >= INBUF_LEN as u8 {
|
||||
self.inbuf_read = 0;
|
||||
}
|
||||
Some(c)
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn program_default_exit() -> ! {
|
||||
liblbos::syscalls::exit()
|
||||
}
|
77
src/turntable.rs
Normal file
77
src/turntable.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use liblbos::fs::{DirectoryReader, FileReader, FileSystem, Record, RecordType};
|
||||
use liblbos::ktask;
|
||||
use crate::strprint::twodigit;
|
||||
use crate::syscalls::{kinfo, read_inbuf};
|
||||
use crate::trafficcontrol::{INBUF_LEN};
|
||||
|
||||
pub fn turntable_task() -> ! {
|
||||
print(b"turntable task\n");
|
||||
|
||||
fn print(bytes: &[u8]) {
|
||||
crate::syscalls::write_terminal(bytes);
|
||||
}
|
||||
|
||||
let mut buf: [u8; INBUF_LEN] = [0; INBUF_LEN];
|
||||
loop {
|
||||
let read = read_inbuf(&mut buf);
|
||||
if read > 0 {
|
||||
for c in &buf[0..read] {
|
||||
if *c == b'\r' {
|
||||
let kinfo = kinfo();
|
||||
print(b"\nkinfo\n");
|
||||
print(b"task count ");
|
||||
print(&twodigit(kinfo.current_process_count as u8));
|
||||
print(b"\ntotal mem ");
|
||||
print(&twodigit(kinfo.total_mem_blocks as u8));
|
||||
print(b"\nfree mem ");
|
||||
print(&twodigit(kinfo.free_mem_blocks as u8));
|
||||
print(b"\n");
|
||||
|
||||
let mut fs = FileSystem::empty();
|
||||
if ktask::ktask_fsopen(&mut fs) != 0 {
|
||||
print(b"failed to open fs\n");
|
||||
}
|
||||
let mut dir = DirectoryReader::empty();
|
||||
if ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 {
|
||||
print(b"failed to open dir\n");
|
||||
}
|
||||
let mut record = Record {
|
||||
name: [0; 12],
|
||||
record_type: 0,
|
||||
target: 0,
|
||||
total_size_bytes: 0,
|
||||
};
|
||||
while ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) == 0 {
|
||||
print(b"\n");
|
||||
if record.record_type == 0 {
|
||||
print(b"unexpected eod");
|
||||
break;
|
||||
}
|
||||
print(b" - ");
|
||||
print(&record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)]);
|
||||
if record.record_type == RecordType::Directory as u8 {
|
||||
print(b" (dir)");
|
||||
} else {
|
||||
print(b" (file)");
|
||||
let mut reader = FileReader::empty();
|
||||
let mut buf = [0u8; 16];
|
||||
if ktask::ktask_fsopenfile(&fs, &record, &mut reader) != 0 {
|
||||
print(b" failed to open file\n");
|
||||
continue;
|
||||
}
|
||||
if ktask::ktask_fsreadfile(&fs, &mut reader, &mut buf) != 0 {
|
||||
print(b" failed to read file\n");
|
||||
continue;
|
||||
}
|
||||
print(b"\n (");
|
||||
print(&buf);
|
||||
print(b")\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
print(&[*c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
105
src/uart.rs
Normal file
105
src/uart.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
use core::fmt::Write;
|
||||
|
||||
pub trait Serial {
|
||||
fn put(&self, c: u8);
|
||||
fn putstr(&self, s: &str);
|
||||
fn put_bytes(&self, s: &[u8]);
|
||||
fn get(&self) -> Option<u8>;
|
||||
}
|
||||
|
||||
pub struct UART {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl UART {
|
||||
pub fn init(&self) {
|
||||
let offset = self.offset as *mut u8;
|
||||
unsafe {
|
||||
// word length
|
||||
let lcr = (1 << 0) | (1 << 1);
|
||||
offset.add(3).write_volatile(lcr);
|
||||
|
||||
// enable fifo
|
||||
offset.add(2).write_volatile(1 << 0);
|
||||
// receiver buffer interrupts
|
||||
offset.add(1).write_volatile(1 << 0);
|
||||
|
||||
// divisor boilerplate code, maybe check this later if we want custom baud rate
|
||||
let divisor: u16 = 592;
|
||||
let dlo: u8 = (divisor & 0xff) as u8;
|
||||
let dhi: u8 = (divisor >> 8) as u8;
|
||||
|
||||
offset.add(3).write_volatile(lcr | 1 << 7);
|
||||
offset.add(0).write_volatile(dlo);
|
||||
offset.add(1).write_volatile(dhi);
|
||||
offset.add(3).write_volatile(lcr);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_and_init(offset: usize) -> UART {
|
||||
let uart = UART { offset: offset };
|
||||
uart.init();
|
||||
uart
|
||||
}
|
||||
|
||||
pub const fn new(offset: usize) -> UART {
|
||||
let uart = UART { offset: offset };
|
||||
uart
|
||||
}
|
||||
|
||||
pub fn get(&self) -> Option<u8> {
|
||||
unsafe {
|
||||
if (self.offset as *mut u8).add(5).read_volatile() & 1 == 0 {
|
||||
None
|
||||
} else {
|
||||
Some((self.offset as *mut u8).add(0).read_volatile())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put(&self, c: u8) {
|
||||
unsafe {
|
||||
(self.offset as *mut u8).add(0).write_volatile(c);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn putstr(&self, s: &str) {
|
||||
for c in s.bytes() {
|
||||
self.put(c);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn put_bytes(&self, s: &[u8]) {
|
||||
for c in s {
|
||||
self.put(*c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for UART {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for c in s.bytes() {
|
||||
self.put(c);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Serial for UART {
|
||||
fn put(&self, c: u8) {
|
||||
self.put(c);
|
||||
}
|
||||
|
||||
fn putstr(&self, s: &str) {
|
||||
self.putstr(s);
|
||||
}
|
||||
|
||||
fn put_bytes(&self, s: &[u8]) {
|
||||
self.put_bytes(s);
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<u8> {
|
||||
self.get()
|
||||
}
|
||||
}
|
66
src/worm.rs
Normal file
66
src/worm.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use core::cell::UnsafeCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub struct Worm<T> {
|
||||
inner: UnsafeCell<T>,
|
||||
active: AtomicBool,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for Worm<T> {}
|
||||
|
||||
pub struct WormWriter<'a, T: 'a>(&'a Worm<T>);
|
||||
|
||||
impl<'a, T: 'a> Drop for WormWriter<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
self.0.active.store(false, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> Deref for WormWriter<'a, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.0.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: 'a> DerefMut for WormWriter<'a, T> {
|
||||
fn deref_mut(&mut self) -> &mut T {
|
||||
unsafe { &mut *self.0.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Worm<T> {
|
||||
pub const fn new(inner: T) -> Self {
|
||||
Self {
|
||||
inner: UnsafeCell::new(inner),
|
||||
active: AtomicBool::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn lock(&self) -> WormWriter<'_, T> {
|
||||
if self.active.swap(true, Ordering::Relaxed) {
|
||||
panic!("worm is locked");
|
||||
}
|
||||
WormWriter(self)
|
||||
}
|
||||
|
||||
pub unsafe fn force_unlock(&self) {
|
||||
self.active.store(false, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub fn is_locked(&self) -> bool {
|
||||
self.active.load(Ordering::Relaxed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Worm<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
if self.active.load(Ordering::Relaxed) {
|
||||
panic!("worm is locked");
|
||||
}
|
||||
unsafe { &*self.inner.get() }
|
||||
}
|
||||
}
|
2
turntable/.gitignore
vendored
Normal file
2
turntable/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/target
|
||||
/.idea
|
14
turntable/Cargo.lock
generated
Normal file
14
turntable/Cargo.lock
generated
Normal file
|
@ -0,0 +1,14 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "liblbos"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "turntable"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"liblbos",
|
||||
]
|
25
turntable/Cargo.toml
Normal file
25
turntable/Cargo.toml
Normal file
|
@ -0,0 +1,25 @@
|
|||
[package]
|
||||
name = "turntable"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
liblbos = { path = "../liblbos" }
|
||||
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
opt-level = "z"
|
||||
debug-assertions = false
|
||||
overflow-checks = false
|
||||
lto = true
|
||||
codegen-units = 1
|
||||
|
||||
[features]
|
||||
default = ["arch_riscv32"]
|
||||
arch_riscv32 = ["liblbos/arch_riscv32"]
|
16
turntable/build.rs
Normal file
16
turntable/build.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
fn main() {
|
||||
let arch = std::env::var("LBOS_ARCH").unwrap_or("riscv32".to_string());
|
||||
println!("cargo:rerun-if-env-changed=LBOS_ARCH");
|
||||
println!("cargo:rustc-cfg=feature=\"arch_{}\"", arch);
|
||||
|
||||
println!("cargo:rerun-if-changed=src/arch/{}/asm", arch);
|
||||
|
||||
// specify the linker.ld script
|
||||
println!("cargo:rustc-link-arg=-Tsrc/arch/{arch}/asm/linker.ld");
|
||||
|
||||
// output relocation info
|
||||
println!("cargo:rustc-link-arg=--emit-relocs");
|
||||
|
||||
// don't page align sections
|
||||
println!("cargo:rustc-link-arg=-n");
|
||||
}
|
2
turntable/src/arch/mod.rs
Normal file
2
turntable/src/arch/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
#[cfg(feature = "arch_riscv32")]
|
||||
mod riscv32;
|
34
turntable/src/arch/riscv32/asm/linker.ld
Normal file
34
turntable/src/arch/riscv32/asm/linker.ld
Normal file
|
@ -0,0 +1,34 @@
|
|||
OUTPUT_FORMAT( "elf32-littleriscv" )
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
|
||||
ENTRY( _start )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
program (wxa) : ORIGIN = 0x0, LENGTH = 20480
|
||||
}
|
||||
|
||||
PHDRS
|
||||
{
|
||||
prg PT_LOAD ;
|
||||
}
|
||||
|
||||
SECTIONS {
|
||||
.text : {
|
||||
. = ALIGN(512);
|
||||
*(.text .text.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.data : {
|
||||
*(.sdata .sdata.*) *(.data .data.*)
|
||||
} >program AT>program :prg
|
||||
|
||||
.bss : {
|
||||
*(.sbss .sbss.*)
|
||||
*(.bss .bss.*)
|
||||
} >program AT>program :prg
|
||||
}
|
10
turntable/src/arch/riscv32/mod.rs
Normal file
10
turntable/src/arch/riscv32/mod.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use crate::main;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn _start() -> isize {
|
||||
unsafe {
|
||||
main();
|
||||
}
|
||||
|
||||
0
|
||||
}
|
202
turntable/src/main.rs
Normal file
202
turntable/src/main.rs
Normal file
|
@ -0,0 +1,202 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use crate::terminal::{print, println};
|
||||
use liblbos::fs::{DirectoryReader, FileSystem};
|
||||
|
||||
static TEST: &[u8] = b"test";
|
||||
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
liblbos::syscalls::write_terminal(b"abort\n");
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn lsdir() {
|
||||
let mut fs = FileSystem::empty();
|
||||
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
||||
println("failed to open fs");
|
||||
return;
|
||||
}
|
||||
let mut dir = DirectoryReader::empty();
|
||||
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 {
|
||||
println("failed to open dir");
|
||||
return;
|
||||
}
|
||||
let mut record = liblbos::fs::Record {
|
||||
name: [0; 12],
|
||||
record_type: 0,
|
||||
target: 0,
|
||||
total_size_bytes: 0,
|
||||
};
|
||||
while liblbos::ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) == 0 {
|
||||
print("\n");
|
||||
if record.record_type == 0 {
|
||||
println("unexpected eod");
|
||||
break;
|
||||
}
|
||||
print(" - ");
|
||||
liblbos::syscalls::write_terminal(
|
||||
&record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)],
|
||||
);
|
||||
if record.record_type == liblbos::fs::RecordType::Directory as u8 {
|
||||
print(" (dir)");
|
||||
} else {
|
||||
print(" (file)");
|
||||
}
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
fn attempt_run_file(name: &str) {
|
||||
print("\n");
|
||||
if name.len() > 11 {
|
||||
println("filename too long");
|
||||
return;
|
||||
}
|
||||
|
||||
let mut fs = FileSystem::empty();
|
||||
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
||||
println("failed to open fs");
|
||||
return;
|
||||
}
|
||||
let mut dir = DirectoryReader::empty();
|
||||
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, b"/") != 0 {
|
||||
println("failed to open dir");
|
||||
return;
|
||||
}
|
||||
let mut record = liblbos::fs::Record {
|
||||
name: [0; 12],
|
||||
record_type: 0,
|
||||
target: 0,
|
||||
total_size_bytes: 0,
|
||||
};
|
||||
fn namecmp(record: &liblbos::fs::Record, name: &[u8]) -> bool {
|
||||
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
||||
record_name == name
|
||||
}
|
||||
while !namecmp(&record, name.as_bytes()) {
|
||||
if liblbos::ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) != 0 {
|
||||
println("file not found");
|
||||
return;
|
||||
}
|
||||
if record.record_type == 0 {
|
||||
println("unexpected eod");
|
||||
return;
|
||||
}
|
||||
if record.record_type == liblbos::fs::RecordType::Directory as u8
|
||||
&& namecmp(&record, name.as_bytes())
|
||||
{
|
||||
println("not a file");
|
||||
return;
|
||||
}
|
||||
}
|
||||
// found, load the file
|
||||
let mut reader = liblbos::fs::FileReader::empty();
|
||||
if liblbos::ktask::ktask_fsopenfile(&fs, &record, &mut reader) != 0 {
|
||||
println("failed to open file");
|
||||
return;
|
||||
}
|
||||
let size = record.total_size_bytes as usize;
|
||||
let buf = liblbos::syscalls::alloc_blocks(size.div_ceil(512));
|
||||
if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 {
|
||||
println("failed to read file");
|
||||
}
|
||||
let mut task_setup = liblbos::TaskSetup {
|
||||
epc: 0,
|
||||
ddi_first_addr: 0,
|
||||
ddi_size: 0,
|
||||
wait_for_task_exit: true,
|
||||
};
|
||||
|
||||
if liblbos::ktask::ktask_loadddifile(buf, size, &mut task_setup as *mut _ as usize) != 0 {
|
||||
println("failed to load ddi");
|
||||
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
||||
return;
|
||||
}
|
||||
|
||||
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
||||
|
||||
let task_id = liblbos::syscalls::create_task(task_setup);
|
||||
|
||||
println("\ntask exited\n");
|
||||
}
|
||||
|
||||
fn execute(cmd: &str) {
|
||||
print("\n");
|
||||
let mut split = cmd.split_ascii_whitespace();
|
||||
|
||||
if let Some(cmd) = split.next() {
|
||||
match cmd {
|
||||
"ls" => {
|
||||
lsdir();
|
||||
}
|
||||
_ => {
|
||||
attempt_run_file(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn hex(mut n: u8) -> [u8; 2] {
|
||||
let mut buf = [0u8; 2];
|
||||
for i in (0..2).rev() {
|
||||
let num = n & 0b1111;
|
||||
n >>= 4;
|
||||
if num < 10 {
|
||||
buf[i] = b'0' + num;
|
||||
} else {
|
||||
buf[i] = b'a' + (num - 10);
|
||||
}
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
mod arch;
|
||||
mod terminal;
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn main() {
|
||||
print("\n\n");
|
||||
print("turntable v");
|
||||
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
print(VERSION);
|
||||
print("\n");
|
||||
|
||||
println("(c) 2025 Real Microsoft, LLC");
|
||||
|
||||
print("> ");
|
||||
|
||||
let mut cmdbuf = [0u8; 256];
|
||||
let mut cmdbuf_len = 0;
|
||||
let mut inbuf = [0u8; 8];
|
||||
loop {
|
||||
let read = liblbos::syscalls::read_inbuf(&mut inbuf);
|
||||
if read > 0 {
|
||||
for c in &inbuf[0..read] {
|
||||
if cmdbuf_len >= cmdbuf.len() {
|
||||
print("\x07");
|
||||
} else {
|
||||
// backspace
|
||||
if *c == 0x7f && cmdbuf_len > 0 {
|
||||
cmdbuf_len -= 1;
|
||||
print("\x08 \x08");
|
||||
} else {
|
||||
cmdbuf[cmdbuf_len] = *c;
|
||||
cmdbuf_len += 1;
|
||||
if *c == b'\r' {
|
||||
execute(unsafe {
|
||||
core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len - 1])
|
||||
});
|
||||
cmdbuf_len = 0;
|
||||
print("> ");
|
||||
break;
|
||||
} else {
|
||||
liblbos::syscalls::write_terminal(&[*c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
10
turntable/src/terminal.rs
Normal file
10
turntable/src/terminal.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub fn print(s: &str) {
|
||||
unsafe {
|
||||
liblbos::syscalls::write_terminal(s.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn println(s: &str) {
|
||||
print(s);
|
||||
print("\n");
|
||||
}
|
Loading…
Add table
Reference in a new issue