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