Compare commits

...

118 Commits

Author SHA1 Message Date
alnyan 5ee2e0c5ab strace: rewrite strace with cross 2025-08-11 23:55:55 +03:00
alnyan 9c32c11b0b sysutils: add dummy chroot program 2025-08-02 20:43:21 +03:00
alnyan 3be32b7b8f maint: split peripheral drivers into bsp packages 2025-08-01 10:21:49 +03:00
alnyan 919d6d62ba pinctrl: basic gpio led support 2025-08-01 08:59:24 +03:00
alnyan 062db06473 pinctrl: basic support for gpio 2025-07-31 00:03:45 +03:00
alnyan 06a6e11dab pinctrl: basic pinctrl/pinmux support 2025-07-30 20:43:12 +03:00
alnyan 66b12d7151 jh7110: improve clocks, add generic syscon driver 2025-07-28 14:20:11 +03:00
alnyan f5aa55c3fa device-tree: add bus_size_cells 2025-07-27 14:34:41 +03:00
alnyan 96350f1eaf device-tree: better driver search, missing reports 2025-07-27 14:08:44 +03:00
alnyan 51b72aa4d8 log: reduce message verbosity 2025-07-27 13:43:41 +03:00
alnyan e0b6290a54 clock: implement Hertz type 2025-07-27 13:37:39 +03:00
alnyan 18d01e82c8 jh7110: better clock driver 2025-07-27 13:24:25 +03:00
alnyan 3a61529b24 bcm283x: basic support for mbox + framebuffer 2025-07-26 18:27:20 +03:00
alnyan e873681c21 doc: update docs and dts 2025-07-23 10:47:57 +03:00
alnyan 3683d721c7 colors: add surface resize event 2025-07-20 14:32:08 +03:00
alnyan 8f7ac51fbb colors: update winit usage 2025-07-20 14:31:05 +03:00
alnyan 1bb48a0625 libterm: fix cursor style ctlseq 2025-07-20 14:03:07 +03:00
alnyan c4c79be393 colors: better surface interface 2025-07-20 14:02:27 +03:00
alnyan e0f6be7050 term: move escape handling logic to libterm 2025-07-20 11:41:29 +03:00
alnyan 3e90bd619b netutils/http: implement redirects 2025-07-19 20:01:22 +03:00
alnyan 2da0604391 sysutils/top: add down/up keys 2025-07-19 19:45:39 +03:00
alnyan f3eb88ac19 cross: add terminal cursor position report 2025-07-19 19:45:29 +03:00
alnyan d08a42d5b2 cryptic: reorganize symmetric ciphers 2025-07-19 19:44:11 +03:00
alnyan d2c0f8e3fd rsh: improve symmetric crypto, use aeads instead of aes-cbc 2025-07-19 15:31:56 +03:00
alnyan 6b0d5def50 maint: fix warnings 2025-07-19 09:50:12 +03:00
alnyan dd43135b64 cryptic: fix tlsv1.3 issues, fix missing (dummy) signature algos 2025-07-19 09:50:12 +03:00
alnyan 8a983aea72 x86/rtc: fix incorrect year limit 2025-07-19 09:50:12 +03:00
alnyan bf8e75b86c lib/cryptic: add crypto provider for rustls 2025-07-19 09:50:05 +03:00
alnyan c5d4079900 net/stmmac: sync up code with changes 2025-07-18 09:21:11 +03:00
alnyan f7a8361674 netutils/ping: implement dns queries 2025-07-18 09:20:00 +03:00
alnyan 3ae789a7de shell: fix signals not being delivered to children 2025-07-18 09:19:32 +03:00
alnyan 2f5a614594 net/igbe: support more Intel GbE NICs 2025-07-18 09:18:34 +03:00
alnyan 69fb239dde term: fix terminal not being set for shell process 2025-07-18 09:17:10 +03:00
alnyan 94a1587771 rsh: fix broken aes256cbc, fix incorrect pidfd polling 2025-07-18 09:15:17 +03:00
alnyan 8e699135c6 term: add extended key support (escape seqs) 2025-07-18 09:13:36 +03:00
alnyan ccd1a762c9 term: hosted testing 2025-07-18 09:12:27 +03:00
alnyan 363dc86522 maint: remove i686 target from tools and userspace 2025-07-18 09:07:38 +03:00
alnyan a5526f8001 maint: remove memtables crate 2025-07-17 17:53:53 +03:00
alnyan 0672d55e8e maint: remove etc/ld/x86 2025-07-17 17:50:02 +03:00
alnyan 3b1bdea1dd maint: fix clippy warnings 2025-07-17 17:47:24 +03:00
alnyan b8e7430353 maint: fix warnings 2025-07-17 17:08:59 +03:00
alnyan 511d1e45c0 x86_64: position-independent kernel 2025-07-17 16:45:57 +03:00
alnyan 019146e9ff riscv64: position-independent kernel 2025-07-17 14:38:51 +03:00
alnyan 1f6f091c2c aarch64: position-independent kernel 2025-07-17 12:58:43 +03:00
alnyan 6a723790de maint: drop support for i686 2025-07-16 20:19:40 +03:00
alnyan 223aeef10f boot: fix riscv64/aarch64 boot headers 2025-07-16 16:00:57 +03:00
alnyan 04afd55f35 aarch64: add linux image header 2025-07-16 12:03:16 +03:00
alnyan 21ff433b51 gentables/aarch64: fix incorrect operator precedence 2025-07-16 12:01:54 +03:00
alnyan 2501a85874 runtime: move pidfd support to yggdrasil-rt 2025-07-16 11:00:47 +03:00
alnyan 31e58f961f libc: fix build issues 2025-06-10 15:18:50 +03:00
alnyan 5057555f57 bcm283x: fix missing docs in aux uart 2025-05-20 14:27:20 +03:00
alnyan a88d1af925 bcm283x: fix bcm283x_aux init/traits 2025-05-20 13:20:52 +03:00
alnyan 2fcf800cc8 yboot: pick higher fb resolutions 2025-05-17 11:22:39 +03:00
alnyan 8c4a882766 netutils: add netconf apply subcommand 2025-05-14 13:45:49 +03:00
alnyan be93d68fb1 block: implement mbr partition probing 2025-04-15 12:50:28 +03:00
alnyan be7b895662 maint: update to rustc 1.88.0-nightly 2025-04-14 18:38:24 +03:00
alnyan 608912804f shell: rework shell parsing, if/while/for/&&/|| 2025-03-12 22:11:54 +02:00
alnyan 4798240473 libc: fix missing bits 2025-03-09 21:45:32 +02:00
alnyan c5994dd390 libc: implement program_invocation_[short_]name 2025-03-09 14:42:38 +02:00
alnyan d963b3bac9 libc: borrow relibc's wctype.h 2025-03-09 14:16:42 +02:00
alnyan 7a9a0ce59e libc: extend libc I/O coverage 2025-03-09 11:59:38 +02:00
alnyan 69649f1cea ports: add gnu grep 2025-03-09 01:20:42 +02:00
alnyan 56640a4fc2 ports: add gnu diffutils, patch, gzip 2025-03-09 01:02:15 +02:00
alnyan fc9018585b libc: extend GNU library/tool compatibility 2025-03-08 22:31:50 +02:00
alnyan 87ae150dc1 ports: add lua-5.4.7 port 2025-03-08 02:39:09 +02:00
alnyan cac16c1df9 libc: add L_tmpnam to stdio.h 2025-03-08 02:20:20 +02:00
alnyan 1c07b74e6d ports: add GNU make port 2025-03-08 01:58:43 +02:00
alnyan 8ffc223a2b fs/libc: implement some libc functions, fix file times 2025-03-08 01:22:19 +02:00
alnyan fd0e2cc229 ports: fix rv64 port 2025-03-07 16:00:55 +02:00
alnyan cd6b6ac7f5 maint: update gitignore 2025-03-07 12:11:53 +02:00
alnyan 60bd925122 dyn-loader: add config, better option parsing 2025-03-07 12:10:11 +02:00
alnyan 9f2ad4f2c9 ports: nicer build system for C/C++ ports 2025-03-06 20:41:16 +02:00
alnyan f30cafb3bd user: add a basic NTP client 2025-03-05 17:21:33 +02:00
alnyan fb25e70714 user/proc: fix bug in env passing, more shell env 2025-03-05 15:14:21 +02:00
alnyan be3e72b80e shell: fix script discovery 2025-03-05 14:14:04 +02:00
alnyan c35a61fb7f vfs/user: implement chmod utility 2025-03-05 13:10:51 +02:00
alnyan e330db1e55 user: add md2txt 2025-03-05 11:30:04 +02:00
alnyan 8deeb3ac9e user: reorganize userspace directories 2025-03-05 00:36:51 +02:00
alnyan 3567b79e1d term: more attribute support 2025-03-03 17:53:19 +02:00
alnyan 7485476caa shell/term: ^L to clear screen 2025-03-03 15:24:50 +02:00
alnyan 91d05d352f colors: add window management events 2025-03-03 13:47:06 +02:00
alnyan 8493573721 colors: add workspaces 2025-03-03 13:05:38 +02:00
alnyan c4e3128528 shell: accept unicode input 2025-03-03 00:17:13 +02:00
alnyan 771c553571 term/sysutils: alternate mode, cursor hide/show, top-like utility 2025-03-02 17:27:26 +02:00
alnyan 59b34fb269 sysutils: add thread display to ps 2025-03-02 14:04:29 +02:00
alnyan 6f8fce3388 sysutils: basic ps utility 2025-03-02 13:00:38 +02:00
alnyan 1b2b41406a rv64: fix incorrect relocations produced for rv64 entry code 2025-03-02 12:45:40 +02:00
alnyan 31fa51e64c maint/proc: add /sys/proc + migrate to rustc 1.87.0-nightly 2025-03-02 02:17:27 +02:00
alnyan a45c54faf8 xtask: split run into two functions in cargo.rs 2025-03-02 00:03:23 +02:00
alnyan de98ae1082 sysutils: grep for stdin 2025-03-01 23:52:38 +02:00
alnyan aefa7a93fa x86: fix ps/2 E0-keys not working on some devices 2025-03-01 23:26:22 +02:00
alnyan 3291df4eeb strace: display mutex() calls as well 2025-03-01 21:09:01 +02:00
alnyan c069982ed9 sysutils: ls colors 2025-03-01 18:40:24 +02:00
alnyan dfa74e5c87 term: simple utf8 decoder 2025-03-01 15:23:33 +02:00
alnyan 770021df6a sysutils: basic grep-like utility 2025-03-01 01:37:31 +02:00
alnyan 4a7aa8d831 doc: add MIT license and font attributions 2025-03-01 01:35:16 +02:00
alnyan c4c8b8acc6 term: switch to truetype fonts 2025-03-01 01:20:51 +02:00
alnyan 8c4bdcbe64 pty: make pty buffer blocking 2025-02-28 12:40:14 +02:00
alnyan 99644d335d pty: increase output buffer size 2025-02-28 12:09:06 +02:00
alnyan 03242a0635 proc: implement process tracing 2025-02-27 18:49:20 +02:00
alnyan bbdcfd947a c: add rv64 cmake toolchain file 2025-02-26 19:17:55 +02:00
alnyan e3916868d2 dyn-loader: relocs and entry for rv64 2025-02-26 18:30:43 +02:00
alnyan 3a5a693691 rv64: static libc for riscv64 2025-02-26 17:52:52 +02:00
alnyan 72633eb339 maint: sync up other architectures with mmap(file) 2025-02-26 16:21:56 +02:00
alnyan 43acdb9e13 libc/colors: non-blocking local socket + fixed string bugs in libc 2025-02-26 11:53:08 +02:00
alnyan 7fdc57fd9f proc: implement basic mmap with files, real_program in arg 2025-02-25 17:41:14 +02:00
alnyan d910e8c1a0 user: replace third-party humansize 2025-02-24 14:53:09 +02:00
alnyan 6abea7ef22 libc: implement posix_spawn()/truncate() 2025-02-24 11:05:23 +02:00
alnyan 5d5379ac8a dyn-loader: implement basic dladdr() 2025-02-24 11:00:56 +02:00
alnyan 8e45e48362 mm: bump physical memory limit 2025-02-24 11:00:10 +02:00
alnyan f1a6033f5b vfs: implement file truncation, bump open file limit 2025-02-24 10:59:48 +02:00
alnyan a1ccdf7e76 ext2: support reading from files with holes 2025-02-24 10:58:44 +02:00
alnyan dcc5d56750 user: rework userspace tracing 2025-02-19 22:06:29 +02:00
alnyan 0105be8fea libc: partially sync ygglibc with socket changes 2025-02-18 23:51:07 +02:00
alnyan c2cf314dcd colors: basic bar program 2025-02-18 19:44:17 +02:00
alnyan f605b0a80c colors: rewrite colors, hosted testing support 2025-02-18 11:27:54 +02:00
alnyan 82175f342e debug: avoid debugging deadlocks 2025-02-15 20:18:30 +02:00
alnyan f716c50988 virtio: add virtio-blk, rework virtio queues 2025-02-15 16:41:47 +02:00
790 changed files with 133852 additions and 15849 deletions
+3
View File
@@ -3,3 +3,6 @@
/xtask.toml
/qemu.toml
/etc/boot/yboot.cfg
/disk-*.img
/tmp-*.txt
/*.log
Generated
+96 -48
View File
@@ -532,12 +532,6 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "elf"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
[[package]]
name = "encode_unicode"
version = "1.0.0"
@@ -741,18 +735,6 @@ dependencies = [
"pin-utils",
]
[[package]]
name = "gentables"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"bytemuck",
"clap",
"elf 0.7.4",
"memtables",
"thiserror",
]
[[package]]
name = "getrandom"
version = "0.2.15"
@@ -1119,7 +1101,6 @@ dependencies = [
"cfg-if",
"kernel-arch-aarch64",
"kernel-arch-hosted",
"kernel-arch-i686",
"kernel-arch-interface",
"kernel-arch-riscv64",
"kernel-arch-x86_64",
@@ -1135,7 +1116,6 @@ dependencies = [
"device-api",
"kernel-arch-interface",
"libk-mm-interface",
"memtables",
"static_assertions",
"tock-registers",
"yggdrasil-abi",
@@ -1145,23 +1125,9 @@ dependencies = [
name = "kernel-arch-hosted"
version = "0.1.0"
dependencies = [
"kernel-arch-interface",
"libk-mm-interface",
"yggdrasil-abi",
]
[[package]]
name = "kernel-arch-i686"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"device-api",
"kernel-arch-interface",
"kernel-arch-x86",
"libk-mm-interface",
"log",
"static_assertions",
"tock-registers",
"yggdrasil-abi",
]
@@ -1183,7 +1149,6 @@ dependencies = [
"kernel-arch-interface",
"libk-mm-interface",
"log",
"memtables",
"static_assertions",
"tock-registers",
"yggdrasil-abi",
@@ -1211,7 +1176,6 @@ dependencies = [
"kernel-arch-x86",
"libk-mm-interface",
"log",
"memtables",
"static_assertions",
"tock-registers",
"yggdrasil-abi",
@@ -1269,7 +1233,7 @@ dependencies = [
"cfg-if",
"crossbeam-queue",
"device-api",
"elf 0.7.2",
"elf",
"futures-util",
"kernel-arch",
"libc",
@@ -1291,6 +1255,7 @@ dependencies = [
name = "libk-mm"
version = "0.1.0"
dependencies = [
"async-trait",
"kernel-arch",
"libk-mm-interface",
"libk-util",
@@ -1356,6 +1321,10 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "libutil"
version = "0.1.0"
[[package]]
name = "libyalloc"
version = "0.1.0"
@@ -1459,14 +1428,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memtables"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"bytemuck",
]
[[package]]
name = "miniz_oxide"
version = "0.8.4"
@@ -2677,6 +2638,70 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"kernel-arch-aarch64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"futures-util",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
dependencies = [
"bytemuck",
"device-api",
"device-tree",
"kernel-arch-riscv64",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_fat32"
version = "0.1.0"
@@ -2867,12 +2892,30 @@ dependencies = [
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_blk"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"libk",
"libk-mm",
"libk-util",
"log",
"tock-registers",
"ygg_driver_pci",
"ygg_driver_virtio_core",
"yggdrasil-abi",
]
[[package]]
name = "ygg_driver_virtio_core"
version = "0.1.0"
dependencies = [
"bitflags 2.8.0",
"device-api",
"kernel-arch-hosted",
"libk",
"libk-mm",
"libk-util",
@@ -2886,6 +2929,7 @@ dependencies = [
name = "ygg_driver_virtio_gpu"
version = "0.1.0"
dependencies = [
"async-trait",
"bytemuck",
"device-api",
"libk",
@@ -2904,6 +2948,7 @@ dependencies = [
"bitflags 2.8.0",
"bytemuck",
"device-api",
"futures-util",
"libk",
"libk-mm",
"libk-util",
@@ -2948,13 +2993,12 @@ dependencies = [
"device-api",
"device-api-macros",
"device-tree",
"elf 0.7.2",
"elf",
"ext2",
"futures-util",
"git-version",
"kernel-arch",
"kernel-arch-aarch64",
"kernel-arch-i686",
"kernel-arch-interface",
"kernel-arch-riscv64",
"kernel-arch-x86",
@@ -2964,7 +3008,6 @@ dependencies = [
"libk-util",
"log",
"memfs",
"memtables",
"prettyplease",
"static_assertions",
"tock-registers",
@@ -2972,6 +3015,10 @@ dependencies = [
"yboot-proto",
"ygg_driver_acpi",
"ygg_driver_ahci",
"ygg_driver_bsp_arm",
"ygg_driver_bsp_bcm283x",
"ygg_driver_bsp_jh7110",
"ygg_driver_bsp_riscv",
"ygg_driver_fat32",
"ygg_driver_input",
"ygg_driver_net_core",
@@ -2983,6 +3030,7 @@ dependencies = [
"ygg_driver_pci",
"ygg_driver_usb",
"ygg_driver_usb_xhci",
"ygg_driver_virtio_blk",
"ygg_driver_virtio_gpu",
"ygg_driver_virtio_net",
"yggdrasil-abi",
+2 -3
View File
@@ -11,13 +11,13 @@ exclude = [
]
members = [
"xtask",
"kernel/tools/gentables",
"kernel",
"lib/abi",
"lib/libyalloc",
"lib/runtime",
"lib/qemu",
"lib/abi-serde"
"lib/abi-serde",
"lib/libutil"
]
[workspace.dependencies]
@@ -66,7 +66,6 @@ libk-util.path = "kernel/libk/libk-util"
libk-mm.path = "kernel/libk/libk-mm"
libk-mm-interface.path = "kernel/libk/libk-mm/interface"
libk-device.path = "kernel/libk/libk-device"
memtables.path = "kernel/lib/memtables"
vmalloc.path = "kernel/lib/vmalloc"
device-api-macros.path = "kernel/lib/device-api/macros"
device-tree.path = "kernel/lib/device-tree"
+9
View File
@@ -0,0 +1,9 @@
MIT License
Copyright (c) 2025 Mark Poliakov <mark@alnyan.me>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the " Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+1
View File
@@ -1,3 +1,4 @@
#![allow(unused)]
#![no_std]
use bytemuck::{Pod, Zeroable};
+111 -38
View File
@@ -1,7 +1,8 @@
use core::mem::size_of;
use bytemuck::Zeroable;
use log::{debug, error, info};
use log::{error, info};
use types::{Rela, SHT_RELA};
// TODO use 'elf' crate
use uefi::{
prelude::BootServices,
@@ -15,6 +16,7 @@ use crate::elf::types::{PT_LOAD, SHF_ALLOC, SHF_WRITE, SHT_PROGBITS};
use self::types::{Ehdr, Phdr, Shdr};
#[allow(unused)]
mod types {
use bytemuck::{Pod, Zeroable};
@@ -23,14 +25,18 @@ mod types {
pub type Half = u16;
pub type Word = u32;
pub type XWord = u64;
pub type SXWord = i64;
pub const PT_LOAD: Word = 1;
pub const SHT_PROGBITS: Word = 1;
pub const SHT_RELA: Word = 4;
pub const SHF_WRITE: XWord = 1 << 0;
pub const SHF_ALLOC: XWord = 1 << 1;
pub const R_X86_64_RELATIVE: u32 = 8;
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Ehdr {
@@ -77,6 +83,20 @@ mod types {
pub memsz: XWord,
pub align: XWord,
}
#[derive(Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Rela {
pub offset: Addr,
pub info: XWord,
pub addend: SXWord,
}
impl Rela {
pub fn r_type(&self) -> u32 {
self.info as u32
}
}
}
// Maximum address this loader can map in the target kernel
@@ -92,6 +112,8 @@ pub struct LoadedObject {
pub image_start: u64,
pub image_end: u64,
pub load_address: u64,
pub entry: u64,
pub protocol_struct_paddr: u64,
@@ -105,6 +127,12 @@ struct LocatedProtocol {
size: usize,
}
struct RelaSection {
offset: u64,
entry_count: usize,
entry_size: usize,
}
trait ReadExact {
fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), Error>;
}
@@ -119,6 +147,23 @@ impl ReadExact for RegularFile {
}
}
impl RelaSection {
pub fn from_shdr(shdr: &Shdr) -> Option<Self> {
if shdr.type_ != SHT_RELA {
return None;
}
let entry_size = shdr.entsize as usize;
let entry_count = shdr.size as usize / entry_size;
Some(Self {
offset: shdr.offset,
entry_size,
entry_count,
})
}
}
impl Object {
pub fn open<D: File>(root: &mut D, path: &CStr16) -> Result<Self, Error> {
let file = root.open(path, FileMode::Read, FileAttribute::empty())?;
@@ -140,11 +185,11 @@ impl Object {
return Err(Error::new(Status::LOAD_ERROR, ()));
}
// Check that the entry point is set
if ehdr.entry == 0 {
error!("Image does not have a valid entry point");
return Err(Error::new(Status::LOAD_ERROR, ()));
}
// // Check that the entry point is set
// if ehdr.entry == 0 {
// error!("Image does not have a valid entry point");
// return Err(Error::new(Status::LOAD_ERROR, ()));
// }
Ok(Self { file, ehdr })
}
@@ -170,11 +215,6 @@ impl Object {
self.file
.read_exact(bytemuck::bytes_of_mut(&mut proto_data))?;
info!(
"Kernel is virtually mapped at {:#x}",
proto_data.kernel_virt_offset
);
// 2. Find the kernel's range and check that the loaded physical addresses are actually
// usable from UEFI
let mut image_start = u64::MAX;
@@ -209,16 +249,22 @@ impl Object {
assert_eq!(image_start & 0xFFF, 0);
assert_eq!(image_end & 0xFFF, 0);
info!("Image start: {:#x}, end: {:#x}", image_start, image_end);
// Reserve the kernel memory
let reserved_addr = bs
// Allocate memory to load the kernel into
let kernel_load_address = bs
.allocate_pages(
AllocateType::Address(image_start),
AllocateType::MaxAddress(0xFFFFFFFF),
MemoryType::LOADER_DATA,
(image_end - image_start) as usize / 0x1000,
)
.expect("Could not allocate memory for kernel image");
assert_eq!(reserved_addr, image_start);
.expect("Could not allocate memory for the kernel");
// Print info
info!("Image start: {:#x}, end: {:#x}", image_start, image_end);
info!(
"Kernel virtual offset: {:#x}",
proto_data.kernel_virt_offset
);
info!("Kernel load address: {kernel_load_address:#x}");
// 3. Load the segments
for i in 0..self.ehdr.phnum {
@@ -228,53 +274,80 @@ impl Object {
continue;
}
let segment_load_base = phdr.paddr + kernel_load_address;
info!(
"Load segment {}: {:#x?}",
i,
phdr.paddr..phdr.paddr + phdr.memsz
"[{i}] Load {:#x?}",
segment_load_base..segment_load_base + phdr.memsz
);
if phdr.filesz > 0 {
// The section has load data
let dst = unsafe {
core::slice::from_raw_parts_mut(phdr.paddr as *mut u8, phdr.filesz as usize)
let dst_slice = unsafe {
core::slice::from_raw_parts_mut(
segment_load_base as *mut u8,
phdr.filesz as usize,
)
};
debug!(
"Load {:#x?} from ELF offset {:#x}",
phdr.paddr..phdr.paddr + phdr.filesz,
phdr.offset
);
self.file.set_position(phdr.offset)?;
self.file.read_exact(dst)?;
self.file.read_exact(dst_slice)?;
}
if phdr.memsz > phdr.filesz {
let dst = unsafe {
let dst_slice = unsafe {
core::slice::from_raw_parts_mut(
(phdr.paddr + phdr.filesz) as *mut u8,
(segment_load_base + phdr.filesz) as *mut u8,
(phdr.memsz - phdr.filesz) as usize,
)
};
debug!(
"Zero data {:#x?}",
phdr.paddr + phdr.filesz..phdr.paddr + phdr.memsz
);
dst_slice.fill(0);
}
}
dst.fill(0);
// 4. Perform kernel relocation
let mut rela_section = None;
for i in 0..self.ehdr.shnum as usize {
let shdr = self.read_shdr(i)?;
if let Some(rela) = RelaSection::from_shdr(&shdr) {
rela_section = Some(rela);
break;
}
}
if let Some(rela_section) = rela_section {
info!("Relocating kernel: {image_start:#x} -> {kernel_load_address:#x}");
info!("({} relocations)", rela_section.entry_count);
let b = (kernel_load_address + proto_data.kernel_virt_offset) as i64;
for i in 0..rela_section.entry_count {
let mut rela = Rela::zeroed();
self.file
.set_position(rela_section.offset + (i * rela_section.entry_size) as u64)?;
self.file.read_exact(bytemuck::bytes_of_mut(&mut rela))?;
match rela.r_type() {
types::R_X86_64_RELATIVE => {
let qword = (rela.offset + kernel_load_address) as *mut i64;
let value = rela.addend + b;
unsafe { qword.write_volatile(value) };
}
other => todo!("Unsupported relocation type: {other}"),
}
}
}
// Now that the image is in memory, protocol structure can be written in the further steps
let protocol_struct_paddr = (loc_proto.address as u64) - proto_data.kernel_virt_offset;
let protocol_struct_paddr = loc_proto.address as u64 + kernel_load_address; // (loc_proto.address as u64) - proto_data.kernel_virt_offset;
let protocol_version = proto_data.header.version;
let entry = self.ehdr.entry;
let entry = self.ehdr.entry + kernel_load_address;
Ok(LoadedObject {
image_start,
image_end,
load_address: kernel_load_address,
entry,
protocol_struct_paddr,
protocol_version,
+1 -1
View File
@@ -25,7 +25,7 @@ pub fn load_somewhere(
let file_info: &FileInfo = file.get_info(&mut info_buffer).unwrap();
let size = file_info.file_size();
let page_count = (size + 0xFFF) / 0x1000;
let page_count = size.div_ceil(0x1000);
let base = bs.allocate_pages(
AllocateType::MaxAddress(MAXIMUM_ADDRESS),
+32 -22
View File
@@ -16,7 +16,7 @@ use log::{debug, error, info};
use uefi::{
prelude::*,
proto::{
console::gop::{GraphicsOutput, PixelFormat},
console::gop::{self, GraphicsOutput, PixelFormat},
device_path::DevicePath,
loaded_image::LoadedImage,
media::{file::Directory, fs::SimpleFileSystem},
@@ -32,23 +32,31 @@ use yboot_proto::{
LoadProtocolV1, LOADER_MAGIC,
};
use crate::{mem::MemoryDescriptorExt, protocol_ext::GraphicsOutputExt};
use crate::mem::MemoryDescriptorExt;
fn mode_score(mode: &gop::Mode) -> usize {
let (w, h) = mode.info().resolution();
let mut size_score = w * h;
if w > 1920 || h > 1080 {
// Don't pick too large sizes
size_score = 0;
}
size_score
}
fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<(), Error> {
let gop_handle = bs.get_handle_for_protocol::<GraphicsOutput>()?;
let mut gop = bs.open_protocol_exclusive::<GraphicsOutput>(gop_handle)?;
// Find the requested mode
let mode = gop.match_mode(fb.req_width, fb.req_height).ok_or_else(|| {
error!(
"Requested mode is not supported: {}x{}",
fb.req_width, fb.req_height
);
let mode = gop.modes().max_by_key(mode_score).ok_or_else(|| {
error!("No mode found");
Error::new(Status::INVALID_PARAMETER, ())
})?;
gop.set_mode(&mode)?;
let (res_width, res_height) = mode.info().resolution();
let mut result = gop.frame_buffer();
let format = match mode.info().pixel_format() {
@@ -57,8 +65,8 @@ fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<()
_ => 0,
};
fb.res_width = fb.req_width;
fb.res_height = fb.req_height;
fb.res_width = res_width as _;
fb.res_height = res_height as _;
fb.res_address = result.as_mut_ptr() as _;
fb.res_stride = mode.info().stride() as u64 * 4;
fb.res_size = result.size() as _;
@@ -85,7 +93,7 @@ fn locate_rsdp(st: &SystemTable<Boot>) -> Option<u64> {
fn boot_partition(
image: Handle,
bs: &BootServices,
) -> Result<ScopedProtocol<SimpleFileSystem>, Error> {
) -> Result<ScopedProtocol<'_, SimpleFileSystem>, Error> {
let loaded_image = bs.open_protocol_exclusive::<LoadedImage>(image)?;
let device_handle = loaded_image.device();
@@ -105,7 +113,7 @@ fn load_kernel<'a>(
config: &Config,
root: &mut Directory,
st: &SystemTable<Boot>,
) -> Result<(u64, u64, &'a mut LoadProtocolV1), Error> {
) -> Result<(u64, u64, u64, &'a mut LoadProtocolV1), Error> {
let bs = st.boot_services();
let mut kernel_obj = Object::open(root, cstr16!("kernel.elf"))?;
@@ -175,13 +183,14 @@ fn load_kernel<'a>(
let entry = loaded_obj.entry + proto_data.kernel_virt_offset;
Ok((entry, mmap_memory, proto_data))
Ok((entry, loaded_obj.load_address, mmap_memory, proto_data))
}
unsafe fn map_and_enter_kernel(
st: SystemTable<Boot>,
proto_data: &mut LoadProtocolV1,
mmap_memory: u64,
load_base: u64,
entry: u64,
) -> ! {
let (_, mmap) = st.exit_boot_services();
@@ -208,7 +217,7 @@ unsafe fn map_and_enter_kernel(
let cr3 = mem::map_image();
asm!("cli; wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, options(noreturn, att_syntax));
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, in("ecx") load_base, options(noreturn, att_syntax));
}
#[entry]
@@ -235,15 +244,16 @@ fn efi_main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status
}
};
let (entry, mmap_memory, proto_data) = match load_kernel(&config, &mut root, &system_table) {
Ok(e) => e,
Err(error) => {
error!("Failed to load the kernel/initrd: {error:?}");
return Status::LOAD_ERROR;
}
};
let (entry, load_base, mmap_memory, proto_data) =
match load_kernel(&config, &mut root, &system_table) {
Ok(e) => e,
Err(error) => {
error!("Failed to load the kernel/initrd: {error:?}");
return Status::LOAD_ERROR;
}
};
unsafe {
map_and_enter_kernel(system_table, proto_data, mmap_memory, entry);
map_and_enter_kernel(system_table, proto_data, mmap_memory, load_base, entry);
}
}
+1 -1
View File
@@ -66,7 +66,7 @@ impl MemoryDescriptorExt for MemoryDescriptor {
}
}
pub fn memory_map(bs: &BootServices) -> Result<MemoryMap, Error> {
pub fn memory_map(bs: &BootServices) -> Result<MemoryMap<'_>, Error> {
bs.memory_map(unsafe { &mut MMAP_BUFFER.data })
}
+79 -38
View File
@@ -3,55 +3,96 @@
Booting Yggdrasil on Raspberry Pi 4B with u-boot:
1. Clone u-boot sources to some directory and checkout some
stable branch. I've used v2024.10.
2. Modify cmd/boot.c by replacing the do_go_exec function:
1. Clone u-boot sources and build with rpi_4_defconfig
/* Allow ports to override the default behavior */
__attribute__((weak))
unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
char *const argv[])
{
void *entry_ptr = (void *) entry;
ulong fdt_addr_r = 0;
if (argc >= 2) {
fdt_addr_r = hextoul(argv[1], NULL);
}
void (*func)(ulong) = entry_ptr;
func(fdt_addr_r);
return 0;
}
3. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 rpi_4_defconfig
4. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 -j
5. Copy u-boot.bin into your Pi SD-card's boot partition.
**NOTE** I assume you have all the bootloader parts in the boot partition already.
If not, clone raspberry fw repo and copy the following files to the boot partition:
* bootcode.bin
* start4.elf
* all the .dtb files (a bcm2711-rpi-4-b.dtb should be enough though)
6. config.txt:
2. Copy u-boot.bin into the Pi's boot partition and edit the config.txt:
enable_uart=1
arm64_bit=1
kernel=u-boot.bin
7. Compile the OS with `cargo xtask --arch=aarch64 --board=raspi4b --release`
8. Copy the following files into some directory:
* target/aarch64-unknown-raspi4b/release/yggdrasil-kernel
3. Compile the OS with `cargo xtask --arch=aarch64 --board=raspi4b --release`
4. Copy the following files into some directory:
* target/aarch64-unknown-raspi4b/release/kernel.bin
* userspace/target/aarch64-unknown-yggdrasil/release/initrd.tar
9. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
5. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
10. Connect an ethernet and serial to the Pi and run the following commands in u-boot shell:
6. Connect an ethernet and serial to the Pi and run the following commands in u-boot shell:
tftpboot 0x04000000 <YOUR IP>:initrd.tar
tftpboot ${loadaddr} <YOUR IP>:yggdrasil-kernel
load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
fdt addr ${fdt_addr_r}
fdt resize
fdt memory 0x0 0x3C000000
fdt chosen 0x04000000 <WHATEVER SIZE WAS PRINTED WHEN RUNNING THE FIRST COMMAND>
bootelf -p
go ${kernel_addr_r} ${fdt_addr_r}
### If using DHCP
$ dhcp
### If not using DHCP
$ env set ipaddr <RASPBERRY-IP-ADDR>
$ env set fdt_addr_r 0x11000000
$ env set initrd_addr_r 0x04000000
$ tftpboot ${initrd_addr_r} <BUILD-MACHINE-IP-ADDR>:initrd.tar
$ tftpboot ${loadaddr} <BUILD-MACHINE-IP-ADDR>:kernel.bin
$ load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
$ fdt addr ${fdt_addr_r}
$ fdt resize
$ fdt memory 0x0 0x3C000000
$ booti ${loadaddr} ${initrd_addr_r}:<SIZE-PRINTED-WHEN-LOADING-INITRD> ${fdt_addr_r}
11. Yggdrasil OS should start!
###### Assuming BUILD-MACHINE-IP-ADDR is 13.0.0.1 and RASPBERRY-IP-ADDR is 13.0.0.2, here's
###### a quick command for a development boot
###### (FIXME when initrd gets larger than 64MiB)
env set ipaddr 13.0.0.2; env set fdt_addr_r 0x11000000; env set initrd_addr_r 0x04000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.tar; tftpboot ${loadaddr} 13.0.0.1:kernel.bin; load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:67108864 ${fdt_addr_r}
dhcp;
env set initrd_addr_r 0x20000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb; fdt addr ${fdt_addr_r}; fdt resize; fdt memory 0x0 0x3C000000; booti ${loadaddr} ${initrd_addr_r}:0x4000000 ${fdt_addr_r}
Missing drivers:
No driver for Some("hvs@7e400000") ("brcm,bcm2711-hvs")
No driver for Some("i2c@7e804000") ("brcm,bcm2711-i2c")
also "brcm,bcm2835-i2c"
No driver for Some("usb@7e980000") ("brcm,bcm2835-usb")
No driver for Some("local_intc@40000000") ("brcm,bcm2836-l1-intc")
: avs-monitor@7d5d2000: probed
No driver for Some("thermal") ("brcm,bcm2711-thermal")
No driver for Some("dma@7e007000") ("brcm,bcm2835-dma")
No driver for Some("watchdog@7e100000") ("brcm,bcm2835-pm")
also "brcm,bcm2835-pm-wdt"
No driver for Some("rng@7e104000") ("brcm,bcm2711-rng200")
No driver for Some("pixelvalve@7e206000") ("brcm,bcm2711-pixelvalve0")
No driver for Some("pixelvalve@7e207000") ("brcm,bcm2711-pixelvalve1")
No driver for Some("pixelvalve@7e20a000") ("brcm,bcm2711-pixelvalve2")
No driver for Some("pwm@7e20c800") ("brcm,bcm2835-pwm")
No driver for Some("pixelvalve@7e216000") ("brcm,bcm2711-pixelvalve4")
No driver for Some("clock@7ef00000") ("brcm,brcm2711-dvp")
No driver for Some("interrupt-controller@7ef00100") ("brcm,bcm2711-l2-intc")
also "brcm,l2-intc"
No driver for Some("hdmi@7ef00700") ("brcm,bcm2711-hdmi0")
No driver for Some("i2c@7ef04500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("hdmi@7ef05700") ("brcm,bcm2711-hdmi1")
No driver for Some("i2c@7ef09500") ("brcm,bcm2711-hdmi-i2c")
No driver for Some("firmware") ("raspberrypi,bcm2835-firmware")
also "simple-mfd"
No driver for Some("clocks") ("raspberrypi,firmware-clocks")
No driver for Some("gpio") ("raspberrypi,firmware-gpio")
No driver for Some("reset") ("raspberrypi,firmware-reset")
No driver for Some("power") ("raspberrypi,bcm2835-power")
No driver for Some("mailbox@7e00b840") ("brcm,bcm2835-vchiq")
No driver for Some("phy") ("usb-nop-xceiv")
No driver for Some("gpu") ("brcm,bcm2711-vc5")
No driver for Some("mmc@7e340000") ("brcm,bcm2711-emmc2")
No driver for Some("arm-pmu") ("arm,cortex-a72-pmu")
also "arm,armv8-pmuv3"
No driver for Some("cpu@0") ("arm,cortex-a72")
No driver for Some("cpu@1") ("arm,cortex-a72")
No driver for Some("cpu@2") ("arm,cortex-a72")
No driver for Some("cpu@3") ("arm,cortex-a72")
No driver for Some("pcie@7d500000") ("brcm,bcm2711-pcie")
No driver for Some("ethernet@7d580000") ("brcm,bcm2711-genet-v5")
No driver for Some("mdio@e14") ("brcm,genet-mdio-v5")
No driver for Some("leds") ("gpio-leds")
No driver for Some("wifi-pwrseq") ("mmc-pwrseq-simple")
No driver for Some("sd_io_1v8_reg") ("regulator-gpio")
No driver for Some("sd_vcc_reg") ("regulator-fixed")
+114 -14
View File
@@ -1,7 +1,6 @@
Booting Yggdrasil OS on Starfive VisionFive 2 RISC-V board:
* TODO: proper format for initrd image
* TODO: 0x70000000 can be replaced with a builtin var?
Prerequisites:
@@ -13,18 +12,119 @@ Steps:
1. Copy yggdrasil-kernel.bin and initrd.img into some directory and start a TFTP server there
2. Connect to VF2's serial port, ethernet and enter u-boot
3. Run the following commands:
3. Run the following commands in u-boot:
### If using DHCP
$ dhcp
### If not using DHCP
$ env set ipaddr <VF2-IP-ADDR>
$ env set initrd_addr_r 0x70000000
### [Optional] set some kernel cmdline params
$ env set bootargs "debug.serial-level=info"
$ tftpboot ${initrd_addr_r} <BUILD-MACHINE-IP-ADDR>:initrd.img
$ tftpboot ${loadaddr} <BUILD-MACHINE-IP-ADDR>:yggdrasil-kernel.bin
$ load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}
$ fdt resize
$ booti ${loadaddr} ${initrd_addr_r}:<initrd-size> ${fdt_addr_r}
###### Assuming BUILD-MACHINE-IP-ADDR is 13.0.0.1 and VF2-IP-ADDR is 13.0.0.2, here's
###### a quick command for a development boot
###### (FIXME when initrd gets larger than 64MiB)
env set ipaddr 13.0.0.2; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 13.0.0.1:initrd.img; tftpboot ${loadaddr} 13.0.0.1:yggdrasil-kernel.bin; load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
#### For DHCP boot with BUILD-MACHINE-IP-ADDR 192.168.88.10
# Get an IP address
dhcp
# [Optional] set some kernel cmdline params
setenv bootargs "debug.serial-level=info"
# Load initrd
tftpboot 0x70000000 <your-ip-address>:initrd.img
# Load kernel
tftpboot ${loadaddr} <your-ip-address>:yggdrasil-kernel.bin
# Load dtb
load mmc 1:3 ${fdt_addr_r} dtbs/...-starfive/starfive/${fdtfile}
fdt resize
# Enter the kernel
booti ${loadaddr} 0x70000000:<initrd-size> ${fdt_addr_r}
dhcp; env set initrd_addr_r 0x70000000; tftpboot ${initrd_addr_r} 192.168.88.10:initrd.img; tftpboot ${loadaddr} 192.168.88.10:yggdrasil-kernel.bin; load mmc 1:3 ${fdt_addr_r} dtbs/6.6.20-starfive/starfive/${fdtfile}; fdt resize; booti ${loadaddr} ${initrd_addr_r}:60000000 ${fdt_addr_r}
Missing drivers:
Clock/reset/pin:
No driver for Some("pinctrl@17020000") ("starfive,jh7110-aon-pinctrl")
No driver for Some("clock-controller@19810000") ("starfive,jh7110-ispcrg")
No driver for Some("clock-controller@295c0000") ("starfive,jh7110-voutcrg")
Power/reg/GPIO:
No driver for Some("opp-table-0") ("operating-points-v2")
No driver for Some("pmic@36") ("x-powers,axp15060")
No driver for Some("power-controller@17030000") ("starfive,jh7110-pmu")
No driver for Some("leds") ("gpio-leds")
No driver for Some("gpio-restart") ("gpio-restart")
Serial:
No driver for Some("i2c@10030000") ("snps,designware-i2c")
No driver for Some("i2c@10050000") ("snps,designware-i2c")
No driver for Some("i2c@12050000") ("snps,designware-i2c")
No driver for Some("i2c@12060000") ("snps,designware-i2c")
No driver for Some("spi@10060000") ("arm,pl022")
also "arm,primecell"
Bus:
No driver for Some("usb@10100000") ("starfive,jh7110-usb")
No driver for Some("usb@0") ("cdns,usb3")
No driver for Some("phy@10200000") ("starfive,jh7110-usb-phy")
No driver for Some("phy@10210000") ("starfive,jh7110-pcie-phy")
No driver for Some("phy@10220000") ("starfive,jh7110-pcie-phy")
No driver for Some("pcie@940000000") ("starfive,jh7110-pcie")
No driver for Some("pcie@9c0000000") ("starfive,jh7110-pcie")
Interrupt:
No driver for Some("interrupt-controller") ("riscv,cpu-intc")
No driver for Some("timer@2000000") ("starfive,jh7110-clint")
also "sifive,clint0"
Display/GPU subsystem:
No driver for Some("display-subsystem") ("starfive,jh7110-display")
also "verisilicon,display-subsystem"
No driver for Some("dsi-output") ("starfive,jh7110-display-encoder")
also "verisilicon,dsi-encoder"
No driver for Some("jpu@13090000") ("starfive,jpu")
No driver for Some("vpu_dec@130a0000") ("starfive,vdec")
No driver for Some("vpu_enc@130b0000") ("starfive,venc")
No driver for Some("gpu@18000000") ("img-gpu")
No driver for Some("vin_sysctl@19800000") ("starfive,jh7110-vin")
No driver for Some("phy@19820000") ("starfive,jh7110-dphy-rx")
No driver for Some("dc8200@29400000") ("starfive,jh7110-dc8200")
also "verisilicon,dc8200"
No driver for Some("hdmi@29590000") ("starfive,jh7110-hdmi")
also "inno,hdmi"
No driver for Some("mipi@295d0000") ("starfive,jh7110-mipi_dsi")
also "cdns,dsi"
No driver for Some("mipi-dphy@295e0000") ("starfive,jh7110-mipi-dphy-tx")
also "m31,mipi-dphy-tx"
Misc:
No driver for Some("mailbox_client") ("starfive,mailbox-test")
No driver for Some("cache-controller@2010000") ("starfive,jh7110-ccache")
also "sifive,ccache0"
also "cache"
No driver for Some("pwm@120d0000") ("starfive,jh7110-pwm")
also "opencores,pwm-v1"
No driver for Some("temperature-sensor@120e0000") ("starfive,jh7110-temp")
No driver for Some("timer@13050000") ("starfive,jh7110-timer")
No driver for Some("mailbox@13060000") ("starfive,mail_box")
No driver for Some("watchdog@13070000") ("starfive,jh7110-wdt")
No driver for Some("crypto@16000000") ("starfive,jh7110-crypto")
No driver for Some("rng@1600c000") ("starfive,jh7110-trng")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("mdio") ("snps,dwmac-mdio")
No driver for Some("dma-controller@16050000") ("starfive,jh7110-axi-dma")
No driver for Some("dma-controller@16008000") ("arm,pl080")
also "arm,primecell"
No driver for Some("rtc@17040000") ("starfive,jh7110-rtc")
No driver for Some("e24@6e210000") ("starfive,e24")
No driver for Some("linux,cma") ("shared-dma-pool")
Storage:
No driver for Some("spi@13010000") ("starfive,jh7110-qspi")
also "cdns,qspi-nor"
No driver for Some("flash@0") ("jedec,spi-nor")
No driver for Some("partitions") ("fixed-partitions")
No driver for Some("mmc@16010000") ("starfive,jh7110-mmc")
No driver for Some("mmc@16020000") ("starfive,jh7110-mmc")
Audio:
No driver for Some("pwmdac@100b0000") ("starfive,jh7110-pwmdac")
No driver for Some("i2s@120b0000") ("starfive,jh7110-i2stx0")
+2 -1
View File
@@ -3,7 +3,7 @@
"os": "none",
"abi": "softfloat",
"llvm-target": "aarch64-unknown-none",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
"max-atomic-width": 128,
"target-pointer-width": "64",
"features": "+v8a,+strict-align,-neon,-fp-armv8",
@@ -13,6 +13,7 @@
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"eh-frame-header": false,
"linker": "rust-lld",
Binary file not shown.
+433
View File
@@ -0,0 +1,433 @@
/dts-v1/;
/ {
interrupt-parent = <0x8005>;
dma-coherent;
model = "linux,dummy-virt";
#size-cells = <0x02>;
#address-cells = <0x02>;
compatible = "linux,dummy-virt";
psci {
migrate = <0xc4000005>;
cpu_on = <0xc4000003>;
cpu_off = <0x84000002>;
cpu_suspend = <0xc4000001>;
method = "smc";
compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";
};
memory@40000000 {
reg = <0x00 0x40000000 0x00 0x20000000>;
device_type = "memory";
};
platform-bus@c000000 {
interrupt-parent = <0x8005>;
ranges = <0x00 0x00 0xc000000 0x2000000>;
#address-cells = <0x01>;
#size-cells = <0x01>;
compatible = "qemu,platform", "simple-bus";
};
fw-cfg@9020000 {
dma-coherent;
reg = <0x00 0x9020000 0x00 0x18>;
compatible = "qemu,fw-cfg-mmio";
};
virtio_mmio@a000000 {
dma-coherent;
interrupts = <0x00 0x10 0x01>;
reg = <0x00 0xa000000 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000200 {
dma-coherent;
interrupts = <0x00 0x11 0x01>;
reg = <0x00 0xa000200 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000400 {
dma-coherent;
interrupts = <0x00 0x12 0x01>;
reg = <0x00 0xa000400 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000600 {
dma-coherent;
interrupts = <0x00 0x13 0x01>;
reg = <0x00 0xa000600 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000800 {
dma-coherent;
interrupts = <0x00 0x14 0x01>;
reg = <0x00 0xa000800 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000a00 {
dma-coherent;
interrupts = <0x00 0x15 0x01>;
reg = <0x00 0xa000a00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000c00 {
dma-coherent;
interrupts = <0x00 0x16 0x01>;
reg = <0x00 0xa000c00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a000e00 {
dma-coherent;
interrupts = <0x00 0x17 0x01>;
reg = <0x00 0xa000e00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001000 {
dma-coherent;
interrupts = <0x00 0x18 0x01>;
reg = <0x00 0xa001000 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001200 {
dma-coherent;
interrupts = <0x00 0x19 0x01>;
reg = <0x00 0xa001200 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001400 {
dma-coherent;
interrupts = <0x00 0x1a 0x01>;
reg = <0x00 0xa001400 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001600 {
dma-coherent;
interrupts = <0x00 0x1b 0x01>;
reg = <0x00 0xa001600 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001800 {
dma-coherent;
interrupts = <0x00 0x1c 0x01>;
reg = <0x00 0xa001800 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001a00 {
dma-coherent;
interrupts = <0x00 0x1d 0x01>;
reg = <0x00 0xa001a00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001c00 {
dma-coherent;
interrupts = <0x00 0x1e 0x01>;
reg = <0x00 0xa001c00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a001e00 {
dma-coherent;
interrupts = <0x00 0x1f 0x01>;
reg = <0x00 0xa001e00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002000 {
dma-coherent;
interrupts = <0x00 0x20 0x01>;
reg = <0x00 0xa002000 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002200 {
dma-coherent;
interrupts = <0x00 0x21 0x01>;
reg = <0x00 0xa002200 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002400 {
dma-coherent;
interrupts = <0x00 0x22 0x01>;
reg = <0x00 0xa002400 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002600 {
dma-coherent;
interrupts = <0x00 0x23 0x01>;
reg = <0x00 0xa002600 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002800 {
dma-coherent;
interrupts = <0x00 0x24 0x01>;
reg = <0x00 0xa002800 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002a00 {
dma-coherent;
interrupts = <0x00 0x25 0x01>;
reg = <0x00 0xa002a00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002c00 {
dma-coherent;
interrupts = <0x00 0x26 0x01>;
reg = <0x00 0xa002c00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a002e00 {
dma-coherent;
interrupts = <0x00 0x27 0x01>;
reg = <0x00 0xa002e00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003000 {
dma-coherent;
interrupts = <0x00 0x28 0x01>;
reg = <0x00 0xa003000 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003200 {
dma-coherent;
interrupts = <0x00 0x29 0x01>;
reg = <0x00 0xa003200 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003400 {
dma-coherent;
interrupts = <0x00 0x2a 0x01>;
reg = <0x00 0xa003400 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003600 {
dma-coherent;
interrupts = <0x00 0x2b 0x01>;
reg = <0x00 0xa003600 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003800 {
dma-coherent;
interrupts = <0x00 0x2c 0x01>;
reg = <0x00 0xa003800 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003a00 {
dma-coherent;
interrupts = <0x00 0x2d 0x01>;
reg = <0x00 0xa003a00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003c00 {
dma-coherent;
interrupts = <0x00 0x2e 0x01>;
reg = <0x00 0xa003c00 0x00 0x200>;
compatible = "virtio,mmio";
};
virtio_mmio@a003e00 {
dma-coherent;
interrupts = <0x00 0x2f 0x01>;
reg = <0x00 0xa003e00 0x00 0x200>;
compatible = "virtio,mmio";
};
gpio-keys {
compatible = "gpio-keys";
poweroff {
gpios = <0x8007 0x03 0x00>;
linux,code = <0x74>;
label = "GPIO Key Poweroff";
};
};
pl061@9030000 {
phandle = <0x8007>;
clock-names = "apb_pclk";
clocks = <0x8000>;
interrupts = <0x00 0x07 0x04>;
gpio-controller;
#gpio-cells = <0x02>;
compatible = "arm,pl061", "arm,primecell";
reg = <0x00 0x9030000 0x00 0x1000>;
};
pcie@10000000 {
interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
interrupt-map = <0x00 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x03 0x04 0x00 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x04 0x04 0x00 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x05 0x04 0x00 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x04 0x04 0x800 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x05 0x04 0x800 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x06 0x04 0x800 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x05 0x04 0x1000 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x06 0x04 0x1000 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x03 0x04 0x1000 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x01 0x8005 0x00 0x00 0x00 0x06 0x04 0x1800 0x00 0x00 0x02 0x8005 0x00 0x00 0x00 0x03 0x04 0x1800 0x00 0x00 0x03 0x8005 0x00 0x00 0x00 0x04 0x04 0x1800 0x00 0x00 0x04 0x8005 0x00 0x00 0x00 0x05 0x04>;
#interrupt-cells = <0x01>;
ranges = <0x1000000 0x00 0x00 0x00 0x3eff0000 0x00 0x10000 0x2000000 0x00 0x10000000 0x00 0x10000000 0x00 0x2eff0000 0x3000000 0x80 0x00 0x80 0x00 0x80 0x00>;
reg = <0x40 0x10000000 0x00 0x10000000>;
msi-map = <0x00 0x8006 0x00 0x10000>;
dma-coherent;
bus-range = <0x00 0xff>;
linux,pci-domain = <0x00>;
#size-cells = <0x02>;
#address-cells = <0x03>;
device_type = "pci";
compatible = "pci-host-ecam-generic";
};
pl031@9010000 {
clock-names = "apb_pclk";
clocks = <0x8000>;
interrupts = <0x00 0x02 0x04>;
reg = <0x00 0x9010000 0x00 0x1000>;
compatible = "arm,pl031", "arm,primecell";
};
pl011@9000000 {
clock-names = "uartclk", "apb_pclk";
clocks = <0x8000 0x8000>;
interrupts = <0x00 0x01 0x04>;
reg = <0x00 0x9000000 0x00 0x1000>;
compatible = "arm,pl011", "arm,primecell";
};
pmu {
interrupts = <0x01 0x07 0xf04>;
compatible = "arm,armv8-pmuv3";
};
intc@8000000 {
phandle = <0x8005>;
interrupts = <0x01 0x09 0x04>;
reg = <0x00 0x8000000 0x00 0x10000 0x00 0x8010000 0x00 0x10000 0x00 0x8030000 0x00 0x10000 0x00 0x8040000 0x00 0x10000>;
compatible = "arm,cortex-a15-gic";
ranges;
#size-cells = <0x02>;
#address-cells = <0x02>;
interrupt-controller;
#interrupt-cells = <0x03>;
v2m@8020000 {
phandle = <0x8006>;
reg = <0x00 0x8020000 0x00 0x1000>;
msi-controller;
compatible = "arm,gic-v2m-frame";
};
};
flash@0 {
bank-width = <0x04>;
reg = <0x00 0x00 0x00 0x4000000 0x00 0x4000000 0x00 0x4000000>;
compatible = "cfi-flash";
};
cpus {
#size-cells = <0x00>;
#address-cells = <0x01>;
cpu-map {
socket0 {
cluster0 {
core0 {
cpu = <0x8004>;
};
core1 {
cpu = <0x8003>;
};
core2 {
cpu = <0x8002>;
};
core3 {
cpu = <0x8001>;
};
};
};
};
cpu@0 {
phandle = <0x8004>;
reg = <0x00>;
enable-method = "psci";
compatible = "arm,cortex-a57";
device_type = "cpu";
};
cpu@1 {
phandle = <0x8003>;
reg = <0x01>;
enable-method = "psci";
compatible = "arm,cortex-a57";
device_type = "cpu";
};
cpu@2 {
phandle = <0x8002>;
reg = <0x02>;
enable-method = "psci";
compatible = "arm,cortex-a57";
device_type = "cpu";
};
cpu@3 {
phandle = <0x8001>;
reg = <0x03>;
enable-method = "psci";
compatible = "arm,cortex-a57";
device_type = "cpu";
};
};
timer {
interrupts = <0x01 0x0d 0xf04 0x01 0x0e 0xf04 0x01 0x0b 0xf04 0x01 0x0a 0xf04 0x01 0x0c 0xf04>;
always-on;
compatible = "arm,armv8-timer", "arm,armv7-timer";
};
apb-pclk {
phandle = <0x8000>;
clock-output-names = "clk24mhz";
clock-frequency = <0x16e3600>;
#clock-cells = <0x00>;
compatible = "fixed-clock";
};
aliases {
serial0 = "/pl011@9000000";
};
chosen {
linux,initrd-end = <0x00 0x49fd4600>;
linux,initrd-start = <0x00 0x48000000>;
stdout-path = "/pl011@9000000";
rng-seed = <0xf119f64b 0xacade219 0xaefd1e87 0x5fb37f65 0xc770054a 0xd779b25f 0x1ba6d6e9 0x8121c19d>;
kaslr-seed = <0x1f500308 0xbb36e27a>;
};
};
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
Binary file not shown.
+217
View File
@@ -0,0 +1,217 @@
/dts-v1/;
/ {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "riscv-virtio";
model = "riscv-virtio,qemu";
poweroff {
value = <0x5555>;
offset = <0x00>;
regmap = <0x04>;
compatible = "syscon-poweroff";
};
reboot {
value = <0x7777>;
offset = <0x00>;
regmap = <0x04>;
compatible = "syscon-reboot";
};
platform-bus@4000000 {
interrupt-parent = <0x03>;
ranges = <0x00 0x00 0x4000000 0x2000000>;
#address-cells = <0x01>;
#size-cells = <0x01>;
compatible = "qemu,platform", "simple-bus";
};
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x00 0x40000000>;
};
cpus {
#address-cells = <0x01>;
#size-cells = <0x00>;
timebase-frequency = <0x989680>;
cpu@0 {
phandle = <0x01>;
device_type = "cpu";
reg = <0x00>;
status = "okay";
compatible = "riscv";
riscv,cbop-block-size = <0x40>;
riscv,cboz-block-size = <0x40>;
riscv,cbom-block-size = <0x40>;
riscv,isa-extensions = "i", "m", "a", "f", "d", "c", "h", "zic64b", "zicbom", "zicbop", "zicboz", "ziccamoa", "ziccif", "zicclsm", "ziccrse", "zicntr", "zicsr", "zifencei", "zihintntl", "zihintpause", "zihpm", "zmmul", "za64rs", "zaamo", "zalrsc", "zawrs", "zfa", "zca", "zcd", "zba", "zbb", "zbc", "zbs", "shcounterenw", "shgatpa", "shtvala", "shvsatpa", "shvstvala", "shvstvecd", "ssccptr", "sscounterenw", "sstc", "sstvala", "sstvecd", "ssu64xl", "svadu", "svvptc";
riscv,isa-base = "rv64i";
riscv,isa = "rv64imafdch_zic64b_zicbom_zicbop_zicboz_ziccamoa_ziccif_zicclsm_ziccrse_zicntr_zicsr_zifencei_zihintntl_zihintpause_zihpm_zmmul_za64rs_zaamo_zalrsc_zawrs_zfa_zca_zcd_zba_zbb_zbc_zbs_shcounterenw_shgatpa_shtvala_shvsatpa_shvstvala_shvstvecd_ssccptr_sscounterenw_sstc_sstvala_sstvecd_ssu64xl_svadu_svvptc";
mmu-type = "riscv,sv57";
interrupt-controller {
#interrupt-cells = <0x01>;
interrupt-controller;
compatible = "riscv,cpu-intc";
phandle = <0x02>;
};
};
cpu-map {
cluster0 {
core0 {
cpu = <0x01>;
};
};
};
};
pmu {
riscv,event-to-mhpmcounters = <0x01 0x01 0x7fff9 0x02 0x02 0x7fffc 0x10019 0x10019 0x7fff8 0x1001b 0x1001b 0x7fff8 0x10021 0x10021 0x7fff8>;
compatible = "riscv,pmu";
};
fw-cfg@10100000 {
dma-coherent;
reg = <0x00 0x10100000 0x00 0x18>;
compatible = "qemu,fw-cfg-mmio";
};
flash@20000000 {
bank-width = <0x04>;
reg = <0x00 0x20000000 0x00 0x2000000 0x00 0x22000000 0x00 0x2000000>;
compatible = "cfi-flash";
};
aliases {
serial0 = "/soc/serial@10000000";
};
chosen {
linux,initrd-end = <0x00 0xa2b4f200>;
linux,initrd-start = <0x00 0xa0200000>;
stdout-path = "/soc/serial@10000000";
rng-seed = <0xa7074b10 0xf3373c0c 0x94a3a9a0 0xa2442477 0x817e30af 0x6460a6d7 0xbcaa71c4 0xb75dd35>;
};
soc {
#address-cells = <0x02>;
#size-cells = <0x02>;
compatible = "simple-bus";
ranges;
rtc@101000 {
interrupts = <0x0b>;
interrupt-parent = <0x03>;
reg = <0x00 0x101000 0x00 0x1000>;
compatible = "google,goldfish-rtc";
};
serial@10000000 {
interrupts = <0x0a>;
interrupt-parent = <0x03>;
clock-frequency = "", "8@";
reg = <0x00 0x10000000 0x00 0x100>;
compatible = "ns16550a";
};
test@100000 {
phandle = <0x04>;
reg = <0x00 0x100000 0x00 0x1000>;
compatible = "sifive,test1", "sifive,test0", "syscon";
};
virtio_mmio@10008000 {
interrupts = <0x08>;
interrupt-parent = <0x03>;
reg = <0x00 0x10008000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10007000 {
interrupts = <0x07>;
interrupt-parent = <0x03>;
reg = <0x00 0x10007000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10006000 {
interrupts = <0x06>;
interrupt-parent = <0x03>;
reg = <0x00 0x10006000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10005000 {
interrupts = <0x05>;
interrupt-parent = <0x03>;
reg = <0x00 0x10005000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10004000 {
interrupts = <0x04>;
interrupt-parent = <0x03>;
reg = <0x00 0x10004000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10003000 {
interrupts = <0x03>;
interrupt-parent = <0x03>;
reg = <0x00 0x10003000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10002000 {
interrupts = <0x02>;
interrupt-parent = <0x03>;
reg = <0x00 0x10002000 0x00 0x1000>;
compatible = "virtio,mmio";
};
virtio_mmio@10001000 {
interrupts = <0x01>;
interrupt-parent = <0x03>;
reg = <0x00 0x10001000 0x00 0x1000>;
compatible = "virtio,mmio";
};
plic@c000000 {
phandle = <0x03>;
riscv,ndev = <0x5f>;
reg = <0x00 0xc000000 0x00 0x600000>;
interrupts-extended = <0x02 0x0b 0x02 0x09>;
interrupt-controller;
compatible = "sifive,plic-1.0.0", "riscv,plic0";
#address-cells = <0x00>;
#interrupt-cells = <0x01>;
};
clint@2000000 {
interrupts-extended = <0x02 0x03 0x02 0x07>;
reg = <0x00 0x2000000 0x00 0x10000>;
compatible = "sifive,clint0", "riscv,clint0";
};
pci@30000000 {
interrupt-map-mask = <0x1800 0x00 0x00 0x07>;
interrupt-map = <0x00 0x00 0x00 0x01 0x03 0x20 0x00 0x00 0x00 0x02 0x03 0x21 0x00 0x00 0x00 0x03 0x03 0x22 0x00 0x00 0x00 0x04 0x03 0x23 0x800 0x00 0x00 0x01 0x03 0x21 0x800 0x00 0x00 0x02 0x03 0x22 0x800 0x00 0x00 0x03 0x03 0x23 0x800 0x00 0x00 0x04 0x03 0x20 0x1000 0x00 0x00 0x01 0x03 0x22 0x1000 0x00 0x00 0x02 0x03 0x23 0x1000 0x00 0x00 0x03 0x03 0x20 0x1000 0x00 0x00 0x04 0x03 0x21 0x1800 0x00 0x00 0x01 0x03 0x23 0x1800 0x00 0x00 0x02 0x03 0x20 0x1800 0x00 0x00 0x03 0x03 0x21 0x1800 0x00 0x00 0x04 0x03 0x22>;
ranges = <0x1000000 0x00 0x00 0x00 0x3000000 0x00 0x10000 0x2000000 0x00 0x40000000 0x00 0x40000000 0x00 0x40000000 0x3000000 0x04 0x00 0x04 0x00 0x04 0x00>;
reg = <0x00 0x30000000 0x00 0x10000000>;
dma-coherent;
bus-range = <0x00 0xff>;
linux,pci-domain = <0x00>;
device_type = "pci";
compatible = "pci-host-ecam-generic";
#size-cells = <0x02>;
#interrupt-cells = <0x01>;
#address-cells = <0x03>;
};
};
};
-29
View File
@@ -1,29 +0,0 @@
{
"arch": "x86",
"cpu": "pentium4",
"os": "none",
"abi": "softfloat",
"llvm-target": "i686-unknown-linux-gnu",
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
"max-atomic-width": 64,
"target-pointer-width": "32",
"features": "-avx,-sse,+soft-float",
"executables": true,
"stack-probes": {
"kind": "inline"
},
"dynamic-linking": true,
"panic-strategy": "abort",
"relocation-model": "pic",
"has-thread-local": false,
"supported-split-debuginfo": [
"packed",
"unpacked",
"off"
],
"linker": "rust-lld",
"linker-flavor": "ld.lld"
}
+75
View File
@@ -0,0 +1,75 @@
ENTRY(__aarch64_entry);
SECTIONS {
. = 0x0;
PROVIDE(__kernel_start = .);
.text : {
*(.text.entry)
*(.text*)
}
. = ALIGN(4K);
.rodata : {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*));
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.rela : {
PROVIDE(__rela_start = .);
*(.rela*)
PROVIDE(__rela_end = .);
}
.got : {
*(.got*)
}
.dynamic : {
*(.dynamic)
}
. = ALIGN(4K);
.data : {
*(.data*)
}
. = ALIGN(4K);
PROVIDE(__bss_start = .);
.bss : {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end = .);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
.dynsym : {
*(.dynsym)
}
.gnu.hash : {
*(.gnu.hash)
}
.hash : {
*(.hash)
}
.dynstr : {
*(.dynstr)
}
}
-55
View File
@@ -1,55 +0,0 @@
ENTRY(__aarch64_entry);
KERNEL_PHYS_BASE = 0x40080000;
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
};
-55
View File
@@ -1,55 +0,0 @@
ENTRY(__aarch64_entry);
KERNEL_PHYS_BASE = 0x80000;
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
};
-58
View File
@@ -1,58 +0,0 @@
ENTRY(__rv64_entry);
KERNEL_PHYS_BASE = 0x40200000;
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
. = ALIGN(8);
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
};
-58
View File
@@ -1,58 +0,0 @@
ENTRY(__rv64_entry);
KERNEL_PHYS_BASE = 0x80200000;
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.text.vectors));
*(.text*)
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.rodata*)
*(.eh_frame*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
. = ALIGN(4K);
.data : AT(. - KERNEL_VIRT_OFFSET) {
*(.data*)
. = ALIGN(8);
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*))
PROVIDE(__init_array_end = .);
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
PROVIDE(__kernel_end = .);
};
+75
View File
@@ -0,0 +1,75 @@
ENTRY(__riscv64_entry);
SECTIONS {
. = 0x0;
PROVIDE(__kernel_start = .);
.text : {
*(.text.entry)
*(.text*)
}
. = ALIGN(4K);
.rodata : {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*));
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.rela : {
PROVIDE(__rela_start = .);
*(.rela*)
PROVIDE(__rela_end = .);
}
.got : {
*(.got*)
}
.dynamic : {
*(.dynamic)
}
. = ALIGN(4K);
.data : {
*(.data*)
}
. = ALIGN(4K);
PROVIDE(__bss_start = .);
.bss : {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end = .);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
.dynsym : {
*(.dynsym)
}
.gnu.hash : {
*(.gnu.hash)
}
.hash : {
*(.hash)
}
.dynstr : {
*(.dynstr)
}
}
-53
View File
@@ -1,53 +0,0 @@
ENTRY(__i686_entry);
KERNEL_PHYS_BASE = 0x100000;
KERNEL_VIRT_OFFSET = 0xC0000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
KEEP(*(.multiboot))
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
*(.text*)
}
.export.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.export.text*))
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.eh_frame*)
*(.rodata*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
.data : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.yboot))
*(.data*)
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__kernel_end = .);
};
-52
View File
@@ -1,52 +0,0 @@
ENTRY(__x86_64_entry);
KERNEL_PHYS_BASE = 0x200000;
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
SECTIONS {
. = KERNEL_PHYS_BASE;
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
.text.entry : {
*(.text.entry)
}
. = ALIGN(16);
. = . + KERNEL_VIRT_OFFSET;
.text : AT(. - KERNEL_VIRT_OFFSET) {
*(.text*)
}
.export.text : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.export.text*))
}
. = ALIGN(4K);
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
*(.eh_frame*)
*(.rodata*)
}
. = ALIGN(4K);
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.tables))
}
.data : AT(. - KERNEL_VIRT_OFFSET) {
KEEP(*(.data.yboot))
*(.data*)
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
.bss : AT(. - KERNEL_VIRT_OFFSET) {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
PROVIDE(__kernel_end = .);
};
+73
View File
@@ -0,0 +1,73 @@
ENTRY(__x86_64_entry);
SECTIONS {
. = 0x0;
PROVIDE(__kernel_start = .);
.text : {
*(.text.entry)
*(.text*)
}
. = ALIGN(4K);
.rodata : {
*(.rodata*)
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__init_array_start = .);
KEEP(*(.init_array*));
PROVIDE(__init_array_end = .);
}
. = ALIGN(4K);
.rela : {
PROVIDE(__rela_start = .);
*(.rela*)
PROVIDE(__rela_end = .);
}
.dynamic : {
*(.dynamic)
}
. = ALIGN(4K);
.data : {
KEEP(*(.data.yboot))
*(.data*)
*(.got*)
}
. = ALIGN(4K);
PROVIDE(__bss_start = .);
.bss : {
*(COMMON)
*(.bss*)
}
. = ALIGN(4K);
PROVIDE(__bss_end = .);
PROVIDE(__kernel_end = .);
PROVIDE(__kernel_size = __kernel_end - __kernel_start);
.dynsym : {
*(.dynsym)
}
.gnu.hash : {
*(.gnu.hash)
}
.hash : {
*(.hash)
}
.dynstr : {
*(.dynstr)
}
}
+2 -1
View File
@@ -14,7 +14,8 @@
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"code-model": "large",
"position-independent-executables": true,
"code-model": "medium",
"eh-frame-header": false,
"crt-objects-fallback": "false",
+3 -1
View File
@@ -3,17 +3,19 @@
"cpu": "x86-64",
"os": "none",
"abi": "softfloat",
"rustc-abi": "x86-softfloat",
"llvm-target": "x86_64-unknown-linux-gnu",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
"max-atomic-width": 64,
"target-pointer-width": "64",
"features": "-avx,-sse,+soft-float",
"features": "-avx,-sse,-avx2,+soft-float",
"disable-redzone": true,
"executables": true,
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"has-thread-local": false,
+10 -13
View File
@@ -19,7 +19,6 @@ chrono.workspace = true
device-api = { workspace = true, features = ["derive"] }
device-api-macros.workspace = true
memtables.workspace = true
vmalloc.workspace = true
kernel-arch.workspace = true
@@ -30,6 +29,7 @@ ygg_driver_net_core = { path = "driver/net/core" }
ygg_driver_net_loopback = { path = "driver/net/loopback" }
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
ygg_driver_nvme = { path = "driver/block/nvme" }
ygg_driver_ahci = { path = "driver/block/ahci" }
ygg_driver_input = { path = "driver/input" }
@@ -56,11 +56,16 @@ git-version = "0.3.9"
aarch64-cpu.workspace = true
device-tree.workspace = true
kernel-arch-aarch64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
[target.'cfg(target_arch = "riscv64")'.dependencies]
device-tree.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_bsp_arm.path = "driver/bsp/arm" # PrimeCell components
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[target.'cfg(target_arch = "x86_64")'.dependencies]
yboot-proto.workspace = true
@@ -72,10 +77,6 @@ ygg_driver_net_igbe.path = "driver/net/igbe"
acpi.workspace = true
[target.'cfg(target_arch = "x86")'.dependencies]
kernel-arch-i686.workspace = true
kernel-arch-x86.workspace = true
[build-dependencies]
abi-generator.workspace = true
@@ -86,24 +87,20 @@ prettyplease = "0.2.15"
aarch64-cpu.workspace = true
device-tree.workspace = true
kernel-arch-x86_64.workspace = true
kernel-arch-i686.workspace = true
kernel-arch-x86.workspace = true
kernel-arch-aarch64.workspace = true
kernel-arch-riscv64.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_bsp_arm.path = "driver/bsp/arm"
ygg_driver_bsp_riscv.path = "driver/bsp/riscv"
ygg_driver_net_stmmac.path = "driver/net/stmmac"
ygg_driver_bsp_bcm283x.path = "driver/bsp/bcm283x"
ygg_driver_bsp_jh7110.path = "driver/bsp/jh7110"
[features]
default = ["fb_console"]
fb_console = []
# TODO replace this with a better configuration mechanism
aarch64_board_virt = ["kernel-arch-aarch64/aarch64_board_virt"]
aarch64_board_raspi4b = ["kernel-arch-aarch64/aarch64_board_raspi4b"]
riscv64_board_virt = ["kernel-arch-riscv64/riscv64_board_virt"]
riscv64_board_jh7110 = ["kernel-arch-riscv64/riscv64_board_jh7110"]
[lints]
workspace = true
-3
View File
@@ -9,9 +9,6 @@ kernel-arch-x86_64.path = "x86_64"
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
kernel-arch-aarch64.path = "aarch64"
[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies]
kernel-arch-i686.path = "i686"
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
kernel-arch-riscv64.path = "riscv64"
-6
View File
@@ -7,7 +7,6 @@ edition = "2021"
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
memtables.workspace = true
device-api = { workspace = true, features = ["derive"] }
bitflags.workspace = true
@@ -18,10 +17,5 @@ tock-registers.workspace = true
[build-dependencies]
cc = "1.0"
[features]
default = []
aarch64_board_virt = []
aarch64_board_raspi4b = []
[lints]
workspace = true
+1 -1
View File
@@ -5,7 +5,7 @@ fn build_fp_context_obj() {
let out_dir = env::var("OUT_DIR").unwrap();
println!("cargo:rerun-if-changed={}", FP_CONTEXT_S);
println!("cargo:rerun-if-changed={FP_CONTEXT_S}");
cc::Build::new()
.out_dir(&out_dir)
+3 -5
View File
@@ -1,5 +1,5 @@
#![no_std]
#![feature(naked_functions, trait_upcasting, decl_macro)]
#![feature(decl_macro)]
#![allow(clippy::new_without_default)]
extern crate alloc;
@@ -40,11 +40,9 @@ impl CpuData for PerCpuData {}
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
#[naked]
#[unsafe(naked)]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!("1: nop; b 1b");
}
core::arch::naked_asm!("1: nop; b 1b");
}
impl ArchitectureImpl {
+124
View File
@@ -0,0 +1,124 @@
use core::ops::Range;
use aarch64_cpu::registers::{TTBR0_EL1, TTBR1_EL1};
use kernel_arch_interface::{mem::DeviceMemoryAttributes, KERNEL_VIRT_OFFSET};
use libk_mm_interface::{
address::PhysicalAddress,
device::{DevicePageManager, DevicePageTableLevel},
table::EntryLevel,
};
use crate::mem::{
auto_lower_address,
table::{PageAttributes, PageEntry, PageTable, L1, L2, L3},
tlb_flush_range_va,
};
pub const IDENTITY_SIZE_L1: usize = 8;
pub const DEVICE_L1: usize = IDENTITY_SIZE_L1;
pub const DEVICE_MAPPING_L3_COUNT: usize = 32;
pub const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + (DEVICE_L1 << L1::SHIFT);
#[repr(transparent)]
pub struct L2DeviceMemory(pub PageTable<L2>);
#[repr(transparent)]
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MAPPING_L3_COUNT]);
static mut KERNEL_L1: PageTable<L1> = PageTable::zeroed();
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
DevicePageManager::new(
L3DeviceMemory([PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT]),
L2DeviceMemory(PageTable::zeroed()),
);
impl DevicePageTableLevel for L2DeviceMemory {
type Level = L2;
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
const INDEX_RANGE: Range<usize> = DEVICE_MAPPING_L3_COUNT..512;
fn map_page(
&mut self,
index: usize,
physical: PhysicalAddress,
attrs: &DeviceMemoryAttributes,
) {
let _ = attrs;
self.0[index] = PageEntry::device_block(physical);
}
fn unmap_page(&mut self, index: usize) {
self.0[index] = PageEntry::INVALID;
}
fn is_mapped(&self, index: usize) -> bool {
self.0[index].is_present()
}
fn flush_range(range: Range<usize>) {
let start = range.start * L2::SIZE + Self::VIRTUAL_BASE;
let size = (range.end - range.start) * L2::SIZE;
tlb_flush_range_va(start, size);
}
}
impl DevicePageTableLevel for L3DeviceMemory {
type Level = L3;
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MAPPING_L3_COUNT;
fn map_page(
&mut self,
index: usize,
physical: PhysicalAddress,
attrs: &DeviceMemoryAttributes,
) {
let _ = attrs;
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i] = PageEntry::device_page(physical);
}
fn unmap_page(&mut self, index: usize) {
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i] = PageEntry::INVALID;
}
fn is_mapped(&self, index: usize) -> bool {
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i].is_present()
}
fn flush_range(range: Range<usize>) {
let start = range.start * L3::SIZE + Self::VIRTUAL_BASE;
let size = (range.end - range.start) * L3::SIZE;
tlb_flush_range_va(start, size);
}
}
pub unsafe fn setup() {
// 0..IDENTITY_SIZE_L1 -> lower RAM region
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i << L1::SHIFT);
KERNEL_L1[i] = PageEntry::normal_block(phys, PageAttributes::empty());
}
// DEVICE_L1 -> Device L2 table
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
// ..512 -> Device L2 pages
for i in 0..DEVICE_MAPPING_L3_COUNT {
let phys =
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::empty());
}
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_L1[DEVICE_L1] = PageEntry::table(phys, PageAttributes::empty());
}
pub unsafe fn load() {
let ttbr_physical = auto_lower_address(&raw const KERNEL_L1) as u64;
TTBR0_EL1.set_baddr(ttbr_physical);
TTBR1_EL1.set_baddr(ttbr_physical);
}
+141
View File
@@ -0,0 +1,141 @@
use core::sync::atomic::{self, Ordering};
use aarch64_cpu::{
asm::barrier,
registers::{PAR_EL1, SCTLR_EL1},
};
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
use tock_registers::interfaces::{ReadWriteable, Readable};
use crate::mem::table::L3;
/// Enables data cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care.
pub unsafe fn enable_dcache() {
barrier::dsb(barrier::ISHST);
barrier::isb(barrier::SY);
SCTLR_EL1.modify(SCTLR_EL1::C::Cacheable);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
/// Enables instruction cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care.
pub unsafe fn enable_icache() {
barrier::isb(barrier::SY);
SCTLR_EL1.modify(SCTLR_EL1::I::Cacheable);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
/// Disables instruction cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care. Might break some instructions.
pub unsafe fn disable_icache() {
barrier::isb(barrier::SY);
ic_iallu();
SCTLR_EL1.modify(SCTLR_EL1::I::NonCacheable);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_asid(asid: u8) {
barrier::dsb(barrier::ISHST);
let value = (asid as u64) << 48;
unsafe {
core::arch::asm!("tlbi aside1, {value}", value = in(reg) value);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_all() {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("tlbi vmalle1is");
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_vaae1(page: usize) {
barrier::dsb(barrier::ISHST);
let argument = page >> 12;
unsafe {
core::arch::asm!("tlbi vaae1, {argument}", argument = in(reg) argument);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_range_va(base: usize, size: usize) {
let end = (base + size).page_align_up::<L3>();
let base = base.page_align_down::<L3>();
let count = (end - base).page_count::<L3>();
for i in 0..count {
tlb_flush_vaae1(base + i * L3::SIZE);
}
}
pub fn at_s1e0r(input: usize) -> Option<u64> {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("at s1e0r, {address}", address = in(reg) input);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
Some(PAR_EL1.read(PAR_EL1::PA))
} else {
None
}
}
pub fn at_s1e1r(input: usize) -> Option<u64> {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("at s1e1r, {address}", address = in(reg) input);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
Some(PAR_EL1.read(PAR_EL1::PA))
} else {
None
}
}
pub fn ic_iallu() {
atomic::compiler_fence(Ordering::SeqCst);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
unsafe {
core::arch::asm!("ic iallu");
}
barrier::isb(barrier::SY);
}
pub fn dc_cvac(input: usize) {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("dc cvac, {address}", address = in(reg) input);
}
}
+79 -484
View File
@@ -1,108 +1,49 @@
use core::{
alloc::Layout,
ops::{Deref, DerefMut},
sync::atomic::{self, AtomicUsize, Ordering},
};
#![allow(clippy::missing_safety_doc)]
use aarch64_cpu::{
asm::barrier,
registers::{MAIR_EL1, PAR_EL1, SCTLR_EL1, TTBR0_EL1, TTBR1_EL1},
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1},
};
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock, Architecture, KERNEL_VIRT_OFFSET,
sync::IrqSafeSpinlock,
KERNEL_VIRT_OFFSET,
};
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use memtables::aarch64::{FixedTables, KERNEL_L3_COUNT};
use static_assertions::const_assert_eq;
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use tock_registers::interfaces::{ReadWriteable, Writeable};
use yggdrasil_abi::error::Error;
use crate::ArchitectureImpl;
pub use intrinsics::*;
use self::table::{PageAttributes, PageEntry, PageTable, L1, L2, L3};
use crate::{mem::table::L1, ArchitectureImpl};
pub mod fixed;
pub mod intrinsics;
pub mod process;
pub mod table;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
// TODO eliminate this requirement by using precomputed indices
const MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET;
#[cfg(any(feature = "aarch64_board_virt", rust_analyzer))]
const KERNEL_PHYS_BASE: usize = 0x40080000;
#[cfg(any(feature = "aarch64_board_raspi4b", rust_analyzer))]
const KERNEL_PHYS_BASE: usize = 0x80000;
// Precomputed mappings
const KERNEL_L1_INDEX: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
const KERNEL_START_L2_INDEX: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
const KERNEL_END_L2_INDEX: usize = KERNEL_START_L2_INDEX + KERNEL_L3_COUNT;
// Must not be zero, should be at 4MiB
const_assert_eq!(KERNEL_START_L2_INDEX, 0);
// From static mapping
#[cfg(any(feature = "aarch64_board_raspi4b", rust_analyzer))]
const_assert_eq!(KERNEL_L1_INDEX, 0);
#[cfg(any(feature = "aarch64_board_virt", rust_analyzer))]
const_assert_eq!(KERNEL_L1_INDEX, 1);
// Runtime mappings
// 2MiB max
const EARLY_MAPPING_L2I: usize = KERNEL_END_L2_INDEX + 1;
// 1GiB max
const DEVICE_MAPPING_L1I: usize = KERNEL_L1_INDEX + 2;
const DEVICE_MAPPING_L3_COUNT: usize = 4;
// 16GiB max
const RAM_MAPPING_START_L1I: usize = KERNEL_L1_INDEX + 3;
pub const RAM_MAPPING_L1_COUNT: usize = 16;
// 2MiB for early mappings
const EARLY_MAPPING_OFFSET: usize =
MAPPING_OFFSET | (KERNEL_L1_INDEX * L1::SIZE) | (EARLY_MAPPING_L2I * L2::SIZE);
static mut EARLY_MAPPING_L3: PageTable<L3> = PageTable::zeroed();
// 1GiB for device MMIO mapping
const DEVICE_MAPPING_OFFSET: usize = MAPPING_OFFSET | (DEVICE_MAPPING_L1I * L1::SIZE);
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
[PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT];
// 16GiB for RAM mapping
pub const RAM_MAPPING_OFFSET: usize = MAPPING_OFFSET | (RAM_MAPPING_START_L1I * L1::SIZE);
pub static MEMORY_LIMIT: AtomicUsize = AtomicUsize::new(0);
split_spinlock! {
use crate::ArchitectureImpl;
use crate::mem::FixedTables;
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
static KERNEL_TABLES: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
static KERNEL_MEMORY_LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address < MEMORY_LIMIT.load(Ordering::Acquire) {
address + RAM_MAPPING_OFFSET
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
} else {
panic!("Invalid physical address: {:#x}", address);
panic!("Invalid physical address: {address:#x}");
}
}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET
|| address - RAM_MAPPING_OFFSET >= MEMORY_LIMIT.load(Ordering::Acquire)
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
{
panic!("Not a virtualized physical address: {:#x}", address);
panic!("Invalid virtual (-> physical) address {address:#x}");
}
(address - RAM_MAPPING_OFFSET) as _
(address - KERNEL_VIRT_OFFSET) as u64
}
unsafe fn map_device_pages(
@@ -110,442 +51,96 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
map_device_memory(PhysicalAddress::from_u64(base), count, attrs)
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
unmap_device_memory(mapping)
}
}
/// Memory mapping which may be used for performing early kernel initialization
pub struct EarlyMapping<'a, T: ?Sized> {
value: &'a mut T,
page_count: usize,
}
impl<'a, T: Sized> EarlyMapping<'a, T> {
/// # Safety
///
/// `physical` address provided must be a valid non-NULL address actually containing `T`.
pub unsafe fn map_slice(
physical: PhysicalAddress,
len: usize,
) -> Result<EarlyMapping<'a, [T]>, Error> {
let layout = Layout::array::<T>(len).unwrap();
let aligned = physical.page_align_down::<L3>();
let offset = physical.page_offset::<L3>();
let page_count = (offset + layout.size()).div_ceil(L3::SIZE);
let virt = map_early_pages(aligned, page_count)?;
let value = core::slice::from_raw_parts_mut((virt + offset) as *mut T, len);
Ok(EarlyMapping { value, page_count })
}
}
impl<T: ?Sized> Deref for EarlyMapping<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<T: ?Sized> DerefMut for EarlyMapping<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
}
}
impl<T: ?Sized> Drop for EarlyMapping<'_, T> {
fn drop(&mut self) {
let address = (self.value as *mut T).addr() & !(L3::SIZE - 1);
for i in 0..self.page_count {
let page = address + i * L3::SIZE;
unsafe {
unmap_early_page(page);
}
}
}
}
fn kernel_table_flags() -> PageAttributes {
PageAttributes::TABLE
| PageAttributes::ACCESS
| PageAttributes::SH_INNER
| PageAttributes::PAGE_ATTR_NORMAL
| PageAttributes::PRESENT
}
fn ram_block_flags() -> PageAttributes {
// TODO UXN, PXN
PageAttributes::BLOCK
| PageAttributes::ACCESS
| PageAttributes::SH_OUTER
| PageAttributes::PAGE_ATTR_NORMAL
| PageAttributes::PRESENT
}
// Early mappings
unsafe fn map_early_pages(physical: PhysicalAddress, count: usize) -> Result<usize, Error> {
for l3i in 0..512 {
let mut taken = false;
for i in 0..count {
if EARLY_MAPPING_L3[i + l3i].is_present() {
taken = true;
break;
}
}
if taken {
continue;
}
for i in 0..count {
let page = physical.add(i * L3::SIZE);
// TODO NX, NC
EARLY_MAPPING_L3[i + l3i] = PageEntry::normal_page(page, PageAttributes::empty());
tlb_flush_vaae1(EARLY_MAPPING_OFFSET + (l3i + i) * L3::SIZE);
}
return Ok(EARLY_MAPPING_OFFSET + l3i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn unmap_early_page(address: usize) {
if !(EARLY_MAPPING_OFFSET..EARLY_MAPPING_OFFSET + L2::SIZE).contains(&address) {
panic!("Tried to unmap invalid early mapping: {:#x}", address);
}
let l3i = (address - EARLY_MAPPING_OFFSET).page_index::<L3>();
assert!(EARLY_MAPPING_L3[l3i].is_present());
EARLY_MAPPING_L3[l3i] = PageEntry::INVALID;
}
/// # Safety
///
/// Only meant to be used by the architecture initialization functions.
pub unsafe fn map_ram_l1(index: usize) {
if index >= RAM_MAPPING_L1_COUNT {
ArchitectureImpl::halt();
}
let mut tables = KERNEL_TABLES.lock();
let table_index = index + RAM_MAPPING_START_L1I;
if tables.l1.data[table_index] != 0 {
ArchitectureImpl::halt();
}
tables.l1.data[table_index] = ((index * L1::SIZE) as u64) | ram_block_flags().bits();
tlb_flush_vaae1(RAM_MAPPING_OFFSET + index * L1::SIZE);
}
// Device mappings
unsafe fn map_device_memory_l3(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
// TODO don't map pages if already mapped
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
continue 'l0;
}
}
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
// TODO NX, NC
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::device_page(base.add(j * L3::SIZE));
tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + l2i * L2::SIZE + l3i * L3::SIZE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
#[allow(unused)]
unsafe fn map_device_memory_l2(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
for j in 0..count {
if DEVICE_MAPPING_L2[i + j].is_present() {
continue 'l0;
}
}
for j in 0..count {
DEVICE_MAPPING_L2[i + j] = PageEntry::<L2>::device_block(base.add(j * L2::SIZE));
tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + (i + j) * L2::SIZE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
}
Err(Error::OutOfMemory)
}
pub(crate) unsafe fn map_device_memory(
base: PhysicalAddress,
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
let l3_aligned = base.page_align_down::<L3>();
let l3_offset = base.page_offset::<L3>();
let page_count = (l3_offset + size).page_count::<L3>();
if page_count > 256 {
// Large mapping, use L2 mapping instead
let l2_aligned = base.page_align_down::<L2>();
let l2_offset = base.page_offset::<L2>();
let page_count = (l2_offset + size).page_count::<L2>();
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l2_aligned.into_u64(),
address,
base_address,
page_count,
L2::SIZE,
))
} else {
// Just map the pages directly
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l3_aligned.into_u64(),
address,
base_address,
page_count,
L3::SIZE,
))
}
}
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
match map.page_size {
L3::SIZE => {
for i in 0..map.page_count {
let page = map.base_address + i * L3::SIZE;
let l2i = page.page_index::<L2>();
let l3i = page.page_index::<L3>();
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
tlb_flush_vaae1(page);
}
}
L2::SIZE => todo!(),
_ => unimplemented!(),
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
}
}
#[inline]
pub fn tlb_flush_asid(asid: u8) {
barrier::dsb(barrier::ISHST);
let value = (asid as u64) << 48;
unsafe {
core::arch::asm!("tlbi aside1, {value}", value = in(reg) value);
pub fn auto_lower_address<T>(ptr: *const T) -> usize {
let address = ptr.addr();
if address < KERNEL_VIRT_OFFSET {
address
} else {
address - KERNEL_VIRT_OFFSET
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_all() {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("tlbi vmalle1is");
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
#[inline]
pub fn tlb_flush_vaae1(page: usize) {
barrier::dsb(barrier::ISHST);
let argument = page >> 12;
unsafe {
core::arch::asm!("tlbi vaae1, {argument}", argument = in(reg) argument);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
pub fn at_s1e0r(input: usize) -> Option<u64> {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("at s1e0r, {address}", address = in(reg) input);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
Some(PAR_EL1.read(PAR_EL1::PA))
pub fn auto_upper_address<T>(ptr: *const T) -> usize {
let address = ptr.addr();
if address < KERNEL_VIRT_OFFSET {
address + KERNEL_VIRT_OFFSET
} else {
None
address
}
}
pub fn at_s1e1r(input: usize) -> Option<u64> {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("at s1e1r, {address}", address = in(reg) input);
}
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
Some(PAR_EL1.read(PAR_EL1::PA))
} else {
None
}
}
pub fn ic_iallu() {
atomic::compiler_fence(Ordering::SeqCst);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
unsafe {
core::arch::asm!("ic iallu");
}
barrier::isb(barrier::SY);
}
pub fn dc_cvac(input: usize) {
barrier::dsb(barrier::ISHST);
unsafe {
core::arch::asm!("dc cvac, {address}", address = in(reg) input);
}
}
fn auto_address<T>(value: *const T) -> usize {
let addr = value.addr();
if addr < KERNEL_VIRT_OFFSET {
// Called from lower half
addr
} else {
// Called from higher-half
addr - KERNEL_VIRT_OFFSET
}
}
/// (BSP-early init) loads precomputed kernel mapping tables for the kernel to jump to "higher-half"
///
/// # Safety
///
/// Unsafe, must only be called by BSP during its early init while still in "lower-half"
pub unsafe fn load_fixed_tables() {
let ttbr0 = auto_address(&raw const KERNEL_TABLES) as u64;
TTBR0_EL1.set(ttbr0);
TTBR1_EL1.set(ttbr0);
}
/// Sets up additional translation tables for kernel usage
///
/// # Safety
///
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
pub unsafe fn init_fixed_tables() {
// TODO this could be built in compile-time too?
let mut tables = KERNEL_TABLES.grab();
let early_mapping_l3_phys = auto_address(&raw const EARLY_MAPPING_L3);
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
for i in 0..DEVICE_MAPPING_L3_COUNT {
let device_mapping_l3_phys = PhysicalAddress::from_usize(
&DEVICE_MAPPING_L3S[i] as *const _ as usize - KERNEL_VIRT_OFFSET,
);
DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
}
assert_eq!(tables.l2.data[EARLY_MAPPING_L2I], 0);
tables.l2.data[EARLY_MAPPING_L2I] =
(early_mapping_l3_phys as u64) | kernel_table_flags().bits();
tlb_flush_vaae1(EARLY_MAPPING_OFFSET);
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
tables.l1.data[DEVICE_MAPPING_L1I] =
(device_mapping_l2_phys as u64) | kernel_table_flags().bits();
tlb_flush_all();
}
pub fn setup_memory_attributes() {
fn setup_memory_attributes() {
// TODO: Figure out why WriteBack_NonTransient_ReadWriteAlloc doesn't work on Pi 4B
MAIR_EL1.write(
//// Attribute 0 -- normal memory
MAIR_EL1::Attr0_Normal_Inner::WriteBack_NonTransient +
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
//// Attribute 1 -- normal non-cacheable memory
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
//// Attribute 2 -- device memory
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
//// Attribute 1 -- normal non-cacheable memory
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
//// Attribute 2 -- device memory
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
);
}
/// Enables data cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care.
pub unsafe fn enable_dcache() {
unsafe fn enable_mmu() {
TCR_EL1.write(
TCR_EL1::AS::ASID8Bits +
TCR_EL1::A1::TTBR0 +
TCR_EL1::HD::CLEAR +
// General
TCR_EL1::IPS::Bits_48 +
// TTBR0
TCR_EL1::TG0::KiB_4 + TCR_EL1::T0SZ.val(25) + TCR_EL1::SH0::Inner +
// TTBR1
TCR_EL1::TG1::KiB_4 + TCR_EL1::T1SZ.val(25) + TCR_EL1::SH1::Inner,
);
barrier::dsb(barrier::ISHST);
barrier::isb(barrier::SY);
SCTLR_EL1.modify(SCTLR_EL1::C::Cacheable);
SCTLR_EL1.modify(
SCTLR_EL1::E0E::LittleEndian
+ SCTLR_EL1::EE::LittleEndian
+ SCTLR_EL1::WXN::Disable
+ SCTLR_EL1::SA0::Enable
+ SCTLR_EL1::SA::Enable
+ SCTLR_EL1::A::Enable
+ SCTLR_EL1::I::NonCacheable
+ SCTLR_EL1::C::NonCacheable,
);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
// Enable translation
SCTLR_EL1.modify(SCTLR_EL1::M::Enable);
// Enable caches
enable_icache();
enable_dcache();
}
/// Enables instruction cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care.
pub unsafe fn enable_icache() {
barrier::isb(barrier::SY);
SCTLR_EL1.modify(SCTLR_EL1::I::Cacheable);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
}
/// Disables instruction cache.
///
/// # Safety
///
/// Manipulates low-level machine state, use with care. Might break some instructions.
pub unsafe fn disable_icache() {
barrier::isb(barrier::SY);
ic_iallu();
SCTLR_EL1.modify(SCTLR_EL1::I::NonCacheable);
barrier::dsb(barrier::ISH);
barrier::isb(barrier::SY);
pub unsafe fn init_lower(bsp: bool) {
setup_memory_attributes();
if bsp {
fixed::setup();
}
fixed::load();
enable_mmu();
}
+40 -5
View File
@@ -7,7 +7,7 @@ use core::{
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
@@ -79,7 +79,15 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
)
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
unsafe fn update_page_attributes(
&mut self,
address: usize,
update: &PageAttributeUpdate,
) -> Result<(), Error> {
self.update_l3_entry(address, |entry| entry.update(update))
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<(PhysicalAddress, bool), Error> {
self.pop_l3_entry(address)
}
@@ -120,7 +128,32 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
fn update_l3_entry<F: FnOnce(&mut PageEntry<L3>) -> Result<(), Error>>(
&mut self,
virt: usize,
mapper: F,
) -> Result<(), Error> {
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
let mut l2 = self.l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let entry = &mut l3[l3i];
if !entry.is_present() {
return Err(Error::DoesNotExist);
}
mapper(entry)?;
ic_iallu();
dc_cvac((&raw const l3[l3i]).addr());
tlb_flush_vaae1(virt);
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<(PhysicalAddress, bool), Error> {
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
@@ -129,14 +162,16 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
let mut l2 = self.l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
let entry = l3[l3i];
let page = entry.as_page().ok_or(Error::DoesNotExist)?;
let dirty = entry.is_dirty();
l3[l3i] = PageEntry::INVALID;
ic_iallu();
dc_cvac((&raw const l3[l3i]).addr());
tlb_flush_vaae1(virt);
Ok(page)
Ok((page, dirty))
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
+41 -3
View File
@@ -9,6 +9,7 @@ use kernel_arch_interface::KERNEL_VIRT_OFFSET;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
process::PageAttributeUpdate,
table::{
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
@@ -46,6 +47,7 @@ bitflags! {
const NON_GLOBAL = 1 << 11;
const DIRTY = 1 << 51;
const PXN = 1 << 53;
const UXN = 1 << 54;
}
@@ -136,8 +138,32 @@ impl<L: EntryLevel> PageTable<L> {
}
impl<L: EntryLevel> PageEntry<L> {
const ATTR_MASK: u64 = 0xFFF | (0xFFFF << 48);
pub const INVALID: Self = Self(0, PhantomData);
pub fn update(&mut self, update: &PageAttributeUpdate) -> Result<(), Error> {
let mut attrs = PageAttributes::from_bits_retain(self.0);
if let Some(write) = update.user_write {
// Make writeable/non-writeable
if write {
attrs &= !PageAttributes::AP_ACCESS_MASK;
attrs |= PageAttributes::AP_BOTH_READWRITE;
} else {
todo!();
}
}
if let Some(dirty) = update.dirty {
if dirty {
attrs |= PageAttributes::DIRTY;
} else {
attrs &= !PageAttributes::DIRTY;
}
}
self.0 &= !Self::ATTR_MASK;
self.0 |= attrs.bits() & Self::ATTR_MASK;
Ok(())
}
pub const fn is_present(self) -> bool {
self.0 & PageAttributes::PRESENT.bits() != 0
}
@@ -271,7 +297,7 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
} else {
None
}
@@ -283,7 +309,7 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
} else if let Some(table) = self.as_table() {
EntryType::Table(table)
} else {
EntryType::Page(PhysicalAddress::from_u64(self.0 & !0xFFF))
EntryType::Page(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
}
}
}
@@ -316,10 +342,14 @@ impl PageEntry<L3> {
)
}
pub fn is_dirty(&self) -> bool {
self.0 & PageAttributes::DIRTY.bits() != 0
}
pub fn as_page(&self) -> Option<PhysicalAddress> {
let mask = (PageAttributes::PRESENT | PageAttributes::PAGE).bits();
if self.0 & mask == mask {
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
} else {
None
}
@@ -355,6 +385,10 @@ impl From<MapAttributes> for PageAttributes {
out |= PageAttributes::AP_KERNEL_READONLY;
}
if value.contains(MapAttributes::DIRTY) {
out |= PageAttributes::DIRTY;
}
if value.contains(MapAttributes::NON_GLOBAL) {
out |= PageAttributes::NON_GLOBAL;
}
@@ -377,6 +411,10 @@ impl From<PageAttributes> for MapAttributes {
_ => unreachable!(),
};
if value.contains(PageAttributes::DIRTY) {
out |= MapAttributes::DIRTY;
}
if value.contains(PageAttributes::NON_GLOBAL) {
out |= MapAttributes::NON_GLOBAL;
}
+1
View File
@@ -7,3 +7,4 @@ edition = "2021"
kernel-arch-interface.workspace = true
yggdrasil-abi.workspace = true
libk-mm-interface.workspace = true
device-api.workspace = true
+28 -2
View File
@@ -1,9 +1,11 @@
#![feature(never_type)]
#![feature(never_type, allocator_api, slice_ptr_get)]
use std::{
alloc::{Allocator, Global, Layout},
marker::PhantomData,
sync::atomic::{AtomicBool, Ordering},
};
use device_api::dma::{DmaAllocation, DmaAllocator};
use kernel_arch_interface::{
cpu::{CpuData, IpiQueue},
mem::{
@@ -105,6 +107,14 @@ impl Architecture for ArchitectureImpl {
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
None
}
fn load_barrier() {}
fn store_barrier() {}
fn memory_barrier() {}
fn flush_virtual_range(_range: std::ops::Range<usize>) {}
}
impl KernelTableManager for KernelTableManagerImpl {
@@ -150,7 +160,7 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
unimplemented!()
}
unsafe fn unmap_page(&mut self, _address: usize) -> Result<PhysicalAddress, Error> {
unsafe fn unmap_page(&mut self, _address: usize) -> Result<(PhysicalAddress, bool), Error> {
unimplemented!()
}
@@ -202,3 +212,19 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator> TaskContext<K, PA>
extern "Rust" fn __signal_process_group(_group_id: ProcessGroupId, _signal: Signal) {
unimplemented!()
}
pub struct HostedDmaAllocator;
impl DmaAllocator for HostedDmaAllocator {
fn allocate(&self, layout: Layout) -> Result<DmaAllocation, Error> {
let ptr = Global.allocate(layout.align_to(0x1000).unwrap()).unwrap();
let base = ptr.as_non_null_ptr();
let addr: usize = base.addr().into();
Ok(DmaAllocation {
host_virtual: base.cast(),
host_physical: addr as _,
page_count: layout.size().div_ceil(0x1000),
bus_address: addr as _,
})
}
}
-16
View File
@@ -1,16 +0,0 @@
[package]
name = "kernel-arch-i686"
version = "0.1.0"
edition = "2021"
[dependencies]
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
device-api = { workspace = true, features = ["derive"] }
kernel-arch-x86.workspace = true
bitflags.workspace = true
static_assertions.workspace = true
tock-registers.workspace = true
log.workspace = true
-116
View File
@@ -1,116 +0,0 @@
// vi: set ft=asm :
.macro SAVE_TASK_STATE
push %edi
push %esi
push %ebp
push %ebx
.endm
.macro LOAD_TASK_STATE
pop %ebx
pop %ebp
pop %esi
pop %edi
.endm
.section .text
.global __i686_task_enter_kernel
.global __i686_task_enter_user
.global __i686_task_enter_from_fork
.global __i686_switch_task
.global __i686_enter_task
.global __i686_switch_and_drop
__i686_task_enter_kernel:
// %esp + 4: argument
// %esp + 0: entry
xor %ecx, %ecx
xchg (%esp), %ecx
// Enable IRQ in EFLAGS
pushfl
pop %edx
or $(1 << 9), %edx
// Setup iret
push %edx // eflags
pushl $0x08 // cs
push %ecx // eip
iret
__i686_task_enter_user:
pop %edx // User %esp
pop %ecx // entry
pop %eax // flags
// Setup iret
// %ss:%esp
pushl $0x23
push %edx
// %eflags
push %eax
// %cs:%eip
pushl $0x1B
push %ecx
mov $0x23, %bx
mov %bx, %ds
mov %bx, %es
mov %bx, %fs
iret
__i686_task_enter_from_fork:
jmp .
__i686_switch_task:
// %esp + 0: return
// %esp + 4: destination
// %esp + 8: source
mov 4(%esp), %eax
mov 8(%esp), %ecx
SAVE_TASK_STATE
// Store stack to "from" context
mov %esp, (%ecx)
// Load stack from "to" context
mov (%eax), %esp
LOAD_TASK_STATE
ret
__i686_enter_task:
// %esp + 0: return
// %esp + 4: destination
// Switch to destination stack
mov 4(%esp), %eax
mov (%eax), %esp
LOAD_TASK_STATE
ret
__i686_switch_and_drop:
// %esp + 0: return
// %esp + 4: destination
// %esp + 8: thread to drop
mov 8(%esp), %ecx
mov 4(%esp), %eax
// Switch to stack
mov (%eax), %esp
LOAD_TASK_STATE
// TODO actually drop the thread
ret
-462
View File
@@ -1,462 +0,0 @@
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{StackBuilder, TaskContext, TaskFrame, UserContextInfo},
};
use kernel_arch_x86::registers::{FpuContext, CR3};
use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{
gdt::{self, TSS},
mem::KERNEL_TABLES,
};
#[allow(unused)]
#[repr(C)]
pub struct ExceptionFrame {
pub eax: u32,
pub ecx: u32,
pub edx: u32,
pub ebx: u32,
pub ebp: u32,
pub esi: u32,
pub edi: u32,
pub exc_number: u32,
pub exc_code: u32,
pub eip: u32,
pub cs: u32,
pub eflags: u32,
pub esp: u32,
pub ss: u32,
}
#[allow(unused)]
#[derive(Debug)]
#[repr(C)]
pub struct SyscallFrame {
pub eax: usize,
// ebx, ecx, edx, esi, edi, ebp
pub args: [usize; 6],
pub eip: u32,
pub cs: u32,
pub eflags: u32,
pub esp: u32,
pub ss: u32,
}
#[allow(unused)]
#[repr(C)]
pub struct InterruptFrame {
pub eax: u32,
pub ecx: u32,
pub edx: u32,
pub ebx: u32,
pub ebp: u32,
pub esi: u32,
pub edi: u32,
pub irq_number: u32,
pub eip: u32,
pub cs: u32,
pub eflags: u32,
esp: u32,
ss: u32,
}
#[repr(C, align(0x10))]
struct Inner {
// 0x00
sp: usize,
gs_base: usize,
}
#[allow(dead_code)]
pub struct TaskContextImpl<
K: KernelTableManager,
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
> {
inner: UnsafeCell<Inner>,
fpu_context: Option<UnsafeCell<FpuContext>>,
stack_base_phys: PhysicalAddress,
stack_size: usize,
cr3: u32,
tss_esp0: u32,
_pd: PhantomData<(K, PA)>,
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContextImpl<K, PA>
{
unsafe fn store_state(&self) {
if let Some(fpu) = self.fpu_context.as_ref() {
FpuContext::store(fpu.get());
}
}
unsafe fn load_state(&self) {
if let Some(fpu) = self.fpu_context.as_ref() {
FpuContext::restore(fpu.get());
}
gdt::set_gs_base((*self.inner.get()).gs_base);
TSS.esp0 = self.tss_esp0;
CR3.set(self.cr3 as _);
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
const SIGNAL_STACK_EXTRA_ALIGN: usize = 0;
const USER_STACK_EXTRA_ALIGN: usize = 0;
fn user(context: UserContextInfo) -> Result<Self, Error> {
const USER_TASK_PAGES: usize = 16;
let stack_base_phys = PA::allocate_contiguous_pages(USER_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, USER_TASK_PAGES * 0x1000);
let mut flags = 0x200;
if context.single_step {
flags |= 1 << 8;
}
stack.push(flags);
stack.push(context.entry as _);
stack.push(context.stack_pointer);
setup_common_context(&mut stack, __i686_task_enter_user as _);
let sp = stack.build();
let esp0 = stack_base + USER_TASK_PAGES * 0x1000;
let fpu_context = FpuContext::new(true);
Ok(Self {
inner: UnsafeCell::new(Inner {
sp,
gs_base: context.thread_pointer,
}),
fpu_context: Some(UnsafeCell::new(fpu_context)),
stack_base_phys,
stack_size: USER_TASK_PAGES * 0x1000,
tss_esp0: esp0 as _,
cr3: context.address_space.try_into().unwrap(),
_pd: PhantomData,
})
}
fn kernel(
entry: extern "C" fn(usize) -> !,
arg: usize,
) -> Result<Self, yggdrasil_abi::error::Error> {
const KERNEL_TASK_PAGES: usize = 32;
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
let mut stack = StackBuilder::new(stack_base, KERNEL_TASK_PAGES * 0x1000);
// Entry and argument
stack.push(arg);
stack.push(entry as _);
// XXX
setup_common_context(&mut stack, __i686_task_enter_kernel as _);
let sp = stack.build();
let cr3 = unsafe {
KERNEL_TABLES
.lock()
.as_physical_address()
.try_into_u32()
.unwrap()
};
// TODO stack is leaked
Ok(Self {
inner: UnsafeCell::new(Inner { sp, gs_base: 0 }),
fpu_context: None,
stack_base_phys,
stack_size: KERNEL_TASK_PAGES * 0x1000,
tss_esp0: 0,
cr3,
_pd: PhantomData,
})
}
unsafe fn switch(&self, from: &Self) {
if core::ptr::addr_eq(self, from) {
return;
}
from.store_state();
self.load_state();
__i686_switch_task(self.inner.get(), from.inner.get());
}
unsafe fn enter(&self) -> ! {
self.load_state();
__i686_enter_task(self.inner.get())
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
self.load_state();
__i686_switch_and_drop(self.inner.get(), thread);
}
fn set_thread_pointer(&self, tp: usize) {
unsafe { (*self.inner.get()).gs_base = tp };
gdt::set_gs_base(tp);
}
fn align_stack_for_entry(sp: usize) -> usize {
(sp & !0xF) - 12
}
}
fn setup_common_context(builder: &mut StackBuilder, entry: usize) {
builder.push(entry);
builder.push(0); // %edi
builder.push(0); // %esi
builder.push(0); // %ebp
builder.push(0); // %ebx
}
extern "C" {
fn __i686_task_enter_kernel();
fn __i686_task_enter_user();
fn __i686_task_enter_from_fork();
fn __i686_enter_task(to: *mut Inner) -> !;
fn __i686_switch_task(to: *mut Inner, from: *mut Inner);
fn __i686_switch_and_drop(to: *mut Inner, from: *const ());
}
impl TaskFrame for SyscallFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
eax: self.eax as _,
ecx: self.args[1] as _,
edx: self.args[2] as _,
ebx: self.args[0] as _,
ebp: self.args[5] as _,
esi: self.args[3] as _,
edi: self.args[4] as _,
user_ip: self.eip,
user_sp: self.esp,
eflags: self.eflags,
}
}
fn restore(&mut self, saved: &SavedFrame) {
self.eax = saved.eax as _;
self.args[0] = saved.ebx as _;
self.args[1] = saved.ecx as _;
self.args[2] = saved.edx as _;
self.args[3] = saved.esi as _;
self.args[4] = saved.edi as _;
self.args[5] = saved.ebp as _;
self.eip = saved.user_ip;
self.esp = saved.user_sp;
self.eflags = saved.eflags;
}
fn user_sp(&self) -> usize {
todo!()
}
fn user_ip(&self) -> usize {
todo!()
}
fn argument(&self) -> u64 {
self.args[0] as _
}
fn set_user_sp(&mut self, value: usize) {
self.esp = value as _;
}
fn set_user_ip(&mut self, value: usize) {
self.eip = value as _;
}
fn set_argument(&mut self, value: u64) {
// TODO implement ABI for passing 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as usize;
}
fn set_single_step(&mut self, _step: bool) {
todo!()
}
fn set_return_value(&mut self, value: u64) {
// TODO implement ABI for returning 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as usize;
}
}
impl TaskFrame for InterruptFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
eax: self.eax,
ecx: self.ecx,
edx: self.edx,
ebx: self.ebx,
ebp: self.ebp,
esi: self.esi,
edi: self.edi,
user_ip: self.eip,
user_sp: self.esp,
eflags: self.eflags,
}
}
fn restore(&mut self, _saved: &SavedFrame) {
todo!()
}
fn user_sp(&self) -> usize {
todo!()
}
fn user_ip(&self) -> usize {
todo!()
}
fn argument(&self) -> u64 {
todo!()
}
fn set_user_sp(&mut self, value: usize) {
self.esp = value as u32;
}
fn set_user_ip(&mut self, value: usize) {
self.eip = value as u32;
}
fn set_argument(&mut self, value: u64) {
// TODO implement ABI for returning 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as u32;
}
fn set_single_step(&mut self, step: bool) {
if step {
self.eflags |= 1 << 8;
} else {
self.eflags &= !(1 << 8);
}
}
fn set_return_value(&mut self, value: u64) {
// TODO implement ABI for returning 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as u32;
}
}
impl TaskFrame for ExceptionFrame {
fn store(&self) -> SavedFrame {
SavedFrame {
eax: self.eax,
ecx: self.ecx,
edx: self.edx,
ebx: self.ebx,
ebp: self.ebp,
esi: self.esi,
edi: self.edi,
user_ip: self.eip,
user_sp: self.esp,
eflags: self.eflags,
}
}
fn restore(&mut self, _saved: &SavedFrame) {
todo!()
}
fn user_sp(&self) -> usize {
todo!()
}
fn user_ip(&self) -> usize {
self.eip as _
}
fn argument(&self) -> u64 {
todo!()
}
fn set_user_sp(&mut self, value: usize) {
self.esp = value as u32;
}
fn set_user_ip(&mut self, value: usize) {
self.eip = value as u32;
}
fn set_argument(&mut self, value: u64) {
// TODO implement ABI for returning 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as u32;
}
fn set_single_step(&mut self, step: bool) {
if step {
self.eflags |= 1 << 8;
} else {
self.eflags &= !(1 << 8);
}
}
fn set_return_value(&mut self, value: u64) {
// TODO implement ABI for returning 64-bit values via EAX/EDX
if value & (1 << 63) != 0 {
assert_eq!(value & 0xFFFFFFFF00000000, 0xFFFFFFFF00000000);
}
self.eax = value as u32;
}
}
global_asm!(include_str!("context.S"), options(att_syntax));
-108
View File
@@ -1,108 +0,0 @@
use core::{cell::UnsafeCell, ptr::addr_of_mut};
use kernel_arch_interface::guard::IrqGuard;
pub use kernel_arch_x86::gdt::{Entry, Pointer};
use crate::ArchitectureImpl;
#[allow(dead_code)]
#[repr(C, packed)]
pub struct Tss {
prev_tss: u32,
pub esp0: u32,
pub ss0: u16,
_res0: u16,
esp1: u32,
ss1: u16,
_res1: u16,
esp2: u32,
ss2: u16,
_res2: u16,
cr3: u32,
eip: u32,
eflags: u32,
eax: u32,
ecx: u32,
edx: u32,
ebx: u32,
esp: u32,
ebp: u32,
esi: u32,
edi: u32,
es: u32,
cs: u32,
ss: u32,
ds: u32,
fs: u32,
gs: u32,
ldt: u32,
trap: u16,
iomap_base: u16,
}
impl Tss {
const NULL: Self = Self {
prev_tss: 0,
esp0: 0,
ss0: 0x10,
_res0: 0,
esp1: 0,
ss1: 0,
_res1: 0,
esp2: 0,
ss2: 0,
_res2: 0,
cr3: 0,
eip: 0,
eflags: 0,
eax: 0,
ecx: 0,
edx: 0,
ebx: 0,
esp: 0,
ebp: 0,
esi: 0,
edi: 0,
es: 0,
cs: 0,
ss: 0,
ds: 0,
fs: 0,
gs: 0,
ldt: 0,
trap: 0,
iomap_base: 0,
};
}
pub static mut TSS: Tss = Tss::NULL;
pub static mut GDT: UnsafeCell<[Entry; 7]> = UnsafeCell::new([
Entry::NULL, // 0x00
Entry::RING0_CS32, // 0x08
Entry::RING0_DS32, // 0x10
Entry::RING3_CS32, // 0x1B
Entry::RING3_DS32, // 0x23
Entry::NULL, // 0x28, TSS
Entry::RING3_GS32, // 0x33, Task GS
]);
pub fn create_gdt() -> (&'static [Entry], &'static Tss) {
// Won't be deallocated, so leaks are not a concern
let tss = unsafe { &mut *addr_of_mut!(TSS) };
tss.ss0 = 0x10;
let tss_addr = (tss as *mut Tss).addr();
#[allow(static_mut_refs)]
let gdt = unsafe { GDT.get_mut() };
gdt[5] = Entry::tss(tss_addr as u32, (size_of::<Tss>() - 1) as u32);
(gdt, tss)
}
pub fn set_gs_base(gs_base: usize) {
let _guard = IrqGuard::<ArchitectureImpl>::acquire();
unsafe {
#[allow(static_mut_refs)]
GDT.get_mut()[6].set_base(gs_base);
core::arch::asm!("mov $0x33, %ax; mov %ax, %gs", out("ax") _, options(att_syntax, nostack));
}
}
-142
View File
@@ -1,142 +0,0 @@
#![feature(never_type, naked_functions, trace_macros)]
#![no_std]
extern crate alloc;
use core::ptr::null_mut;
use alloc::vec::Vec;
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuData, CpuImpl, IpiQueue},
task::Scheduler,
Architecture,
};
pub mod context;
pub mod gdt;
pub mod mem;
pub use context::TaskContextImpl;
use kernel_arch_x86::cpuid::CpuFeatures;
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
pub struct ArchitectureImpl;
#[repr(C)]
pub struct PerCpuData {
pub available_features: CpuFeatures,
pub enabled_features: CpuFeatures,
}
impl CpuData for PerCpuData {}
static mut CPU: *mut () = null_mut();
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!(
r#"
1:
nop
jmp 1b
"#,
options(att_syntax)
);
}
}
impl Architecture for ArchitectureImpl {
type PerCpuData = PerCpuData;
type CpuFeatures = CpuFeatures;
type BreakpointType = u8;
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
use alloc::boxed::Box;
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(
id.expect("x86_64 required manual CPU ID set"),
data,
)));
cpu.set_local();
}
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
core::arch::asm!("cli");
} else {
core::arch::asm!("sti");
}
old
}
fn interrupt_mask() -> bool {
let mut flags: u32;
unsafe {
core::arch::asm!("pushfl; pop {0:e}", out(reg) flags, options(att_syntax));
}
// If IF is zero, interrupts are disabled (masked)
flags & (1 << 9) == 0
}
fn wait_for_interrupt() {
unsafe {
core::arch::asm!("hlt");
}
}
unsafe fn init_ipi_queues(_queues: Vec<IpiQueue<Self>>) {}
fn local_cpu() -> *mut () {
unsafe { CPU }
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
0
}
unsafe fn set_local_cpu(cpu: *mut ()) {
CPU = cpu;
}
fn cpu_count() -> usize {
1
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
None
}
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
None
}
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
None
}
fn idle_task() -> extern "C" fn(usize) -> ! {
idle_task
}
fn halt() -> ! {
loop {
unsafe {
core::arch::asm!("cli; hlt");
}
}
}
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
Some(&cpu.available_features)
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
Some(&cpu.enabled_features)
}
}
-141
View File
@@ -1,141 +0,0 @@
use kernel_arch_interface::{sync::IrqSafeSpinlock, KERNEL_VIRT_OFFSET};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel, KernelImageObject};
use yggdrasil_abi::error::Error;
use crate::{
mem::{flush_tlb_entry, table::PageAttributes},
ArchitectureImpl,
};
use super::{
table::{PageEntry, PageTable, L0, L3},
KERNEL_TABLES,
};
pub const KERNEL_SPLIT_L0: usize = KERNEL_VIRT_OFFSET >> 22;
pub const DYNAMIC_MAP_COUNT: usize = 64;
pub const FIXED_MAP_COUNT: usize = 1024 - (KERNEL_SPLIT_L0 + DYNAMIC_MAP_COUNT);
pub const DYNAMIC_MAP_OFFSET: usize = (KERNEL_SPLIT_L0 + FIXED_MAP_COUNT) << L0::SHIFT;
pub const MAX_FIXED_PHYSICAL: PhysicalAddress =
PhysicalAddress::from_u64((FIXED_MAP_COUNT as u64) << 22);
#[repr(C)]
pub struct FixedTables {
pub l0: KernelL0,
pub dynamic: IrqSafeSpinlock<ArchitectureImpl, KernelDynamic>,
}
#[repr(C, align(0x1000))]
pub struct KernelL0 {
pub lower: [PageEntry<L0>; KERNEL_SPLIT_L0],
pub kernel: [PageEntry<L0>; FIXED_MAP_COUNT],
pub dynamic: [PageEntry<L0>; DYNAMIC_MAP_COUNT],
}
#[allow(unused)]
pub struct KernelDynamic {
pub l3s: [KernelImageObject<PageTable<L3>>; DYNAMIC_MAP_COUNT],
free: usize,
}
impl FixedTables {
pub const fn zeroed() -> Self {
Self {
l0: KernelL0::zeroed(),
dynamic: IrqSafeSpinlock::new(KernelDynamic::zeroed()),
}
}
pub fn virtualize(&mut self, address: PhysicalAddress) -> usize {
if address < MAX_FIXED_PHYSICAL {
// It's a fixed address
address.into_u64() as usize + KERNEL_VIRT_OFFSET
} else {
todo!()
}
}
pub fn physicalize(&mut self, address: usize) -> Option<PhysicalAddress> {
if address < KERNEL_VIRT_OFFSET {
return None;
}
if address < KERNEL_VIRT_OFFSET + MAX_FIXED_PHYSICAL.into_u64() as usize {
// It's a fixed address
Some(PhysicalAddress::from_usize(address - KERNEL_VIRT_OFFSET))
} else {
todo!()
}
}
pub fn map_dynamic_memory(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
self.dynamic.lock().map(base, page_count)
}
}
impl KernelL0 {
pub const fn zeroed() -> Self {
Self {
lower: [PageEntry::INVALID; KERNEL_SPLIT_L0],
kernel: [PageEntry::INVALID; FIXED_MAP_COUNT],
dynamic: [PageEntry::INVALID; DYNAMIC_MAP_COUNT],
}
}
}
impl KernelDynamic {
pub const fn zeroed() -> Self {
Self {
l3s: [const { unsafe { KernelImageObject::new(PageTable::zeroed()) } };
DYNAMIC_MAP_COUNT],
free: DYNAMIC_MAP_COUNT * 1024,
}
}
fn map(&mut self, base: u64, page_count: usize) -> Result<usize, Error> {
if page_count > self.free {
return Err(Error::OutOfMemory);
}
'l0: for i in 0..DYNAMIC_MAP_COUNT * 1024 - page_count {
for j in 0..page_count {
let entry = self.entry(i + j);
if entry.is_present() {
continue 'l0;
}
}
self.free -= page_count;
for j in 0..page_count {
let address = PhysicalAddress::from_u64(base + ((j as u64) << L3::SHIFT));
*self.entry_mut(i + j) = PageEntry::page(address, PageAttributes::WRITABLE);
unsafe {
flush_tlb_entry(DYNAMIC_MAP_OFFSET + ((i + j) << L3::SHIFT));
}
}
let addr = DYNAMIC_MAP_OFFSET + (i << L3::SHIFT);
return Ok(addr);
}
Err(Error::OutOfMemory)
}
fn entry(&self, index: usize) -> &PageEntry<L3> {
&self.l3s[index / 1024][index % 1024]
}
fn entry_mut(&mut self, index: usize) -> &mut PageEntry<L3> {
&mut self.l3s[index / 1024][index % 1024]
}
}
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
let tables = KERNEL_TABLES.lock();
for (i, entry) in tables.l0.kernel.iter().enumerate() {
dst[i + KERNEL_SPLIT_L0] = *entry;
}
for (i, entry) in tables.l0.dynamic.iter().enumerate() {
dst[i + KERNEL_SPLIT_L0 + FIXED_MAP_COUNT] = *entry;
}
}
-131
View File
@@ -1,131 +0,0 @@
use fixed::FixedTables;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock, KERNEL_VIRT_OFFSET,
};
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
table::{page_count, EntryLevel},
};
use table::{PageAttributes, PageEntry, L0, L3};
use yggdrasil_abi::error::Error;
pub mod fixed;
pub mod process;
pub mod table;
pub use process::ProcessAddressSpaceImpl;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
split_spinlock! {
use libk_mm_interface::KernelImageObject;
use crate::mem::FixedTables;
use crate::ArchitectureImpl;
#[link_section = ".data.tables"]
static KERNEL_TABLES: KernelImageObject<FixedTables> = unsafe {
KernelImageObject::new(FixedTables::zeroed())
};
}
impl KernelTableManager for KernelTableManagerImpl {
unsafe fn map_device_pages(
base: u64,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
// TODO page align up
let offset = (base & 0xFFF) as usize;
let base = base & !0xFFF;
let end = (base + count as u64 + 0xFFF) & !0xFFF;
// assert_eq!(base & 0xFFF, 0);
if end < fixed::MAX_FIXED_PHYSICAL.into_u64() {
// 1:1
let address = Self::virtualize(base);
Ok(RawDeviceMemoryMapping::from_raw_parts(
address, address, 0, 0,
))
} else {
assert_eq!(base & 0xFFF, 0);
log::info!("map_device_pages({:#x}, {})", base, count);
let page_count = page_count::<L3>(count);
let virt = KERNEL_TABLES.lock().map_dynamic_memory(base, page_count)?;
Ok(RawDeviceMemoryMapping::from_raw_parts(
virt + offset,
virt,
page_count,
0,
))
}
}
unsafe fn unmap_device_pages(_mapping: &RawDeviceMemoryMapping<Self>) {
// todo!()
}
fn virtualize(phys: u64) -> usize {
KERNEL_TABLES
.lock()
.virtualize(PhysicalAddress::from_u64(phys))
}
fn physicalize(virt: usize) -> u64 {
KERNEL_TABLES
.lock()
.physicalize(virt)
.expect("Invalid virtual address")
.into_u64()
}
unsafe fn unmap_physical_address(virt: usize) {
if virt < KERNEL_VIRT_OFFSET {
panic!("Invalid 'virtualized' address: {:#x}", virt);
}
let virt = virt - KERNEL_VIRT_OFFSET;
if virt >= fixed::FIXED_MAP_COUNT << L0::SHIFT {
todo!()
}
}
}
/// Sets up fixed MMU translation tables.
///
/// # Safety
///
/// Only meant to be called once during early OS init.
pub unsafe fn init_fixed_tables() {
let mut tables = KERNEL_TABLES.lock();
// Unmap lower stuff
for (i, entry) in tables.l0.lower.iter_mut().enumerate() {
*entry = PageEntry::INVALID;
flush_tlb_entry(i << 22);
}
// Map the rest of fixed translation
for (i, entry) in tables.l0.kernel.iter_mut().enumerate() {
let virt = KERNEL_VIRT_OFFSET + (i << L0::SHIFT);
let phys = (i << L0::SHIFT) as u32;
*entry = PageEntry::block(PhysicalAddress::from_u32(phys), PageAttributes::WRITABLE);
flush_tlb_entry(virt);
}
let dynamic_len = tables.l0.dynamic.len();
for i in 0..dynamic_len {
let phys = tables.dynamic.lock().l3s[i].as_physical_address();
tables.l0.dynamic[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
}
/// # Safety
///
/// `address` must be page-aligned.
#[inline]
pub unsafe fn flush_tlb_entry(address: usize) {
core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax));
}
-137
View File
@@ -1,137 +0,0 @@
use core::marker::PhantomData;
use kernel_arch_interface::KERNEL_VIRT_OFFSET;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::{mem::flush_tlb_entry, KernelTableManagerImpl};
use super::{
fixed::{clone_kernel_tables, KERNEL_SPLIT_L0},
table::{PageEntry, PageTable, L0, L3},
};
#[repr(C)]
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
l0: PhysicalRefMut<'static, PageTable<L0>, KernelTableManagerImpl>,
_alloc: PhantomData<TA>,
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
const UPPER_LIMIT_PFN: usize = KERNEL_VIRT_OFFSET >> L3::SHIFT;
const LOWER_LIMIT_PFN: usize = 32;
fn new() -> Result<Self, Error> {
let mut l0 = unsafe {
PhysicalRefMut::<'static, PageTable<L0>, KernelTableManagerImpl>::map(
TA::allocate_page_table()?,
)
};
for i in 0..1024 {
l0[i] = PageEntry::INVALID;
}
clone_kernel_tables(&mut l0);
Ok(Self {
l0,
_alloc: PhantomData,
})
}
unsafe fn clear(&mut self) {
self.l0.drop_range::<TA>(0..KERNEL_SPLIT_L0);
}
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
self.read_l3_entry(address).ok_or(Error::DoesNotExist)
}
unsafe fn map_page(
&mut self,
address: usize,
physical: PhysicalAddress,
flags: MapAttributes,
) -> Result<(), Error> {
self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false)
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
self.pop_l3_entry(address)
}
fn as_address_with_asid(&self) -> (u64, u64) {
(unsafe { self.l0.as_physical_address().into_u64() }, 0)
}
}
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
// Write a single 4KiB entry
fn write_l3_entry(
&mut self,
virt: usize,
entry: PageEntry<L3>,
overwrite: bool,
) -> Result<(), Error> {
let l0i = virt.page_index::<L0>();
let l3i = virt.page_index::<L3>();
let mut l3 = self.l0.get_mut_or_alloc::<TA>(l0i)?;
if l3[l3i].is_present() && !overwrite {
todo!();
}
l3[l3i] = entry;
unsafe {
flush_tlb_entry(virt);
}
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
let l0i = virt.page_index::<L0>();
let l3i = virt.page_index::<L3>();
let mut l3 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?;
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
l3[l3i] = PageEntry::INVALID;
unsafe {
flush_tlb_entry(virt);
}
Ok(page)
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
let l0i = virt.page_index::<L0>();
let l3i = virt.page_index::<L3>();
let l3 = self.l0.get(l0i)?;
let page = l3[l3i].as_page()?;
Some((page.add(virt & 0xFFF), l3[l3i].attributes().into()))
}
}
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
fn drop(&mut self) {
// SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
// is safe, no one refers to the memory
unsafe {
self.clear();
let l0_phys = self.l0.as_physical_address();
TA::free_page_table(l0_phys);
}
}
}
-256
View File
@@ -1,256 +0,0 @@
use core::{
marker::PhantomData,
ops::{Index, IndexMut, Range},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
table::{
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::KernelTableManagerImpl;
bitflags! {
/// Describes how each page table entry is mapped
pub struct PageAttributes: u32 {
/// When set, the mapping is considered valid and pointing somewhere
const PRESENT = 1 << 0;
/// For tables, allows writes to further translation levels, for pages/blocks, allows
/// writes to the region covered by the entry
const WRITABLE = 1 << 1;
/// When set for L2 entries, the mapping specifies a 2MiB page instead of a page table
/// reference
const BLOCK = 1 << 7;
/// For tables, allows user access to further translation levels, for pages/blocks, allows
/// user access to the region covered by the entry
const USER = 1 << 2;
}
}
// TODO stuff for PAE?
#[derive(Debug, Clone, Copy)]
pub struct L3;
#[derive(Debug, Clone, Copy)]
pub struct L0;
#[derive(Clone, Copy, Debug)]
pub struct PageEntry<L: EntryLevel>(u32, PhantomData<L>);
#[derive(Clone, Copy, Debug)]
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
data: [PageEntry<L>; 1024],
}
impl EntryLevel for L3 {
const SHIFT: usize = 12;
}
impl EntryLevel for L0 {
const SHIFT: usize = 22;
}
impl NonTerminalEntryLevel for L0 {
type NextLevel = L3;
}
impl PageEntry<L3> {
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(),
PhantomData,
)
}
pub fn as_page(&self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
} else {
None
}
}
}
impl PageEntry<L0> {
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
address.try_into_u32().unwrap()
| (PageAttributes::PRESENT | PageAttributes::BLOCK | attrs).bits(),
PhantomData,
)
}
pub fn table(address: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
address.try_into_u32().unwrap() | (PageAttributes::PRESENT | attrs).bits(),
PhantomData,
)
}
pub fn as_table(&self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_u32(self.0 & !0xFFF))
} else {
None
}
}
}
impl<L: EntryLevel> PageEntry<L> {
pub const INVALID: Self = Self(0, PhantomData);
pub fn is_present(&self) -> bool {
self.0 & (1 << 0) != 0
}
pub fn attributes(&self) -> PageAttributes {
PageAttributes::from_bits_retain(self.0)
}
}
impl<L: EntryLevel> PageTable<L> {
pub const fn zeroed() -> Self {
Self {
data: [PageEntry::INVALID; 1024],
}
}
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, Self, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
for i in 0..1024 {
table[i] = PageEntry::INVALID;
}
Ok(table)
}
/// Recursively clears and deallocates the translation table.
///
/// # Safety
///
/// The caller must ensure the table is no longer in use and is not referenced anymore.
pub unsafe fn free<TA: TableAllocator>(this: PhysicalRefMut<Self, KernelTableManagerImpl>) {
let physical = this.as_physical_address();
TA::free_page_table(physical);
}
}
impl NextPageTable for PageTable<L0> {
type NextLevel = PageTable<L3>;
type TableRef = PhysicalRef<'static, Self::NextLevel, KernelTableManagerImpl>;
type TableRefMut = PhysicalRefMut<'static, Self::NextLevel, KernelTableManagerImpl>;
fn get(&self, index: usize) -> Option<Self::TableRef> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRef::map(addr) })
}
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
self[index]
.as_table()
.map(|addr| unsafe { PhysicalRefMut::map(addr) })
}
fn get_mut_or_alloc<TA: TableAllocator>(
&mut self,
index: usize,
) -> Result<Self::TableRefMut, Error> {
let entry = self[index];
if let Some(table) = entry.as_table() {
Ok(unsafe { PhysicalRefMut::map(table) })
} else {
let table = PageTable::new_zeroed::<TA>()?;
self[index] = PageEntry::<L0>::table(
unsafe { table.as_physical_address() },
PageAttributes::WRITABLE | PageAttributes::USER,
);
Ok(table)
}
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
fn index(&self, index: usize) -> &Self::Output {
&self.data[index]
}
}
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.data[index]
}
}
impl EntryLevelDrop for PageTable<L3> {
const FULL_RANGE: Range<usize> = 0..1024;
unsafe fn drop_range<TA: TableAllocator>(&mut self, _range: Range<usize>) {}
}
impl EntryLevelDrop for PageTable<L0> {
const FULL_RANGE: Range<usize> = 0..1024;
unsafe fn drop_range<TA: TableAllocator>(&mut self, range: Range<usize>) {
for index in range {
let entry = self[index];
if let Some(table) = entry.as_table() {
let mut table_ref: PhysicalRefMut<PageTable<L3>, KernelTableManagerImpl> =
PhysicalRefMut::map(table);
table_ref.drop_all::<TA>();
TA::free_page_table(table);
} else if entry.is_present() {
// Memory must've been cleared beforehand, so no non-table entries must be present
panic!(
"Expected a table containing only tables, got table[{}] = {:#x?}",
index, entry.0
);
}
self[index] = PageEntry::INVALID;
}
}
}
impl From<MapAttributes> for PageAttributes {
fn from(value: MapAttributes) -> Self {
let mut res = PageAttributes::WRITABLE;
if value.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
res |= PageAttributes::USER;
}
res
}
}
impl From<PageAttributes> for MapAttributes {
fn from(value: PageAttributes) -> Self {
let mut res = MapAttributes::empty();
if value.contains(PageAttributes::USER) {
res |= MapAttributes::USER_READ;
if value.contains(PageAttributes::WRITABLE) {
res |= MapAttributes::USER_WRITE;
}
}
// TODO ???
res |= MapAttributes::NON_GLOBAL;
res
}
}
-2
View File
@@ -21,8 +21,6 @@ pub mod sync;
pub mod task;
pub mod util;
#[cfg(any(target_arch = "x86", rust_analyzer))]
pub const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", rust_analyzer))]
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
-3
View File
@@ -2,9 +2,6 @@ use core::{fmt, marker::PhantomData, mem::size_of, ptr::NonNull};
use yggdrasil_abi::error::Error;
pub mod address;
pub mod table;
pub trait PhysicalMemoryAllocator {
type Address;
+2 -2
View File
@@ -42,7 +42,7 @@ impl<A: Architecture, T> Spinlock<A, T> {
}
}
pub fn lock(&self) -> SpinlockGuard<A, T> {
pub fn lock(&self) -> SpinlockGuard<'_, A, T> {
// Loop until the lock can be acquired
if LOCK_HACK.load(Ordering::Acquire) {
return SpinlockGuard { lock: self };
@@ -103,7 +103,7 @@ impl<A: Architecture, T> IrqSafeSpinlock<A, T> {
}
/// Attempts to acquire a lock. IRQs will be disabled until the lock is released.
pub fn lock(&self) -> IrqSafeSpinlockGuard<A, T> {
pub fn lock(&self) -> IrqSafeSpinlockGuard<'_, A, T> {
// Disable IRQs to avoid IRQ handler trying to acquire the same lock
let irq_guard = IrqGuard::acquire();
-7
View File
@@ -7,7 +7,6 @@ edition = "2024"
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
memtables.workspace = true
device-api = { workspace = true, features = ["derive"] }
tock-registers.workspace = true
@@ -16,11 +15,5 @@ static_assertions.workspace = true
log.workspace = true
cfg-if.workspace = true
[features]
default = []
riscv64_board_virt = []
riscv64_board_jh7110 = []
[lints]
workspace = true
+2 -3
View File
@@ -13,7 +13,7 @@ use tock_registers::{
use yggdrasil_abi::error::Error;
use crate::{
mem::{self, KERNEL_VIRT_OFFSET},
mem::{self},
registers::SATP,
ArchitectureImpl, PerCpuData,
};
@@ -124,8 +124,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
// TODO stack is leaked
let satp = InMemoryRegister::new(0);
let kernel_table_phys =
((&raw const mem::KERNEL_TABLES).addr() - KERNEL_VIRT_OFFSET) as u64;
let kernel_table_phys = mem::fixed::table_physical_address().into_u64();
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
Ok(Self {
+9 -5
View File
@@ -1,4 +1,4 @@
#![feature(decl_macro, naked_functions)]
#![feature(decl_macro)]
#![no_std]
extern crate alloc;
@@ -44,6 +44,7 @@ pub struct PerCpuData {
}
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
pub static mut BOOT_HART_ID: u64 = 0;
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
static HART_TO_QUEUE: IrqSafeSpinlock<ArchitectureImpl, BTreeMap<u32, usize>> =
IrqSafeSpinlock::new(BTreeMap::new());
@@ -60,11 +61,14 @@ impl CpuData for PerCpuData {
}
}
#[naked]
/// Returns the ID of the bootstrap HART
pub fn boot_hart_id() -> u64 {
unsafe { BOOT_HART_ID }
}
#[unsafe(naked)]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!("1: nop; j 1b");
}
core::arch::naked_asm!("1: nop; j 1b");
}
impl ArchitectureImpl {
+32
View File
@@ -0,0 +1,32 @@
use kernel_arch_interface::sync::IrqSafeSpinlock;
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use crate::{
mem::{
auto_lower_address,
table::{PageEntry, PageTable, L1},
KERNEL_VIRT_OFFSET,
},
ArchitectureImpl,
};
pub const IDENTITY_SIZE_L1: usize = 64;
pub(super) static mut KERNEL_L1: PageTable<L1> = const {
let mut table = PageTable::zeroed();
let mut index = 0;
while index < IDENTITY_SIZE_L1 {
let entry = PageEntry::identity_block(PhysicalAddress::from_usize(index << L1::SHIFT));
table.entries[index] = entry;
table.entries[index + ((KERNEL_VIRT_OFFSET >> L1::SHIFT) & 0x1FF)] = entry;
index += 1;
}
table
};
pub(super) static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
pub fn table_physical_address() -> PhysicalAddress {
PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_L1))
}
+51
View File
@@ -0,0 +1,51 @@
use libk_mm_interface::table::{EntryLevel, EntryLevelExt};
use crate::mem::table::L3;
pub fn tlb_flush_global_full() {
tlb_flush_full();
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_global_va(va: usize) {
tlb_flush_va(va);
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_range_va(start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va(page);
}
}
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va_asid(page, asid);
}
}
#[inline]
pub fn tlb_flush_full() {
unsafe { core::arch::asm!("sfence.vma") };
}
#[inline]
pub fn tlb_flush_va(va: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
}
#[inline]
pub fn tlb_flush_asid(asid: usize) {
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
}
#[inline]
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
}
+41 -295
View File
@@ -1,70 +1,26 @@
use cfg_if::cfg_if;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock,
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
};
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use memtables::riscv64::PageAttributes;
use static_assertions::{const_assert, const_assert_eq};
use table::{PageEntry, PageTable, L1, L2, L3};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::error::Error;
pub use memtables::riscv64::FixedTables;
use crate::{
mem::table::{PageTable, L1, L3},
registers::SATP,
};
use crate::registers::SATP;
pub use intrinsics::*;
pub mod fixed;
pub mod intrinsics;
pub mod process;
pub mod table;
split_spinlock! {
use crate::ArchitectureImpl;
use crate::mem::FixedTables;
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
#[used]
static KERNEL_TABLES: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
cfg_if! {
if #[cfg(feature = "riscv64_board_virt")] {
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
} else if #[cfg(feature = "riscv64_board_jh7110")] {
pub const KERNEL_PHYS_BASE: usize = 0x40200000;
} else if #[cfg(rust_analyzer)] {
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
}
}
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
const_assert_eq!(KERNEL_L2I, 1);
// Runtime mappings
// 1GiB of device memory space
const DEVICE_MAPPING_L1I: usize = KERNEL_START_L1I + 1;
const DEVICE_MAPPING_L3_COUNT: usize = 4;
// 32GiB of RAM space
const RAM_MAPPING_START_L1I: usize = KERNEL_START_L1I + 2;
const RAM_MAPPING_L1_COUNT: usize = 32;
const_assert!(RAM_MAPPING_START_L1I + RAM_MAPPING_L1_COUNT <= 512);
const_assert!(DEVICE_MAPPING_L1I < 512);
const DEVICE_MAPPING_OFFSET: usize = (DEVICE_MAPPING_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
const RAM_MAPPING_OFFSET: usize = (RAM_MAPPING_START_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
// Runtime tables
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
[const { PageTable::zeroed() }; DEVICE_MAPPING_L3_COUNT];
/// Any VAs above this one are sign-extended
pub const USER_BOUNDARY: usize = 0x40_00000000;
@@ -75,17 +31,20 @@ pub struct KernelTableManagerImpl;
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address >= RAM_MAPPING_OFFSET {
panic!("Invalid physical address: {address:#x}");
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
} else {
panic!("Invalid physical address: {address:#x}")
}
address + RAM_MAPPING_OFFSET
}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET {
panic!("Invalid \"physicalized\" virtual address {address:#x}");
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
{
panic!("Invalid virtualized address: {address:#x}");
}
(address - RAM_MAPPING_OFFSET) as u64
(address - KERNEL_VIRT_OFFSET) as u64
}
unsafe fn map_device_pages(
@@ -93,146 +52,32 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
unsafe { map_device_memory(PhysicalAddress::from_u64(base), count, attrs) }
let _ = attrs;
let _lock = fixed::LOCK.lock();
let base = PhysicalAddress::from_u64(base);
let l3_aligned_base = base.page_align_down::<L3>();
let l3_aligned_end = base.add(count).page_align_up::<L3>();
let l3_offset = base - l3_aligned_base;
let l3_page_count = (l3_aligned_end - l3_aligned_base).page_count::<L3>();
let l3_aligned_virt = l3_aligned_base.add(KERNEL_VIRT_OFFSET).into_usize();
Ok(unsafe {
RawDeviceMemoryMapping::from_raw_parts(
l3_aligned_base.into_u64(),
l3_aligned_virt + l3_offset,
l3_aligned_virt,
l3_page_count,
L3::SIZE,
)
})
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
unsafe { unmap_device_memory(mapping) }
let _ = mapping;
}
}
// Device mappings
unsafe fn map_device_memory_l3(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
// TODO don't map pages if already mapped
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
unsafe {
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
continue 'l0;
}
}
}
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
unsafe {
DEVICE_MAPPING_L3S[l2i][l3i] =
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::W);
}
}
let start = DEVICE_MAPPING_OFFSET + i * L3::SIZE;
tlb_flush_range_va(start, count * L3::SIZE);
return Ok(start);
}
Err(Error::OutOfMemory)
}
#[allow(unused)]
unsafe fn map_device_memory_l2(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
for j in 0..count {
unsafe {
if DEVICE_MAPPING_L2[i + j].is_present() {
continue 'l0;
}
}
}
unsafe {
for j in 0..count {
DEVICE_MAPPING_L2[i + j] =
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::W);
}
}
let start = DEVICE_MAPPING_OFFSET + i * L2::SIZE;
tlb_flush_range_va(start, count * L2::SIZE);
return Ok(start);
}
Err(Error::OutOfMemory)
}
pub(crate) unsafe fn map_device_memory(
base: PhysicalAddress,
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
let l3_aligned = base.page_align_down::<L3>();
let l3_offset = base.page_offset::<L3>();
let page_count = (l3_offset + size).page_count::<L3>();
if page_count > 256 {
// Large mapping, use L2 mapping instead
let l2_aligned = base.page_align_down::<L2>();
let l2_offset = base.page_offset::<L2>();
let page_count = (l2_offset + size).page_count::<L2>();
unsafe {
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l2_aligned.into_u64(),
address,
base_address,
page_count,
L2::SIZE,
))
}
} else {
// Just map the pages directly
unsafe {
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l3_aligned.into_u64(),
address,
base_address,
page_count,
L3::SIZE,
))
}
}
}
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
match map.page_size {
L3::SIZE => {
for i in 0..map.page_count {
let page = map.base_address + i * L3::SIZE;
let l2i = page.page_index::<L2>();
let l3i = page.page_index::<L3>();
unsafe {
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
}
}
tlb_flush_range_va(map.base_address, map.page_count * L3::SIZE);
}
L2::SIZE => todo!(),
_ => unimplemented!(),
}
}
pub fn auto_address<T>(x: *const T) -> usize {
pub fn auto_lower_address<T>(x: *const T) -> usize {
let x = x.addr();
if x >= KERNEL_VIRT_OFFSET {
x - KERNEL_VIRT_OFFSET
@@ -247,113 +92,14 @@ pub fn auto_address<T>(x: *const T) -> usize {
///
/// Only meant to be called once per each HART during their early init.
pub unsafe fn enable_mmu() {
let l1_phys = auto_address(&raw const KERNEL_TABLES) as u64;
let l1_phys = auto_lower_address(&raw const fixed::KERNEL_L1) as u64;
tlb_flush_full();
SATP.write(SATP::PPN.val(l1_phys >> 12) + SATP::MODE::Sv39);
}
/// Removes the lower half translation mappings.
///
/// # Safety
///
/// Needs to be called once after secondary HARTs are initialized.
pub unsafe fn unmap_lower_half() {
let mut tables = KERNEL_TABLES.lock();
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
tables.l1.data[kernel_l1i_lower] = 0;
tlb_flush_range_va(0x0, L1::SIZE);
}
/// Sets up run-time kernel translation tables.
///
/// # Safety
///
/// The caller must ensure MMU is already enabled.
pub unsafe fn setup_fixed_tables() {
let mut tables = KERNEL_TABLES.lock();
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
// Set up static runtime mappings
for i in 0..DEVICE_MAPPING_L3_COUNT {
unsafe {
let device_mapping_l3_phys = PhysicalAddress::from_usize(
(&raw const DEVICE_MAPPING_L3S[i]).addr() - KERNEL_VIRT_OFFSET,
);
DEVICE_MAPPING_L2[i] =
PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
}
}
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
tables.l1.data[DEVICE_MAPPING_L1I] =
((device_mapping_l2_phys as u64) >> 2) | PageAttributes::V.bits();
for l1i in 0..RAM_MAPPING_L1_COUNT {
let physical = (l1i as u64) << L1::SHIFT;
tables.l1.data[l1i + RAM_MAPPING_START_L1I] = (physical >> 2)
| (PageAttributes::R
| PageAttributes::W
| PageAttributes::A
| PageAttributes::D
| PageAttributes::V)
.bits();
}
tlb_flush_full();
}
pub fn tlb_flush_global_full() {
tlb_flush_full();
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_global_va(va: usize) {
tlb_flush_va(va);
// TODO send TLB shootdown IPI to other harts
}
pub fn tlb_flush_range_va(start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va(page);
}
}
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
let end = (start + size).page_align_up::<L3>();
let start = start.page_align_down::<L3>();
for page in (start..end).step_by(L3::SIZE) {
tlb_flush_va_asid(page, asid);
}
}
#[inline]
pub fn tlb_flush_full() {
unsafe { core::arch::asm!("sfence.vma") };
}
#[inline]
pub fn tlb_flush_va(va: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
}
#[inline]
pub fn tlb_flush_asid(asid: usize) {
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
}
#[inline]
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
}
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
let tables = KERNEL_TABLES.lock();
let _lock = fixed::LOCK.lock();
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
dst[l1i] = unsafe { fixed::KERNEL_L1[l1i] };
}
}
+49 -7
View File
@@ -6,15 +6,17 @@ use core::{
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
};
use memtables::riscv64::PageAttributes;
use yggdrasil_abi::error::Error;
use crate::mem::{clone_kernel_tables, table::PageEntry};
use crate::mem::{
clone_kernel_tables,
table::{PageAttributes, PageEntry},
};
use super::{
table::{DroppableRange, PageTable, L1, L2, L3},
@@ -70,7 +72,15 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
Ok(())
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
unsafe fn update_page_attributes(
&mut self,
address: usize,
update: &PageAttributeUpdate,
) -> Result<(), Error> {
self.update_l3_entry(address, |entry| entry.update(update))
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<(PhysicalAddress, bool), Error> {
self.pop_l3_entry(address)
}
@@ -118,7 +128,11 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
fn update_l3_entry<F: FnOnce(&mut PageEntry<L3>) -> Result<(), Error>>(
&mut self,
virt: usize,
mapper: F,
) -> Result<(), Error> {
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
@@ -127,12 +141,33 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
let mut l2 = self.l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
let entry = &mut l3[l3i];
if !entry.is_present() {
return Err(Error::DoesNotExist);
}
mapper(entry)?;
super::tlb_flush_va_asid(virt, self.asid as usize);
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<(PhysicalAddress, bool), Error> {
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
// TODO somehow drop tables if they're known to be empty?
let mut l2 = self.l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let entry = l3[l3i];
let page = entry.as_page().ok_or(Error::DoesNotExist)?;
let dirty = entry.is_dirty();
l3[l3i] = PageEntry::INVALID;
super::tlb_flush_va_asid(virt, self.asid as usize);
Ok(page)
Ok((page, dirty))
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
@@ -178,6 +213,9 @@ fn to_page_attributes(src: MapAttributes) -> PageAttributes {
if src.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
result |= PageAttributes::U;
}
if src.contains(MapAttributes::DIRTY) {
result |= PageAttributes::SW_DIRTY;
}
result
}
@@ -192,5 +230,9 @@ fn to_map_attributes(src: PageAttributes) -> MapAttributes {
}
}
if src.contains(PageAttributes::SW_DIRTY) {
result |= MapAttributes::DIRTY;
}
result
}
+95 -4
View File
@@ -1,11 +1,14 @@
use core::{
fmt,
marker::PhantomData,
ops::{Index, IndexMut, Range},
};
use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
process::PageAttributeUpdate,
table::{
page_index, EntryLevel, EntryLevelDrop, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
@@ -15,7 +18,40 @@ use yggdrasil_abi::error::Error;
use super::{KernelTableManagerImpl, USER_BOUNDARY};
pub use memtables::riscv64::PageAttributes;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct PageAttributes: u64 {
const N = 1 << 63;
/// Software-tracked dirty bit (RSW[0])
const SW_DIRTY = 1 << 9;
/// Dirty bit
const D = 1 << 7;
/// Access bit
const A = 1 << 6;
/// Global mapping bit, implies all lower levels are also global
const G = 1 << 5;
/// U-mode access permission
const U = 1 << 4;
/// Execute permission
const X = 1 << 3;
/// Write permission
const W = 1 << 2;
/// Read-permission
const R = 1 << 1;
/// Valid bit
const V = 1 << 0;
}
// X W R Meaning
// 0 0 0 Pointer to next level of page table
// 0 0 1 Read-only page
// 0 1 0 ---
// 0 1 1 Read-write page
// 1 0 0 Execute only
// 1 0 1 Read-execute page
// 1 1 0 ---
// 1 1 1 Read-write-execute page
}
/// L3 - entry is 4KiB
#[derive(Debug, Clone, Copy)]
@@ -41,7 +77,7 @@ impl EntryLevel for L1 {
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
entries: [PageEntry<L>; 512],
pub(crate) entries: [PageEntry<L>; 512],
}
#[derive(Clone, Copy, Debug, PartialEq)]
@@ -88,6 +124,8 @@ impl<L: EntryLevel> PageTable<L> {
}
impl<L: EntryLevel> PageEntry<L> {
// Upper + lower 10 bits
const ATTR_MASK: u64 = 0xFFC00000000003FF;
pub const INVALID: Self = Self(0, PhantomData);
/// Constructs a [PageEntry] from its raw representation.
@@ -103,6 +141,23 @@ impl<L: EntryLevel> PageEntry<L> {
self.0 & PageAttributes::V.bits() != 0
}
pub fn update(&mut self, update: &PageAttributeUpdate) -> Result<(), Error> {
let mut attrs = self.attributes();
if let Some(write) = update.user_write {
attrs.set(PageAttributes::W, write);
}
if let Some(dirty) = update.dirty {
attrs.set(PageAttributes::SW_DIRTY, dirty);
}
self.0 &= !Self::ATTR_MASK;
self.0 |= attrs.bits() & Self::ATTR_MASK;
Ok(())
}
pub const fn is_dirty(&self) -> bool {
self.0 & PageAttributes::SW_DIRTY.bits() != 0
}
pub fn attributes(self) -> PageAttributes {
PageAttributes::from_bits_retain(self.0)
}
@@ -184,6 +239,19 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
}
impl<L: NonTerminalEntryLevel> PageEntry<L> {
pub const fn identity_block(address: PhysicalAddress) -> Self {
Self(
(address.into_u64() >> 2)
| PageAttributes::R.bits()
| PageAttributes::W.bits()
| PageAttributes::X.bits()
| PageAttributes::V.bits()
| PageAttributes::D.bits()
| PageAttributes::A.bits(),
PhantomData,
)
}
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
// TODO validate address alignment
Self(
@@ -211,7 +279,7 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
& (PageAttributes::R | PageAttributes::W | PageAttributes::X | PageAttributes::V)
.bits()
== PageAttributes::V.bits())
.then_some((self.0 << 2) & !0xFFF)
.then_some((self.0 & !Self::ATTR_MASK) << 2)
.map(PhysicalAddress::from_u64)
}
}
@@ -232,7 +300,7 @@ impl PageEntry<L3> {
pub fn as_page(&self) -> Option<PhysicalAddress> {
(self.0 & PageAttributes::V.bits() != 0)
.then_some((self.0 << 2) & !0xFFF)
.then_some((self.0 & !Self::ATTR_MASK) << 2)
.map(PhysicalAddress::from_u64)
}
}
@@ -250,3 +318,26 @@ impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
&mut self.entries[index]
}
}
impl fmt::Display for PageAttributes {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Write;
macro_rules! bit {
($self:ident, $field:expr, $letter:literal) => {
if $self.contains($field) {
f.write_char($letter)
} else {
f.write_char('-')
}
};
}
bit!(self, Self::R, 'r')?;
bit!(self, Self::W, 'w')?;
bit!(self, Self::X, 'x')?;
bit!(self, Self::U, 'u')?;
Ok(())
}
}
-2
View File
@@ -26,8 +26,6 @@ cfg_if! {
extern crate kernel_arch_aarch64 as imp;
} else if #[cfg(target_arch = "x86_64")] {
extern crate kernel_arch_x86_64 as imp;
} else if #[cfg(target_arch = "x86")] {
extern crate kernel_arch_i686 as imp;
} else if #[cfg(target_arch = "riscv64")] {
extern crate kernel_arch_riscv64 as imp;
} else {
+3 -3
View File
@@ -1,5 +1,5 @@
#[allow(dead_code)]
#[repr(packed)]
#[repr(C, packed)]
pub struct Entry {
pub limit_lo: u16,
pub base_lo: u16,
@@ -10,7 +10,7 @@ pub struct Entry {
}
#[allow(dead_code)]
#[repr(packed)]
#[repr(C, packed)]
pub struct Pointer {
pub limit: u16,
pub offset: usize,
@@ -121,7 +121,7 @@ mod imp {
use super::{Entry, Pointer};
#[allow(dead_code)]
#[repr(packed)]
#[repr(C, packed)]
pub struct Tss {
_0: u32,
rsp0: u64,
-1
View File
@@ -7,7 +7,6 @@ edition = "2021"
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk-mm-interface.workspace = true
memtables.workspace = true
device-api = { workspace = true, features = ["derive"] }
kernel-arch-x86.workspace = true
+6 -3
View File
@@ -5,11 +5,14 @@ use kernel_arch_interface::{
task::{ForkFrame, StackBuilder, TaskContext, TaskFrame, UserContextInfo},
};
use kernel_arch_x86::registers::{FpuContext, CR3, MSR_IA32_FS_BASE};
use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress};
use libk_mm_interface::address::PhysicalAddress;
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{mem::KERNEL_TABLES, ArchitectureImpl};
use crate::{
mem::{auto_lower_address, fixed},
ArchitectureImpl,
};
/// Frame saved onto the stack when taking an IRQ
#[derive(Debug)]
@@ -431,7 +434,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
const KERNEL_TASK_PAGES: usize = 32;
let cr3: usize = unsafe { KERNEL_TABLES.lock().as_physical_address() }.into();
let cr3: usize = auto_lower_address(&raw const fixed::KERNEL_PML4); // unsafe { KERNEL_TABLES.lock().as_physical_address() }.into();
let stack_base_phys = PA::allocate_contiguous_pages(KERNEL_TASK_PAGES)?;
let stack_base = stack_base_phys.raw_virtualize::<K>();
+5 -8
View File
@@ -1,6 +1,5 @@
#![no_std]
#![allow(clippy::new_without_default)]
#![feature(naked_functions, trait_upcasting)]
extern crate alloc;
@@ -69,18 +68,16 @@ impl PerCpuData {
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
#[naked]
#[unsafe(naked)]
extern "C" fn idle_task(_: usize) -> ! {
unsafe {
core::arch::naked_asm!(
r#"
core::arch::naked_asm!(
r#"
1:
nop
jmp 1b
"#,
options(att_syntax)
);
}
options(att_syntax)
);
}
impl ArchitectureImpl {
+131
View File
@@ -0,0 +1,131 @@
use core::ops::Range;
use kernel_arch_interface::{mem::DeviceMemoryAttributes, sync::IrqSafeSpinlock, Architecture};
use kernel_arch_x86::registers::CR3;
use libk_mm_interface::{
address::PhysicalAddress,
device::{DevicePageManager, DevicePageTableLevel},
table::{page_index, EntryLevel},
};
use crate::{
mem::{
auto_lower_address,
table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3},
},
ArchitectureImpl, KERNEL_VIRT_OFFSET,
};
pub const IDENTITY_SIZE_L1: usize = 64;
pub const KERNEL_L0I: usize = page_index::<L0>(KERNEL_VIRT_OFFSET);
pub const DEVICE_L1: usize = IDENTITY_SIZE_L1;
pub const DEVICE_MAPPING_L3_COUNT: usize = 32;
pub const DEVICE_MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET + (DEVICE_L1 << L1::SHIFT);
pub static LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
pub static mut KERNEL_PDPT: PageTable<L1> = PageTable::zeroed();
pub static mut KERNEL_PML4: PageTable<L0> = PageTable::zeroed();
pub(super) static mut DEVICE_MEMORY: DevicePageManager<L3DeviceMemory, L2DeviceMemory> =
DevicePageManager::new(
L3DeviceMemory([PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT]),
L2DeviceMemory(PageTable::zeroed()),
);
#[repr(transparent)]
pub struct L2DeviceMemory(pub PageTable<L2>);
#[repr(transparent)]
pub struct L3DeviceMemory(pub [PageTable<L3>; DEVICE_MAPPING_L3_COUNT]);
impl DevicePageTableLevel for L2DeviceMemory {
type Level = L2;
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
const INDEX_RANGE: Range<usize> = DEVICE_MAPPING_L3_COUNT..512;
fn map_page(
&mut self,
index: usize,
physical: PhysicalAddress,
attrs: &DeviceMemoryAttributes,
) {
let _ = attrs;
self.0[index] = PageEntry::<L2>::block(physical, PageAttributes::WRITABLE);
}
fn unmap_page(&mut self, index: usize) {
self.0[index - DEVICE_MAPPING_L3_COUNT] = PageEntry::INVALID;
}
fn is_mapped(&self, index: usize) -> bool {
self.0[index - DEVICE_MAPPING_L3_COUNT].is_present()
}
fn flush_range(range: Range<usize>) {
let _ = range;
}
}
impl DevicePageTableLevel for L3DeviceMemory {
type Level = L3;
const VIRTUAL_BASE: usize = DEVICE_MAPPING_OFFSET;
const INDEX_RANGE: Range<usize> = 0..512 * DEVICE_MAPPING_L3_COUNT;
fn map_page(
&mut self,
index: usize,
physical: PhysicalAddress,
attrs: &DeviceMemoryAttributes,
) {
let _ = attrs;
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i] = PageEntry::page(physical, PageAttributes::WRITABLE);
}
fn unmap_page(&mut self, index: usize) {
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i] = PageEntry::INVALID;
}
fn is_mapped(&self, index: usize) -> bool {
let l2i = index / 512;
let l3i = index % 512;
self.0[l2i][l3i].is_present()
}
fn flush_range(range: Range<usize>) {
let _ = range;
}
}
pub(super) unsafe fn setup(have_1gib_pages: bool) {
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const KERNEL_PDPT));
KERNEL_PML4[KERNEL_L0I] = PageEntry::table(phys, PageAttributes::WRITABLE);
if have_1gib_pages {
for i in 0..IDENTITY_SIZE_L1 {
let phys = PhysicalAddress::from_usize(i * L1::SIZE);
KERNEL_PDPT[i] = PageEntry::<L1>::block(phys, PageAttributes::WRITABLE);
}
} else {
// TODO
ArchitectureImpl::halt();
}
// DEVICE_L1 -> Device L2 table
// 0..DEVICE_MAPPING_L3_COUNT -> Device L3 tables -> Device L3 pages
// ..512 -> Device L2 pages
for i in 0..DEVICE_MAPPING_L3_COUNT {
let phys =
PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.normal.0[i]));
DEVICE_MEMORY.large.0[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
let phys = PhysicalAddress::from_usize(auto_lower_address(&raw const DEVICE_MEMORY.large.0));
KERNEL_PDPT[DEVICE_L1] = PageEntry::table(phys, PageAttributes::WRITABLE);
}
pub(super) unsafe fn load() {
CR3.set_address(auto_lower_address(&raw const KERNEL_PML4));
}
+25 -329
View File
@@ -1,98 +1,37 @@
use core::{
alloc::Layout,
ops::{Deref, DerefMut},
sync::atomic::{AtomicUsize, Ordering},
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
};
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
split_spinlock,
};
use kernel_arch_x86::registers::CR3;
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use static_assertions::{const_assert_eq, const_assert_ne};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use yggdrasil_abi::error::Error;
use crate::KERNEL_VIRT_OFFSET;
use self::table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3};
use self::table::{PageTable, L0, L1};
pub mod fixed;
pub mod process;
pub mod table;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
const CANONICAL_ADDRESS_MASK: usize = 0xFFFF000000000000;
const KERNEL_PHYS_BASE: usize = 0x200000;
// Mapped at compile time
const KERNEL_MAPPING_BASE: usize = KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE;
const KERNEL_L0_INDEX: usize = page_index::<L0>(KERNEL_MAPPING_BASE);
const KERNEL_L1_INDEX: usize = page_index::<L1>(KERNEL_MAPPING_BASE);
const KERNEL_START_L2_INDEX: usize = page_index::<L2>(KERNEL_MAPPING_BASE);
// Must not be zero, should be at 4MiB
const_assert_ne!(KERNEL_START_L2_INDEX, 0);
// From static mapping
const_assert_eq!(KERNEL_L0_INDEX, 511);
const_assert_eq!(KERNEL_L1_INDEX, 0);
// Mapped at boot
const EARLY_MAPPING_L2I: usize = KERNEL_START_L2_INDEX - 1;
const DEVICE_MAPPING_L1I: usize = KERNEL_L1_INDEX + 2;
const RAM_MAPPING_L0I: usize = KERNEL_L0_INDEX - 1;
const DEVICE_MAPPING_L3_COUNT: usize = 4;
split_spinlock! {
use libk_mm_interface::KernelImageObject;
use memtables::x86_64::FixedTables;
use crate::ArchitectureImpl;
#[link_section = ".data.tables"]
static KERNEL_TABLES: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
// 2MiB for early mappings
const EARLY_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK
| (KERNEL_L0_INDEX * L0::SIZE)
| (KERNEL_L1_INDEX * L1::SIZE)
| (EARLY_MAPPING_L2I * L2::SIZE);
static mut EARLY_MAPPING_L3: PageTable<L3> = PageTable::zeroed();
// 1GiB for device MMIO mapping
const DEVICE_MAPPING_OFFSET: usize =
CANONICAL_ADDRESS_MASK | (KERNEL_L0_INDEX * L0::SIZE) | (DEVICE_MAPPING_L1I * L1::SIZE);
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
[PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT];
// 512GiB for whole RAM mapping
pub const RAM_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK | (RAM_MAPPING_L0I * L0::SIZE);
pub static MEMORY_LIMIT: AtomicUsize = AtomicUsize::new(0);
pub static mut RAM_MAPPING_L1: PageTable<L1> = PageTable::zeroed();
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address < MEMORY_LIMIT.load(Ordering::Acquire) {
address + RAM_MAPPING_OFFSET
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
} else {
panic!("Invalid physical address: {:#x}", address);
panic!("Invalid physical address: {address:#x}");
}
}
fn physicalize(address: usize) -> u64 {
if address < RAM_MAPPING_OFFSET
|| address - RAM_MAPPING_OFFSET >= MEMORY_LIMIT.load(Ordering::Acquire)
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
{
panic!("Not a virtualized physical address: {:#x}", address);
panic!("Invalid virtualized address: {address:#x}");
}
(address - RAM_MAPPING_OFFSET) as _
(address - KERNEL_VIRT_OFFSET) as u64
}
unsafe fn map_device_pages(
@@ -100,245 +39,25 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
map_device_memory(PhysicalAddress::from_u64(base), count, attrs)
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
unmap_device_memory(mapping)
}
}
// Early mappings
unsafe fn map_early_pages(physical: PhysicalAddress, count: usize) -> Result<usize, Error> {
for l3i in 0..512 {
let mut taken = false;
for i in 0..count {
if EARLY_MAPPING_L3[i + l3i].is_present() {
taken = true;
break;
}
}
if taken {
continue;
}
for i in 0..count {
// TODO NX, NC
EARLY_MAPPING_L3[i + l3i] =
PageEntry::page(physical.add(i * L3::SIZE), PageAttributes::WRITABLE);
flush_tlb_entry(EARLY_MAPPING_OFFSET + (i + l3i) * L3::SIZE);
}
return Ok(EARLY_MAPPING_OFFSET + l3i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn unmap_early_page(address: usize) {
if !(EARLY_MAPPING_OFFSET..EARLY_MAPPING_OFFSET + L2::SIZE).contains(&address) {
panic!("Tried to unmap invalid early mapping: {:#x}", address);
}
let l3i = (address - EARLY_MAPPING_OFFSET).page_index::<L3>();
assert!(EARLY_MAPPING_L3[l3i].is_present());
EARLY_MAPPING_L3[l3i] = PageEntry::INVALID;
}
// Device mappings
unsafe fn map_device_memory_l3(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
// TODO don't map pages if already mapped
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
continue 'l0;
}
}
for j in 0..count {
let l2i = (i + j) / 512;
let l3i = (i + j) % 512;
// TODO NX, NC
DEVICE_MAPPING_L3S[l2i][l3i] =
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::WRITABLE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn map_device_memory_l2(
base: PhysicalAddress,
count: usize,
_attrs: DeviceMemoryAttributes,
) -> Result<usize, Error> {
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
for j in 0..count {
if DEVICE_MAPPING_L2[i + j].is_present() {
continue 'l0;
}
}
for j in 0..count {
DEVICE_MAPPING_L2[i + j] =
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::WRITABLE);
}
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
}
Err(Error::OutOfMemory)
}
unsafe fn map_device_memory(
base: PhysicalAddress,
size: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
let l3_aligned = base.page_align_down::<L3>();
let l3_offset = base.page_offset::<L3>();
let page_count = (l3_offset + size).page_count::<L3>();
if page_count > 256 {
// Large mapping, use L2 mapping instead
let l2_aligned = base.page_align_down::<L2>();
let l2_offset = base.page_offset::<L2>();
let page_count = (l2_offset + size).page_count::<L2>();
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
let address = base_address + l2_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l2_aligned.into_u64(),
address,
base_address,
page_count,
L2::SIZE,
))
} else {
// Just map the pages directly
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
let address = base_address + l3_offset;
Ok(RawDeviceMemoryMapping::from_raw_parts(
l3_aligned.into_u64(),
address,
base_address,
page_count,
L3::SIZE,
))
}
}
unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
match map.page_size {
L3::SIZE => {
for i in 0..map.page_count {
let page = map.base_address + i * L3::SIZE;
let l2i = page.page_index::<L2>();
let l3i = page.page_index::<L3>();
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
flush_tlb_entry(page);
}
}
L2::SIZE => todo!(),
_ => unimplemented!(),
}
}
/// Memory mapping which may be used for performing early kernel initialization
pub struct EarlyMapping<'a, T: ?Sized> {
value: &'a mut T,
page_count: usize,
}
impl<'a, T: Sized> EarlyMapping<'a, T> {
/// # Safety
///
/// `physical` address provided must be a valid non-NULL address actually containing `T`.
pub unsafe fn map(physical: PhysicalAddress) -> Result<EarlyMapping<'a, T>, Error> {
let layout = Layout::new::<T>();
let aligned = physical.page_align_down::<L3>();
let offset = physical.page_offset::<L3>();
let page_count = (offset + layout.size()).div_ceil(L3::SIZE);
let virt = map_early_pages(aligned, page_count)?;
let value = &mut *((virt + offset) as *mut T);
Ok(EarlyMapping { value, page_count })
}
/// # Safety
///
/// `physical` address provided must be a valid non-NULL address actually containing a `T`
/// slice of given `len`.
pub unsafe fn map_slice(
physical: PhysicalAddress,
len: usize,
) -> Result<EarlyMapping<'a, [T]>, Error> {
let layout = Layout::array::<T>(len).unwrap();
let aligned = physical.page_align_down::<L3>();
let offset = physical.page_offset::<L3>();
let page_count = (offset + layout.size()).div_ceil(L3::SIZE);
let virt = map_early_pages(aligned, page_count)?;
let value = core::slice::from_raw_parts_mut((virt + offset) as *mut T, len);
Ok(EarlyMapping { value, page_count })
}
}
impl<T: ?Sized> Deref for EarlyMapping<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.value
}
}
impl<T: ?Sized> DerefMut for EarlyMapping<'_, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.value
}
}
impl<T: ?Sized> Drop for EarlyMapping<'_, T> {
fn drop(&mut self) {
let address = (self.value as *mut T).addr() & !(L3::SIZE - 1);
for i in 0..self.page_count {
let page = address + i * L3::SIZE;
unsafe {
unmap_early_page(page);
}
}
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
}
}
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
let tables = KERNEL_TABLES.lock();
unsafe {
dst[KERNEL_L0_INDEX] = PageEntry::from_raw(tables.l0.data[KERNEL_L0_INDEX]);
dst[RAM_MAPPING_L0I] = PageEntry::from_raw(tables.l0.data[RAM_MAPPING_L0I]);
dst[fixed::KERNEL_L0I] = fixed::KERNEL_PML4[fixed::KERNEL_L0I];
}
}
pub fn auto_address<T>(pointer: *const T) -> usize {
pub fn auto_lower_address<T>(pointer: *const T) -> usize {
let address = pointer.addr();
if address < KERNEL_VIRT_OFFSET {
address
@@ -363,35 +82,12 @@ pub fn auto_address<T>(pointer: *const T) -> usize {
/// # Safety
///
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
pub unsafe fn init_fixed_tables() {
let mut tables = KERNEL_TABLES.lock();
// TODO this could be built in compile-time too?
let early_mapping_l3_phys = auto_address(&raw const EARLY_MAPPING_L3);
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
let ram_mapping_l1_phys = auto_address(&raw const RAM_MAPPING_L1);
for i in 0..DEVICE_MAPPING_L3_COUNT {
let device_mapping_l3_phys =
PhysicalAddress::from_usize(auto_address(&raw const DEVICE_MAPPING_L3S[i]));
DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::WRITABLE);
#[inline(never)]
pub unsafe fn init_fixed_tables(have_1gib_pages: bool, bsp: bool) {
fixed::setup(have_1gib_pages);
if bsp {
fixed::load();
}
assert_eq!(tables.kernel_l2.data[EARLY_MAPPING_L2I], 0);
tables.kernel_l2.data[EARLY_MAPPING_L2I] = (early_mapping_l3_phys as u64)
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
assert_eq!(tables.kernel_l1.data[DEVICE_MAPPING_L1I], 0);
tables.kernel_l1.data[DEVICE_MAPPING_L1I] = (device_mapping_l2_phys as u64)
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
assert_eq!(tables.l0.data[RAM_MAPPING_L0I], 0);
tables.l0.data[RAM_MAPPING_L0I] =
(ram_mapping_l1_phys as u64) | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
// TODO ENABLE EFER.NXE
let cr3 = auto_address(&raw const tables.l0);
CR3.set_address(cr3);
}
/// # Safety
+39 -4
View File
@@ -4,7 +4,7 @@ use core::marker::PhantomData;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::ProcessAddressSpaceManager,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
@@ -61,7 +61,15 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false)
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
unsafe fn update_page_attributes(
&mut self,
address: usize,
update: &PageAttributeUpdate,
) -> Result<(), Error> {
self.update_l3_entry(address, |entry| entry.update(update))
}
unsafe fn unmap_page(&mut self, address: usize) -> Result<(PhysicalAddress, bool), Error> {
self.pop_l3_entry(address)
}
@@ -111,7 +119,33 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
fn update_l3_entry<F: FnOnce(&mut PageEntry<L3>) -> Result<(), Error>>(
&mut self,
virt: usize,
mapper: F,
) -> Result<(), Error> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
let mut l1 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?;
let mut l2 = l1.get_mut(l1i).ok_or(Error::DoesNotExist)?;
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let entry = &mut l3[l3i];
if !entry.is_present() {
return Err(Error::DoesNotExist);
}
mapper(entry)?;
unsafe {
flush_tlb_entry(virt);
}
Ok(())
}
fn pop_l3_entry(&mut self, virt: usize) -> Result<(PhysicalAddress, bool), Error> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
@@ -123,13 +157,14 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
let mut l3 = l2.get_mut(l2i).ok_or(Error::DoesNotExist)?;
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
let dirty = l3[l3i].is_dirty();
l3[l3i] = PageEntry::INVALID;
unsafe {
flush_tlb_entry(virt);
}
Ok(page)
Ok((page, dirty))
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
+26 -2
View File
@@ -8,6 +8,7 @@ use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
process::PageAttributeUpdate,
table::{
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
@@ -31,6 +32,8 @@ bitflags! {
/// For tables, allows user access to further translation levels, for pages/blocks, allows
/// user access to the region covered by the entry
const USER = 1 << 2;
/// If set, the page has been written to
const DIRTY = 1 << 6;
}
}
@@ -98,11 +101,15 @@ impl PageEntry<L3> {
/// not
pub fn as_page(self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
} else {
None
}
}
pub fn is_dirty(&self) -> bool {
self.0 & PageAttributes::DIRTY.bits() != 0
}
}
impl PageEntry<L2> {
@@ -145,7 +152,7 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
} else {
None
}
@@ -158,6 +165,8 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
}
impl<L: EntryLevel> PageEntry<L> {
const ATTR_MASK: u64 = 0xFFF | (1 << 63);
/// An entry that is not mapped
pub const INVALID: Self = Self(0, PhantomData);
@@ -179,6 +188,21 @@ impl<L: EntryLevel> PageEntry<L> {
pub fn is_present(&self) -> bool {
self.0 & PageAttributes::PRESENT.bits() != 0
}
pub fn update(&mut self, update: &PageAttributeUpdate) -> Result<(), Error> {
let mut attrs = PageAttributes::from_bits_retain(self.0);
if let Some(write) = update.user_write {
if write {
attrs |= PageAttributes::WRITABLE;
} else {
attrs &= !PageAttributes::WRITABLE;
}
}
// Dirty is ignored, it's hardware-managed
self.0 &= !Self::ATTR_MASK;
self.0 |= attrs.bits() & Self::ATTR_MASK;
Ok(())
}
}
impl<L: EntryLevel> PageTable<L> {
+3 -3
View File
@@ -15,7 +15,7 @@ fn build_x86_64() {
const DEFAULT_8086_AS: &str = "nasm";
const AP_BOOTSTRAP_S: &str = "src/arch/x86_64/boot/ap_boot.S";
println!("cargo:rerun-if-changed={}", AP_BOOTSTRAP_S);
println!("cargo:rerun-if-changed={AP_BOOTSTRAP_S}");
let out_dir = env::var("OUT_DIR").unwrap();
let assembler = env::var("AS8086").unwrap_or(DEFAULT_8086_AS.to_owned());
@@ -35,7 +35,7 @@ fn build_x86_64() {
if !output.status.success() {
io::stderr().write_all(&output.stderr).ok();
panic!("{}: could not assemble {}", assembler, AP_BOOTSTRAP_S);
panic!("{assembler}: could not assemble {AP_BOOTSTRAP_S}");
}
}
@@ -93,6 +93,6 @@ fn main() {
"x86_64" => build_x86_64(),
"aarch64" => (),
"riscv64" => (),
_ => panic!("Unknown target arch: {:?}", arch),
_ => panic!("Unknown target arch: {arch:?}"),
}
}
+6
View File
@@ -35,6 +35,9 @@ unsafe impl Allocator for AcpiAllocator {
}
// TODO don't map memory as device if not necessary
/// # Safety
///
/// Allows direct reads from physical memory, unsafe
pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
let io =
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
@@ -49,6 +52,9 @@ pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
}
}
/// # Safety
///
/// Allows direct writes to physical memory, unsafe
pub unsafe fn write_memory<T>(address: PhysicalAddress, value: T) {
let io =
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
+19 -9
View File
@@ -16,7 +16,8 @@ use libk::{
error::Error,
};
use libk_mm::{
address::PhysicalAddress, device::DeviceMemoryIo, table::MapAttributes, PageProvider,
address::PhysicalAddress, device::DeviceMemoryIo, table::MapAttributes, OnDemandPage,
PageProvider, VirtualPage,
};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker, OneTimeInit};
use tock_registers::interfaces::{Readable, Writeable};
@@ -234,7 +235,7 @@ impl AhciPort {
.await
}
async fn submit<C: AtaCommand>(&self, command: &C) -> Result<SubmittedCommand, AhciError> {
async fn submit<C: AtaCommand>(&self, command: &C) -> Result<SubmittedCommand<'_>, AhciError> {
if command.prd_count() > 2 {
log::warn!("TODO: AHCI doesn't like 3+ PRD transfers");
return Err(AhciError::RegionTooLarge);
@@ -365,8 +366,21 @@ impl Device for AhciPort {
}
impl PageProvider for AhciPort {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
unimplemented!()
}
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
unimplemented!()
}
fn release_page(
&self,
_offset: u64,
_phys: PhysicalAddress,
_dirty: bool,
) -> Result<(), Error> {
unimplemented!()
}
fn clone_page(
@@ -375,10 +389,6 @@ impl PageProvider for AhciPort {
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
unimplemented!()
}
}
+17 -8
View File
@@ -11,7 +11,7 @@ use libk::{
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
table::MapAttributes,
PageProvider, PageSlice,
OnDemandPage, PageProvider, PageSlice, VirtualPage,
};
use crate::{command::IdentifyNamespaceRequest, register_nvme_namespace, IoDirection};
@@ -151,8 +151,21 @@ impl BlockDevice for NvmeNamespace {
}
impl PageProvider for NvmeNamespace {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
todo!()
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
unimplemented!()
}
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
unimplemented!()
}
fn release_page(
&self,
_offset: u64,
_phys: PhysicalAddress,
_dirty: bool,
) -> Result<(), Error> {
unimplemented!()
}
fn clone_page(
@@ -161,10 +174,6 @@ impl PageProvider for NvmeNamespace {
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
todo!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
todo!()
unimplemented!()
}
}
+1 -1
View File
@@ -129,7 +129,7 @@ impl PrpList {
list: None,
}),
_ => {
let count = (size + 0xFFF) / 0x1000;
let count = size.div_ceil(0x1000);
let list =
DmaBuffer::new_slice_with(dma, |i| base.add((i + 1) * 0x1000), count - 1)
.map_err(NvmeError::MemoryError)?;
+22 -15
View File
@@ -22,7 +22,9 @@ use libk::{
fs::devfs,
task::{runtime, sync::AsyncMutex},
};
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
use libk_mm::{
address::PhysicalAddress, table::MapAttributes, OnDemandPage, PageProvider, VirtualPage,
};
use libk_util::{
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
OneTimeInit,
@@ -69,10 +71,10 @@ impl ScsiEnclosure {
// Probe LUNs
for i in 0..lun_count {
if this.probe_lun(i as u8).await {
if let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await {
*this.units[i].write() = Some(unit);
}
if this.probe_lun(i as u8).await
&& let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await
{
*this.units[i].write() = Some(unit);
}
}
@@ -115,11 +117,7 @@ impl ScsiEnclosure {
attempts -= 1;
}
if attempts == 0 {
false
} else {
true
}
attempts != 0
}
async fn poll(self: &Arc<Self>) {
@@ -278,12 +276,21 @@ impl BlockDevice for ScsiUnit {
}
impl PageProvider for ScsiUnit {
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
Err(Error::NotImplemented)
fn ondemand_fetch(&self, _opaque: u64) -> Result<OnDemandPage, Error> {
unimplemented!()
}
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
Err(Error::NotImplemented)
fn get_page(&self, _offset: u64) -> Result<VirtualPage, Error> {
unimplemented!()
}
fn release_page(
&self,
_offset: u64,
_phys: PhysicalAddress,
_dirty: bool,
) -> Result<(), Error> {
unimplemented!()
}
fn clone_page(
@@ -292,7 +299,7 @@ impl PageProvider for ScsiUnit {
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
Err(Error::NotImplemented)
unimplemented!()
}
}
+2 -3
View File
@@ -44,7 +44,7 @@ impl ScsiTransportWrapper {
return Err(Error::InvalidArgument);
}
let lba_bytes = (lba as u32).to_be_bytes();
let lba_count = (lba_count as u16).to_be_bytes();
let lba_count = lba_count.to_be_bytes();
// Issue a READ (10) command
let request_buffer = [
0x28,
@@ -84,8 +84,7 @@ impl ScsiTransportWrapper {
response_buffer.slice_mut(0..R::RESPONSE_LEN),
)
.await?;
let response_bytes =
unsafe { MaybeUninit::slice_assume_init_ref(&response_buffer[..response_len]) };
let response_bytes = unsafe { response_buffer[..response_len].assume_init_ref() };
R::parse_response(response_bytes)
}
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "ygg_driver_bsp_arm"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod pl011;
mod pl061;
@@ -1,5 +1,3 @@
//! ARM PL011 driver
use abi::{error::Error, io::TerminalOptions};
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
@@ -18,6 +16,7 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::{error::Error, io::TerminalOptions};
register_bitfields! {
u32,
@@ -181,7 +180,7 @@ impl Device for Pl011 {
device_tree_driver! {
compatible: ["arm,pl011"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
+233
View File
@@ -0,0 +1,233 @@
use core::sync::atomic::{AtomicU64, Ordering};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioInterruptMode, GpioPinLevel, PinHandle,
SinglePinDirection,
},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk::{device::external_interrupt_controller, event::signal_gpio_event};
use libk_mm::device::DeviceMemoryIo;
use libk_util::{bit::BitField, sync::IrqSafeSpinlock};
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x0000 => GPIODATA: [ReadWrite<u32>; 256]),
(0x0400 => GPIODIR: ReadWrite<u32>),
(0x0404 => GPIOIS: ReadWrite<u32>),
(0x0408 => GPIOIBE: ReadWrite<u32>),
(0x040C => GPIOIEV: ReadWrite<u32>),
(0x0410 => GPIOIE: ReadWrite<u32>),
(0x0414 => GPIORIS: ReadOnly<u32>),
(0x0418 => GPIOMIS: ReadOnly<u32>),
(0x041C => GPIOIC: WriteOnly<u32>),
(0x0420 => GPIOAFSEL: ReadWrite<u32>),
(0x0424 => _0),
(0x0FE0 => GPIOPERIPHID: [ReadOnly<u32>; 4]),
(0x0FF0 => GPIOPCELLID: [ReadOnly<u32>; 4]),
(0x1000 => @END),
}
}
struct Pl061 {
#[allow(unused)]
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
irq: FullIrq,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
gpio_events: [AtomicU64; 8],
}
impl Device for Pl061 {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
Ok(())
}
unsafe fn init_irq(self: Arc<Self>) -> Result<(), Error> {
let intc = external_interrupt_controller()?;
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
intc.enable_irq(self.irq.irq)?;
Ok(())
}
fn display_name(&self) -> &str {
"PL061 GPIO Controller"
}
}
impl InterruptHandler for Pl061 {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
let status = {
let lock = self.regs.lock();
let val = lock.GPIOMIS.get();
lock.GPIOIC.set(0xFF);
val
};
for bit in 0..8 {
let ev = self.gpio_events[bit].load(Ordering::Acquire);
if ev != 0 {
signal_gpio_event(ev);
}
}
status != 0
}
}
impl GpioController for Pl061 {
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
todo!()
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
todo!()
}
fn setup_gpio(&self, pin: &PinHandle, event: Option<GpioInterruptEvent>) -> Result<(), Error> {
let regs = self.regs.lock();
let direction = pin
.config
.force_single_direction()
.ok_or(Error::InvalidArgument)
.inspect_err(|_| {
log::warn!(
"pl061: gpio #{} has invalid direction input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
)
})?;
// Enable software control
regs.GPIOAFSEL.set(regs.GPIOAFSEL.get() & !(1 << pin.index));
match direction {
SinglePinDirection::Output => {
log::info!("pl061: gpio #{} set as output", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() | (1 << pin.index));
// Disable interrupt
regs.GPIOIE.set(regs.GPIOIE.get() & !(1 << pin.index));
let level = match pin.config.initial_level {
GpioPinLevel::Low => 0,
GpioPinLevel::High => 1,
};
let mut val = regs.GPIODATA[0].get();
val &= !(1 << pin.index);
val |= level << pin.index;
regs.GPIODATA[0].set(val);
}
SinglePinDirection::Input => {
log::info!("pl061: gpio #{} set as input", pin.index);
regs.GPIODIR.set(regs.GPIODIR.get() & !(1 << pin.index));
if let Some(event) = event {
let ibe = event.mode == GpioInterruptMode::BothEdges;
let is = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::LowLevel;
let iev = event.mode == GpioInterruptMode::HighLevel
|| event.mode == GpioInterruptMode::RisingEdge;
self.gpio_events[pin.index as usize].store(event.event, Ordering::Release);
regs.GPIOIS
.set(regs.GPIOIS.get().modify_bit(pin.index as usize, is));
regs.GPIOIBE
.set(regs.GPIOIBE.get().modify_bit(pin.index as usize, ibe));
regs.GPIOIEV
.set(regs.GPIOIEV.get().modify_bit(pin.index as usize, iev));
regs.GPIOIE.set(regs.GPIOIE.get() | (1 << pin.index));
regs.GPIOIC.set(1 << pin.index);
}
}
}
Ok(())
}
}
impl DeviceTreePinController for Pl061 {
fn configure_pin_group(self: Arc<Self>, _pins: &Arc<Node>) -> Result<(), Error> {
// TODO implement this when I get some board with this pinctrl
todo!()
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["arm,pl061"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clocks = if let Some(clocks) = node.clocks() {
clocks.collect()
} else {
Vec::new()
};
let resets = if let Some(resets) = node.resets() {
resets.collect()
} else {
Vec::new()
};
let irq = node.interrupt(0)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let pinctrl = Arc::new(Pl061 {
regs: IrqSafeSpinlock::new(regs),
irq,
clocks,
resets,
gpio_events: [const { AtomicU64::new(0) }; 8]
});
node.make_pin_controller(pinctrl.clone());
Some(pinctrl)
}
}
}
+18
View File
@@ -0,0 +1,18 @@
[package]
name = "ygg_driver_bsp_bcm283x"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-aarch64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
@@ -1,18 +1,20 @@
//! BCM283x AUX peripheral
use aarch64_cpu::registers::ReadWriteable;
use abi::error::Error;
use alloc::sync::Arc;
use device_api::{
clock::ClockController,
clock::{ClockController, ClockHandle},
device::{Device, DeviceInitContext},
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use device_tree::{
driver::{device_tree_driver, DeviceTreeClockController, Node, ProbeContext},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use tock_registers::{
interfaces::ReadWriteable,
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
@@ -37,27 +39,49 @@ struct Bcm2835Aux {
regs: OneTimeInit<IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>>,
}
impl Bcm2835Aux {
fn ensure_regs(&self) -> Result<&IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>, Error> {
let base = self.base;
self.regs.or_try_init_with(move || unsafe {
DeviceMemoryIo::map(base, Default::default()).map(IrqSafeSpinlock::new)
})
}
}
impl ClockController for Bcm2835Aux {
fn enable_clock(&self, clock: u32) -> Result<(), Error> {
let regs = self.regs.try_get().ok_or(Error::DoesNotExist)?.lock();
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let regs = self.ensure_regs()?;
let regs = regs.lock();
match clock {
0 => {
Some(0) => {
regs.AUX_ENABLES.modify(AUX_ENABLES::MU_ENABLE::SET);
Ok(())
}
None => todo!(),
_ => Err(Error::InvalidArgument),
}
}
fn disable_clock(&self, _clock: u32) -> Result<(), Error> {
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::NotImplemented)
}
}
impl DeviceTreeClockController for Bcm2835Aux {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
parent: self.clone(),
clock: Some(clock),
},
1,
))
}
}
impl Device for Bcm2835Aux {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = DeviceMemoryIo::map(self.base, Default::default())?;
self.regs.init(IrqSafeSpinlock::new(regs));
Ok(())
}
@@ -74,14 +98,17 @@ impl Device for Bcm2835Aux {
device_tree_driver! {
compatible: ["brcm,bcm2835-aux"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
Some(Arc::new(Bcm2835Aux {
let aux = Arc::new(Bcm2835Aux {
_node: node.clone(),
base,
regs: OneTimeInit::new(),
}))
});
node.make_clock_controller(aux.clone());
Some(aux)
}
}
}
@@ -1,20 +1,10 @@
//! Broadcom BCM2835 mini-UART driver
// TODO
#![allow(missing_docs)]
use abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
use alloc::sync::Arc;
use device_api::{
clock::ClockHandle,
device::{Device, DeviceInitContext},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::{
driver::{device_tree_driver, lookup_phandle, Node, ProbeContext},
DeviceTreePropertyRead,
};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use libk::{
debug::DebugSink,
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
@@ -27,6 +17,10 @@ use tock_registers::{
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite},
};
use yggdrasil_abi::{
error::Error,
io::{TerminalOptions, TerminalOutputOptions},
};
register_bitfields! {
u32,
@@ -64,10 +58,11 @@ struct Inner {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
/// Broadcom 283x mini-UART driver
pub struct Bcm2835AuxUart {
node: Arc<Node>,
base: PhysicalAddress,
irq: FullIrq,
clock: ClockHandle,
inner: OneTimeInit<Arc<Terminal<Inner>>>,
}
@@ -151,8 +146,7 @@ impl Device for Bcm2835AuxUart {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// TODO initialize pinctrl
// NOTE: might as well make it an error if clock cannot be initialized
self.enable_clock();
self.clock.enable()?;
let regs = unsafe { DeviceMemoryIo::map(self.base, Default::default()) }?;
let config = TerminalOptions {
@@ -195,35 +189,19 @@ impl Device for Bcm2835AuxUart {
}
}
impl Bcm2835AuxUart {
fn enable_clock(&self) -> Option<()> {
let clocks = self.node.property("clocks")?;
let (phandle, index) = clocks.read_cells(0, (1, 1))?;
let phandle = phandle as u32;
let index = index as u32;
let clock = lookup_phandle(phandle, true)?;
let clock = clock.as_clock_controller()?;
clock.enable_clock(index).ok()?;
Some(())
}
}
// TODO handle pinctrl
device_tree_driver! {
compatible: ["brcm,bcm2835-aux-uart"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
let irq = node.interrupt(0)?;
let clock = node.clock(0)?;
Some(Arc::new(Bcm2835AuxUart {
node: node.clone(),
base,
irq,
clock,
inner: OneTimeInit::new(),
}))
}
+252
View File
@@ -0,0 +1,252 @@
use alloc::sync::Arc;
use device_api::{
device::{Device, DeviceInitContext},
gpio::{GpioController, GpioInterruptEvent, PinHandle},
interrupt::{FullIrq, InterruptHandler, IrqVector},
};
use device_tree::{
driver::{
device_tree_driver, util::generic_gpio_config, DeviceTreeGpioPins, DeviceTreePinController,
Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk::device::external_interrupt_controller;
use libk_mm::device::DeviceMemoryIo;
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
const PUPD_NONE: u32 = 0b00;
register_structs! {
#[allow(non_snake_case)]
Regs {
// Pin function select
(0x00 => GPFSEL: [ReadWrite<u32>; 6]),
(0x18 => _0),
// Set pin
(0x1C => GPSET: [WriteOnly<u32>; 2]),
(0x24 => _1),
// Clear pin
(0x28 => GPCLR: [WriteOnly<u32>; 2]),
(0x30 => _2),
// Current pin level
(0x34 => GPLEV: [ReadOnly<u32>; 2]),
(0x3C => _3),
// Pin event detect status
(0x40 => GPEDS: [ReadWrite<u32>; 2]),
(0x48 => _4),
// Pin rising edge event enable
(0x4C => GPREN: [ReadWrite<u32>; 2]),
(0x54 => _5),
// Pin falling edge event enable
(0x58 => GPFEN: [ReadWrite<u32>; 2]),
(0x60 => _6),
// Pin high event enable
(0x64 => GPHEN: [ReadWrite<u32>; 2]),
(0x6C => _7),
// Pin low event enable
(0x70 => GPLEN: [ReadWrite<u32>; 2]),
(0x78 => _8),
// Pin async rising edge event enable
(0x7C => GPAREN: [ReadWrite<u32>; 2]),
(0x84 => _9),
// Pin async falling edge event enable
(0x88 => GPAFEN: [ReadWrite<u32>; 2]),
(0x90 => _10),
// Pin pull up/down control
(0xE4 => GPIO_PUP_PDN_CNTRL_REG: [ReadWrite<u32>; 4]),
(0xF4 => _11),
(0x100 => @END),
}
}
struct Bcm2711Gpio {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
#[allow(unused)]
irqs: [FullIrq; 2],
}
impl Regs {
fn write_fsel(&self, pin: usize, value: u32) {
let fsel_reg = pin / 10;
let fsel_shift = (pin % 10) * 3;
let mut fsel = self.GPFSEL[fsel_reg].get();
fsel &= !(0x7 << fsel_shift);
fsel |= (value & 0x7) << fsel_shift;
self.GPFSEL[fsel_reg].set(fsel);
}
fn write_pupd(&self, pin: usize, value: u32) {
let pupd_reg = pin / 16;
let pupd_shift = (pin % 16) * 2;
let mut pupd = self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].get();
pupd &= !(0x3 << pupd_shift);
pupd |= (value & 0x3) << pupd_shift;
self.GPIO_PUP_PDN_CNTRL_REG[pupd_reg].set(pupd);
}
fn configure_pin_interrupts(
&self,
pin: usize,
rising_edge: bool,
falling_edge: bool,
level_high: bool,
level_low: bool,
) {
#[inline]
fn modify_reg(reg: &ReadWrite<u32>, bit: u32, set: bool) {
if set {
reg.set(reg.get() | bit);
} else {
reg.set(reg.get() & !bit);
}
}
// TODO use async edge detection (likely have some limitations)?
let reg = pin / 32;
let bit = 1 << (pin % 32);
// Disable async edge events
modify_reg(&self.GPAREN[reg], bit, false);
modify_reg(&self.GPAFEN[reg], bit, false);
modify_reg(&self.GPREN[reg], bit, rising_edge);
modify_reg(&self.GPFEN[reg], bit, falling_edge);
modify_reg(&self.GPHEN[reg], bit, level_high);
modify_reg(&self.GPLEN[reg], bit, level_low);
// Clear interrupt status
self.GPEDS[reg].set(bit);
}
fn configure_pin_function(&self, pin: usize, function: u32, pull: u32) {
self.write_fsel(pin, function);
self.write_pupd(pin, pull);
}
}
impl Device for Bcm2711Gpio {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
let regs = self.regs.lock();
// Disable all interrupts by default
for pin in 0..58 {
regs.configure_pin_interrupts(pin, false, false, false, false);
}
let intc = external_interrupt_controller()?;
for irq in self.irqs.iter() {
intc.register_irq(irq.irq, irq.options, self.clone())?;
intc.enable_irq(irq.irq)?;
}
Ok(())
}
fn display_name(&self) -> &str {
"bcm2711-gpio"
}
}
impl InterruptHandler for Bcm2711Gpio {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
log::warn!("TODO: handle bcm2711-gpio interrupts");
false
}
}
impl GpioController for Bcm2711Gpio {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TOOD: bcm2711 gpio pin #{} setup: input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
Ok(())
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Ok(false)
}
fn write_gpio(&self, _pin: &PinHandle, _value: bool) -> Result<(), Error> {
Ok(())
}
}
impl DeviceTreePinController for Bcm2711Gpio {
fn configure_pin_group(self: Arc<Self>, group: &Arc<Node>) -> Result<(), Error> {
let pins = group.property("brcm,pins").ok_or(Error::InvalidArgument)?;
let function = group.property("brcm,function").ok_or(Error::DoesNotExist)?;
let pull = group.property("brcm,pull");
if function.is_empty() || pins.is_empty() {
return Ok(());
}
let function = function.read_cell(0, 1).ok_or(Error::InvalidArgument)? as u32;
let regs = self.regs.lock();
for i in 0..pins.len() / 4 {
let pin = pins.read_cell(i, 1).ok_or(Error::InvalidArgument)? as u32;
let pull = if let Some(pull) = pull.as_ref().and_then(|p| p.read_cell(i, 1)) {
pull as u32
} else {
PUPD_NONE
};
log::info!("bcm2711-gpio: gpio{pin} function={function}");
regs.configure_pin_function(pin as usize, function, pull);
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
device_tree_driver! {
compatible: ["brcm,bcm2711-gpio"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let irq0 = node.interrupt(0)?;
let irq1 = node.interrupt(1)?;
let base = node.map_base(context, 0)?;
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
let gpio = Arc::new(Bcm2711Gpio {
regs: IrqSafeSpinlock::new(regs),
irqs: [irq0, irq1]
});
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#![no_std]
extern crate alloc;
mod aux;
mod aux_uart;
mod gpio;
mod mbox;
+339
View File
@@ -0,0 +1,339 @@
use core::{cell::UnsafeCell, mem::MaybeUninit};
use alloc::sync::Arc;
use device_api::device::{Device, DeviceInitContext};
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
use kernel_arch_aarch64::mem::table::L3;
use libk::device::{
display::{
DisplayDevice, DisplayMode, DisplayOwner, DriverFlags, FramebufferInfo, PixelFormat,
},
manager::DEVICE_REGISTRY,
};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
device::DeviceMemoryIo,
table::{EntryLevel, MapAttributes},
OnDemandPage, PageBox, PageProvider, VirtualPage,
};
use libk_util::sync::IrqSafeSpinlock;
use tock_registers::{
interfaces::{Readable, Writeable},
register_bitfields, register_structs,
registers::{ReadOnly, ReadWrite, WriteOnly},
};
use yggdrasil_abi::error::Error;
register_bitfields! {
u32,
MboxValue [
ADDRESS OFFSET(4) NUMBITS(28) [],
CHANNEL OFFSET(0) NUMBITS(4) [
PowerManagement = 0,
Framebuffer = 1,
VirtualUart = 2,
Vchiq = 3,
Led = 4,
Button = 5,
Touch = 6,
PropertyArmToVc = 8,
PropertyVcToArm = 9
]
],
MboxStatus [
FULL OFFSET(31) NUMBITS(1) [],
EMPTY OFFSET(30) NUMBITS(1) [],
]
}
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => READ: ReadOnly<u32, MboxValue::Register>),
(0x04 => _0),
(0x10 => POLL: ReadOnly<u32>),
(0x14 => SENDER: ReadOnly<u32>),
(0x18 => STATUS: ReadOnly<u32, MboxStatus::Register>),
(0x1C => CONFIG: ReadWrite<u32>),
(0x20 => WRITE: WriteOnly<u32, MboxValue::Register>),
(0x24 => _1),
(0x40 => @END),
}
}
struct Bcm2835Mbox {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
}
struct Bcm2835Framebuffer {
mode: DisplayMode,
base: PhysicalAddress,
size: usize,
pitch: usize,
}
struct FramebufferResponse {
physical_address: u32,
size: u32,
pitch: u32,
}
unsafe impl Send for Bcm2835Mbox {}
unsafe impl Sync for Bcm2835Mbox {}
impl Device for Bcm2835Mbox {
fn display_name(&self) -> &str {
"bcm2835-mbox"
}
}
impl Bcm2835Mbox {
const RAM_TO_GPU_OFFSET: u32 = 0x40000000;
pub unsafe fn call_raw(&self, buffer: PhysicalAddress, channel: u32) -> Result<(), Error> {
log::info!("bcm2835-mbox: mbox call buffer={buffer:#x}, channel={channel}");
let address = buffer
.try_into_u32()
.inspect_err(|_| log::error!("bcm2835-mbox: invalid physical address {buffer:#x}"))
.map_err(|_| Error::InvalidArgument)?;
if address >= Self::RAM_TO_GPU_OFFSET {
log::error!("bcm2835-mbox: RAM address {buffer:#x} is too high");
return Err(Error::InvalidArgument);
}
let address = address + Self::RAM_TO_GPU_OFFSET;
let regs = self.regs.lock();
while regs.STATUS.matches_all(MboxStatus::FULL::SET) {
core::hint::spin_loop();
}
let val = MboxValue::ADDRESS.val(address >> 4) + MboxValue::CHANNEL.val(channel);
regs.WRITE.write(val);
loop {
while regs.STATUS.matches_all(MboxStatus::EMPTY::SET) {
core::hint::spin_loop();
}
let val = regs.READ.extract();
if val.matches_all(MboxValue::CHANNEL.val(channel)) {
return Ok(());
}
}
}
pub fn configure_framebuffer(
&self,
mode_info: &DisplayMode,
) -> Result<FramebufferResponse, Error> {
#[derive(Debug, Clone, Copy)]
#[repr(C)]
struct FramebufferRequest {
physical_width: u32,
physical_height: u32,
virtual_width: u32,
virtual_height: u32,
pitch: u32,
bpp: u32,
x_offset: u32,
y_offset: u32,
address: u32,
size: u32,
}
let mut buffer = PageBox::new(UnsafeCell::new(FramebufferRequest {
physical_width: mode_info.width,
physical_height: mode_info.height,
virtual_width: mode_info.width,
virtual_height: mode_info.height,
pitch: 0,
bpp: 32,
x_offset: 0,
y_offset: 0,
address: 0,
size: 0,
}))?;
// TODO flush cache for buffer
unsafe { self.call_raw(buffer.as_physical_address(), 0x01) }?;
let buffer = buffer.get_mut();
log::info!("bcm2835-fb: mode set response: {buffer:#?}");
if buffer.address == 0 || buffer.size == 0 || buffer.pitch == 0 {
// Request failed
log::error!("bcm2835-fb: mode set failed");
return Err(Error::InvalidArgument);
}
let address = buffer.address & (Bcm2835Mbox::RAM_TO_GPU_OFFSET - 1);
Ok(FramebufferResponse {
physical_address: address,
size: buffer.size,
pitch: buffer.pitch,
})
}
}
impl Bcm2835Framebuffer {
pub fn setup(mbox: &Arc<Bcm2835Mbox>) -> Result<(), Error> {
const WIDTH: u32 = 1024;
const HEIGHT: u32 = 768;
let mode = DisplayMode {
index: 0,
width: WIDTH,
height: HEIGHT,
frames_per_second: 60,
pixel_format: PixelFormat::R8G8B8A8,
};
let framebuffer = mbox.configure_framebuffer(&mode)?;
let fb = Arc::new(Self {
mode,
base: PhysicalAddress::from_u32(framebuffer.physical_address),
size: framebuffer.size as usize,
pitch: framebuffer.pitch as usize,
});
DEVICE_REGISTRY.display.register(fb, false)?;
Ok(())
}
}
impl Device for Bcm2835Framebuffer {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
unreachable!()
}
fn display_name(&self) -> &str {
"bcm2835-fb"
}
}
impl PageProvider for Bcm2835Framebuffer {
fn get_page(&self, offset: u64) -> Result<VirtualPage, Error> {
let offset = offset as usize;
if offset + L3::SIZE > self.size {
log::warn!(
"Tried to map offset {:#x}, but size is {:#x}",
offset,
self.size
);
Err(Error::InvalidMemoryOperation)
} else {
let page = self.base.add(offset);
Ok(VirtualPage::Immediate(page))
}
}
fn clone_page(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
unimplemented!()
}
fn release_page(
&self,
_offset: u64,
_phys: PhysicalAddress,
_dirty: bool,
) -> Result<(), Error> {
todo!()
}
fn ondemand_fetch(&self, _offset: u64) -> Result<OnDemandPage, Error> {
unimplemented!()
}
}
impl DisplayDevice for Bcm2835Framebuffer {
fn lock(&self, _owner: DisplayOwner) -> Result<(), Error> {
Ok(())
}
fn unlock(&self, _owner: DisplayOwner) -> Result<(), Error> {
Ok(())
}
fn synchronize(&self) -> Result<(), Error> {
Ok(())
}
fn driver_flags(&self) -> DriverFlags {
DriverFlags::empty()
}
fn set_display_mode(&self, _index: u32, _double_buffer: bool) -> Result<(), Error> {
Err(Error::NotImplemented)
}
fn active_framebuffer(&self) -> Result<FramebufferInfo, usize> {
Ok(FramebufferInfo {
base: self.base,
kernel_base: None,
stride: self.pitch,
size: self.size,
})
}
fn active_display_mode(&self) -> Option<DisplayMode> {
Some(self.mode)
}
fn query_display_modes(&self, modes: &mut [MaybeUninit<DisplayMode>]) -> Result<usize, Error> {
if modes.is_empty() {
return Err(Error::BufferTooSmall);
}
modes[0].write(self.mode);
Ok(1)
}
fn active_framebuffers(
&self,
output: &mut [MaybeUninit<FramebufferInfo>],
) -> Result<usize, usize> {
if output.is_empty() {
return Err(1);
}
output[0].write(FramebufferInfo {
base: self.base,
kernel_base: None,
stride: self.pitch,
size: self.size,
});
Ok(1)
}
}
device_tree_driver! {
compatible: ["brcm,bcm2835-mbox"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let base = node.map_base(context, 0)?;
// TODO interrupts
let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }
.inspect_err(|e| log::error!("bcm2835-mbox: mapping failed: {e:?}"))
.ok()?;
let mbox = Arc::new(Bcm2835Mbox {
regs: IrqSafeSpinlock::new(regs)
});
// TODO there's no fdt node for a framebuffer, so it's hardcoded as a child of the mbox
if let Err(error) = Bcm2835Framebuffer::setup(&mbox) {
log::error!("bcm2835-mbox: framebuffer setup error: {error:?}");
}
Some(mbox)
}
}
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_jh7110"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
futures-util.workspace = true
+788
View File
@@ -0,0 +1,788 @@
use core::{marker::PhantomData, ops::Index};
use alloc::sync::Arc;
use device_api::{
clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle},
device::{Device, DeviceInitContext},
};
use device_tree::{
driver::{
device_tree_driver, lookup_phandle, DeviceTreeClockController, DeviceTreeResetController,
DeviceTreeSyscon, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::sync::IrqSafeSpinlock;
use yggdrasil_abi::error::Error;
// SYSCRG clocks
const SYSCRG_CPU_ROOT: usize = 0x000 / 4;
const SYSCRG_CPU_CORE: usize = 0x004 / 4;
const SYSCRG_CPU_BUS: usize = 0x008 / 4;
const SYSCRG_GPU_ROOT: usize = 0x00C / 4;
const SYSCRG_PERH_ROOT: usize = 0x010 / 4;
const SYSCRG_BUS_ROOT: usize = 0x014 / 4;
const SYSCRG_NOCSTG_BUS: usize = 0x018 / 4;
const SYSCRG_AXI_CFG0: usize = 0x01C / 4;
const SYSCRG_STG_AXIAHB: usize = 0x020 / 4;
const SYSCRG_AHB0: usize = 0x024 / 4;
const SYSCRG_AHB1: usize = 0x028 / 4;
const SYSCRG_APB_BUS: usize = 0x02C / 4;
const SYSCRG_APB0: usize = 0x030 / 4;
// usb
const SYSCRG_USB_125M: usize = 0x17C / 4;
const SYSCRG_NOC_BUS_STG_AXI: usize = 0x180 / 4;
// misc
const SYSCRG_IOMUX_APB: usize = 0x1C0 / 4;
// gmac1
const SYSCRG_GMAC1_AHB: usize = 0x184 / 4;
const SYSCRG_GMAC1_AXI: usize = 0x188 / 4;
const SYSCRG_GMAC_SRC: usize = 0x18C / 4;
const SYSCRG_GMAC1_GTXCLK: usize = 0x190 / 4;
const SYSCRG_GMAC1_RMII_RTX: usize = 0x194 / 4;
const SYSCRG_GMAC1_PTP: usize = 0x198 / 4;
const SYSCRG_GMAC1_RX: usize = 0x19C / 4;
const SYSCRG_GMAC1_RX_INV: usize = 0x1A0 / 4;
const SYSCRG_GMAC1_TX: usize = 0x1A4 / 4;
const SYSCRG_GMAC1_TX_INV: usize = 0x1A8 / 4;
const SYSCRG_GMAC1_GTXC: usize = 0x1AC / 4;
// gmac0
const SYSCRG_GMAC0_GTXCLK: usize = 0x1B0 / 4;
const SYSCRG_GMAC0_PTP: usize = 0x1B4 / 4;
const SYSCRG_GMAC_PHY: usize = 0x1B8 / 4;
const SYSCRG_GMAC0_GTXC: usize = 0x1BC / 4;
// TODO ...
// uart
const SYSCRG_UART0_APB: usize = 0x244 / 4;
const SYSCRG_UART0_CORE: usize = 0x248 / 4;
// jtag
const SYSCRG_JTAG_CERTIFICATION_TRNG: usize = 0x2F4 / 4;
const SYSCRG_CLOCK_COUNT: usize = SYSCRG_JTAG_CERTIFICATION_TRNG + 1;
const SYSCRG_OSC: usize = SYSCRG_CLOCK_COUNT;
const SYSCRG_GMAC1_RMII_REFIN: usize = SYSCRG_CLOCK_COUNT + 1;
const SYSCRG_GMAC1_RGMII_RXIN: usize = SYSCRG_CLOCK_COUNT + 2;
#[allow(unused)]
const SYSCRG_I2STX_BCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 3;
#[allow(unused)]
const SYSCRG_I2STX_LRCK_EXT: usize = SYSCRG_CLOCK_COUNT + 4;
#[allow(unused)]
const SYSCRG_I2SRX_BCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 5;
#[allow(unused)]
const SYSCRG_I2SRX_LRCK_EXT: usize = SYSCRG_CLOCK_COUNT + 6;
#[allow(unused)]
const SYSCRG_TDM_EXT: usize = SYSCRG_CLOCK_COUNT + 7;
#[allow(unused)]
const SYSCRG_MCLK_EXT: usize = SYSCRG_CLOCK_COUNT + 8;
const SYSCRG_PLL0_OUT: usize = SYSCRG_CLOCK_COUNT + 9;
const SYSCRG_PLL1_OUT: usize = SYSCRG_CLOCK_COUNT + 10;
const SYSCRG_PLL2_OUT: usize = SYSCRG_CLOCK_COUNT + 11;
// AONCRG clocks
const AONCRG_OSC_DIV4: usize = 0x00 / 4;
const AONCRG_APB_FUNC: usize = 0x04 / 4;
// gmac0
const AONCRG_GMAC0_AHB: usize = 0x08 / 4;
const AONCRG_GMAC0_AXI: usize = 0x0C / 4;
const AONCRG_GMAC0_RMII_RTX: usize = 0x10 / 4;
const AONCRG_GMAC0_TX: usize = 0x14 / 4;
const AONCRG_GMAC0_TX_INV: usize = 0x18 / 4;
const AONCRG_GMAC0_RX: usize = 0x1C / 4;
const AONCRG_GMAC0_RX_INV: usize = 0x20 / 4;
// otpc
const AONCRG_OTPC_APB: usize = 0x24 / 4;
// rtc
const AONCRG_RTC_APB: usize = 0x28 / 4;
const AONCRG_RTC_INTERNAL: usize = 0x2C / 4;
const AONCRG_RTC_32K: usize = 0x30 / 4;
const AONCRG_RTC_CAL: usize = 0x34 / 4;
const AONCRG_CLOCK_COUNT: usize = AONCRG_RTC_CAL + 1;
const AONCRG_OSC: usize = AONCRG_CLOCK_COUNT;
const AONCRG_GMAC0_RMII_REFIN: usize = AONCRG_CLOCK_COUNT + 1;
const AONCRG_GMAC0_RGMII_RXIN: usize = AONCRG_CLOCK_COUNT + 2;
const AONCRG_STG_AXIAHB: usize = AONCRG_CLOCK_COUNT + 3;
const AONCRG_APB_BUS: usize = AONCRG_CLOCK_COUNT + 4;
const AONCRG_GMAC0_GTXCLK: usize = AONCRG_CLOCK_COUNT + 5;
const AONCRG_RTC_OSC: usize = AONCRG_CLOCK_COUNT + 6;
// STGCRG clocks
// hifi4
const STGCRG_HIFI4_CLK_CORE: usize = 0x00 / 4;
// usb
const STGCRG_USB_APB: usize = 0x04 / 4;
const STGCRG_USB_UTMI_APB: usize = 0x08 / 4;
const STGCRG_USB_AXI: usize = 0x0C / 4;
const STGCRG_USB_LPM: usize = 0x10 / 4;
const STGCRG_USB_STB: usize = 0x14 / 4;
const STGCRG_USB_APP_125: usize = 0x18 / 4;
const STGCRG_USB_REF: usize = 0x1C / 4;
const STGCRG_PCIE0_AXI_MST0: usize = 0x20 / 4;
const STGCRG_PCIE0_APB: usize = 0x24 / 4;
const STGCRG_PCIE0_TL: usize = 0x28 / 4;
const STGCRG_PCIE1_AXI_MST0: usize = 0x2C / 4;
const STGCRG_PCIE1_APB: usize = 0x30 / 4;
const STGCRG_PCIE1_TL: usize = 0x34 / 4;
const STGCRG_PCIE1_SLV_DEC: usize = 0x38 / 4;
const STGCRG_SECURITY_HCLK: usize = 0x3C / 4;
const STGCRG_SECURITY_MISC_AHB: usize = 0x40 / 4;
const STGCRG_MTRX_GRP0: usize = 0x44 / 4;
const STGCRG_MTRX_GRP0_BUS: usize = 0x48 / 4;
const STGCRG_MTRX_GRP0_STG: usize = 0x4C / 4;
const STGCRG_MTRX_GRP1: usize = 0x50 / 4;
const STGCRG_MTRX_GRP1_BUS: usize = 0x54 / 4;
const STGCRG_MTRX_GRP1_STG: usize = 0x58 / 4;
const STGCRG_MTRX_GRP1_HIFI: usize = 0x5C / 4;
const STGCRG_E24_RTC: usize = 0x60 / 4;
const STGCRG_E24_CORE: usize = 0x64 / 4;
const STGCRG_E24_DBG: usize = 0x68 / 4;
const STGCRG_DMA1P_AXI: usize = 0x6C / 4;
const STGCRG_DMA1P_AHB: usize = 0x70 / 4;
const STGCRG_CLOCK_COUNT: usize = STGCRG_DMA1P_AHB + 1;
const STGCRG_OSC: usize = STGCRG_CLOCK_COUNT;
const STGCRG_HIFI4_CORE: usize = STGCRG_CLOCK_COUNT + 1;
const STGCRG_STG_AXIAHB: usize = STGCRG_CLOCK_COUNT + 2;
const STGCRG_USB_125M: usize = STGCRG_CLOCK_COUNT + 3;
const STGCRG_CPU_BUS: usize = STGCRG_CLOCK_COUNT + 4;
const STGCRG_HIFI4_AXI: usize = STGCRG_CLOCK_COUNT + 5;
const STGCRG_NOCSTG_BUS: usize = STGCRG_CLOCK_COUNT + 6;
const STGCRG_APB_BUS: usize = STGCRG_CLOCK_COUNT + 7;
#[allow(unused)]
enum ClockDef {
Mux(&'static [usize]),
Div(u32, usize),
MuxDiv(u32, &'static [usize]),
Gate(usize),
GateDiv(u32, usize),
GateMux(&'static [usize]),
Inv(usize),
}
trait ClockDefinition {
const NAME: &'static str;
const CLOCKS: &'static [Option<(&'static str, ClockDef)>];
}
const SYSCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; SYSCRG_CLOCK_COUNT];
t[SYSCRG_CPU_ROOT] = Some(("clk_cpu_root", Mux(&[SYSCRG_OSC, SYSCRG_PLL0_OUT])));
t[SYSCRG_CPU_CORE] = Some(("clk_cpu_core", Div(7, SYSCRG_CPU_ROOT)));
t[SYSCRG_CPU_BUS] = Some(("clk_cpu_bus", Div(2, SYSCRG_CPU_CORE)));
t[SYSCRG_GPU_ROOT] = Some(("clk_gpu_root", Mux(&[SYSCRG_PLL2_OUT, SYSCRG_PLL1_OUT])));
t[SYSCRG_PERH_ROOT] = Some((
"clk_perh_root",
MuxDiv(2, &[SYSCRG_PLL0_OUT, SYSCRG_PLL2_OUT]),
));
t[SYSCRG_BUS_ROOT] = Some(("clk_bus_root", Mux(&[SYSCRG_OSC, SYSCRG_PLL2_OUT])));
t[SYSCRG_NOCSTG_BUS] = Some(("clk_nocstg_bus", Div(3, SYSCRG_BUS_ROOT)));
t[SYSCRG_AXI_CFG0] = Some(("clk_axi_cfg0", Div(3, SYSCRG_BUS_ROOT)));
t[SYSCRG_STG_AXIAHB] = Some(("clk_stg_axiahb", Div(2, SYSCRG_AXI_CFG0)));
t[SYSCRG_AHB0] = Some(("clk_ahb0", Gate(SYSCRG_STG_AXIAHB)));
t[SYSCRG_AHB1] = Some(("clk_ahb1", Gate(SYSCRG_STG_AXIAHB)));
t[SYSCRG_APB_BUS] = Some(("clk_apb_bus", Div(8, SYSCRG_STG_AXIAHB)));
t[SYSCRG_APB0] = Some(("clk_apb0", Gate(SYSCRG_APB_BUS)));
// usb
t[SYSCRG_USB_125M] = Some(("clk_usb_125m", Div(15, SYSCRG_PLL0_OUT)));
t[SYSCRG_NOC_BUS_STG_AXI] = Some(("clk_noc_bus_stg_axi", Gate(SYSCRG_NOCSTG_BUS)));
// misc
t[SYSCRG_IOMUX_APB] = Some(("clk_iomux_apb", Gate(SYSCRG_APB_BUS)));
// gmac1
t[SYSCRG_GMAC1_AHB] = Some(("clk_gmac1_ahb", Gate(SYSCRG_AHB0)));
t[SYSCRG_GMAC1_AXI] = Some(("clk_gmac1_axi", Gate(SYSCRG_STG_AXIAHB)));
t[SYSCRG_GMAC_SRC] = Some(("clk_gmac_src", Div(7, SYSCRG_PLL0_OUT)));
t[SYSCRG_GMAC1_GTXCLK] = Some(("clk_gmac1_gtxclk", Div(15, SYSCRG_PLL0_OUT)));
t[SYSCRG_GMAC1_RMII_RTX] = Some(("clk_gmac1_rmii_rtx", Div(30, SYSCRG_GMAC1_RMII_REFIN)));
t[SYSCRG_GMAC1_PTP] = Some(("clk_gmac1_ptp", GateDiv(31, SYSCRG_GMAC_SRC)));
t[SYSCRG_GMAC1_RX] = Some((
"clk_gmac1_rx",
Mux(&[SYSCRG_GMAC1_RGMII_RXIN, SYSCRG_GMAC1_RMII_RTX]),
));
t[SYSCRG_GMAC1_RX_INV] = Some(("clk_gmac1_rx_inv", Inv(SYSCRG_GMAC1_RX)));
t[SYSCRG_GMAC1_TX] = Some((
"clk_gmac1_tx",
GateMux(&[SYSCRG_GMAC1_GTXCLK, SYSCRG_GMAC1_RMII_RTX]),
));
t[SYSCRG_GMAC1_TX_INV] = Some(("clk_gmac1_tx_inv", Inv(SYSCRG_GMAC1_TX)));
t[SYSCRG_GMAC1_GTXC] = Some(("clk_gmac1_gtxc", Gate(SYSCRG_GMAC1_GTXCLK)));
// gmac0
t[SYSCRG_GMAC0_GTXCLK] = Some(("clk_gmac0_gtxclk", GateDiv(15, SYSCRG_PLL0_OUT)));
t[SYSCRG_GMAC0_PTP] = Some(("clk_gmac0_ptp", GateDiv(31, SYSCRG_GMAC_SRC)));
t[SYSCRG_GMAC_PHY] = Some(("clk_gmac_phy", GateDiv(31, SYSCRG_GMAC_SRC)));
t[SYSCRG_GMAC0_GTXC] = Some(("clk_gmac0_gtxc", Gate(SYSCRG_GMAC0_GTXCLK)));
// TODO ...
// uart
t[SYSCRG_UART0_APB] = Some(("clk_uart0_apb", Gate(SYSCRG_APB0)));
t[SYSCRG_UART0_CORE] = Some(("clk_uart0_core", Gate(SYSCRG_OSC)));
t
};
const AONCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; AONCRG_CLOCK_COUNT];
t[AONCRG_OSC_DIV4] = Some(("clk_osc_div4", Div(4, AONCRG_OSC)));
t[AONCRG_APB_FUNC] = Some(("clk_apb_func", Mux(&[AONCRG_OSC_DIV4, AONCRG_OSC])));
// gmac0
t[AONCRG_GMAC0_AHB] = Some(("clk_gmac0_ahb", Gate(AONCRG_STG_AXIAHB)));
t[AONCRG_GMAC0_AXI] = Some(("clk_gmac0_axi", Gate(AONCRG_STG_AXIAHB)));
t[AONCRG_GMAC0_RMII_RTX] = Some(("clk_gmac0_rmii_rtx", Div(30, AONCRG_GMAC0_RMII_REFIN)));
t[AONCRG_GMAC0_TX] = Some((
"clk_gmac0_tx",
GateMux(&[AONCRG_GMAC0_GTXCLK, AONCRG_GMAC0_RMII_RTX]),
));
t[AONCRG_GMAC0_TX_INV] = Some(("clk_gmac0_tx_inv", Inv(AONCRG_GMAC0_TX)));
t[AONCRG_GMAC0_RX] = Some((
"clk_gmac0_rx",
Mux(&[AONCRG_GMAC0_RGMII_RXIN, AONCRG_GMAC0_RMII_RTX]),
));
t[AONCRG_GMAC0_RX_INV] = Some(("clk_gmac0_rx_inv", Inv(AONCRG_GMAC0_RX)));
// otpc
t[AONCRG_OTPC_APB] = Some(("clk_otpc_apb", Gate(AONCRG_APB_BUS)));
// rtc
t[AONCRG_RTC_APB] = Some(("clk_rtc_apb", Gate(AONCRG_APB_BUS)));
t[AONCRG_RTC_INTERNAL] = Some(("clk_rtc_internal", Div(1022, AONCRG_OSC)));
t[AONCRG_RTC_32K] = Some(("clk_rtc_32k", Mux(&[AONCRG_RTC_OSC, AONCRG_RTC_INTERNAL])));
t[AONCRG_RTC_CAL] = Some(("clk_rtc_cal", Gate(AONCRG_OSC)));
t
};
const STGCRG_CLOCKS: &'static [Option<(&'static str, ClockDef)>] = &const {
use ClockDef::*;
let mut t = [const { None }; STGCRG_CLOCK_COUNT];
// hifi4
t[STGCRG_HIFI4_CLK_CORE] = Some(("clk_hifi4_clk_core", Gate(STGCRG_HIFI4_CORE)));
// usb
t[STGCRG_USB_APB] = Some(("clk_usb_apb", Gate(STGCRG_APB_BUS)));
t[STGCRG_USB_UTMI_APB] = Some(("clk_usb_utmi_apb", Gate(STGCRG_APB_BUS)));
t[STGCRG_USB_AXI] = Some(("clk_usb_axi", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_USB_LPM] = Some(("clk_usb_lpm", GateDiv(2, STGCRG_OSC)));
t[STGCRG_USB_STB] = Some(("clk_usb_stb", GateDiv(4, STGCRG_OSC)));
t[STGCRG_USB_APP_125] = Some(("clk_usb_app_125", Gate(STGCRG_USB_125M)));
t[STGCRG_USB_REF] = Some(("clk_usb_ref", Div(2, STGCRG_OSC)));
// pcie
t[STGCRG_PCIE0_AXI_MST0] = Some(("clk_pcie0_axi_mst0", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_PCIE0_APB] = Some(("clk_pcie0_apb", Gate(STGCRG_APB_BUS)));
t[STGCRG_PCIE0_TL] = Some(("clk_pcie0_tl", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_PCIE1_AXI_MST0] = Some(("clk_pcie1_axi_mst0", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_PCIE1_APB] = Some(("clk_pcie1_apb", Gate(STGCRG_APB_BUS)));
t[STGCRG_PCIE1_TL] = Some(("clk_pcie1_tl", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_PCIE1_SLV_DEC] = Some(("clk_pcie1_slv_dec", Gate(STGCRG_STG_AXIAHB)));
// security
t[STGCRG_SECURITY_HCLK] = Some(("clk_security_hclk", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_SECURITY_MISC_AHB] = Some(("clk_security_misc_ahb", Gate(STGCRG_STG_AXIAHB)));
// mtrx
t[STGCRG_MTRX_GRP0] = Some(("clk_mtrx_grp0", Gate(STGCRG_CPU_BUS)));
t[STGCRG_MTRX_GRP0_BUS] = Some(("clk_mrtx_grp0_bus", Gate(STGCRG_NOCSTG_BUS)));
t[STGCRG_MTRX_GRP0_STG] = Some(("clk_mtrx_grp0_stg", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_MTRX_GRP1] = Some(("clk_mtrx_grp1", Gate(STGCRG_CPU_BUS)));
t[STGCRG_MTRX_GRP1_BUS] = Some(("clk_mtrx_grp1_bus", Gate(STGCRG_NOCSTG_BUS)));
t[STGCRG_MTRX_GRP1_STG] = Some(("clk_mtrx_grp1_stg", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_MTRX_GRP1_HIFI] = Some(("clk_mtrx_grp1_hifi", Gate(STGCRG_HIFI4_AXI)));
// e24
t[STGCRG_E24_RTC] = Some(("clk_e24_rtc", GateDiv(24, STGCRG_OSC)));
t[STGCRG_E24_CORE] = Some(("clk_e24_core", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_E24_DBG] = Some(("clk_e24_dbg", Gate(STGCRG_STG_AXIAHB)));
// dma1p
t[STGCRG_DMA1P_AXI] = Some(("clk_dma1p_axi", Gate(STGCRG_STG_AXIAHB)));
t[STGCRG_DMA1P_AHB] = Some(("clk_dma1p_ahb", Gate(STGCRG_STG_AXIAHB)));
t
};
struct Crg<C, P> {
clock_mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
reset_mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
parents: P,
_pd: PhantomData<C>,
}
struct Pll {
syscon: Arc<dyn DeviceTreeSyscon>,
clk_osc: ClockHandle,
}
struct PllDef {
fbdiv_offset: usize,
pd_offset: usize,
frac_offset: usize,
prediv_offset: usize,
dacpd_bit: u32,
dsmpd_bit: u32,
fbdiv_shift: usize,
}
#[derive(Debug)]
struct PllRegs {
dacpd: bool,
dsmpd: bool,
fbdiv: u32,
#[allow(unused)]
frac: u32,
postdiv1: u32,
prediv: u32,
}
unsafe impl<C, P> Send for Crg<C, P> {}
unsafe impl<C, P> Sync for Crg<C, P> {}
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> Crg<C, P> {
unsafe fn map(base: PhysicalAddress, parents: P) -> Result<Arc<Self>, Error> {
let clock_mapping =
DeviceMemoryIoMut::map_slice(base, C::CLOCKS.len(), Default::default())?;
let reset_mapping =
DeviceMemoryIoMut::map_slice(base.add(C::CLOCKS.len() * 4), 16, Default::default())?;
Ok(Arc::new(Self {
clock_mapping: IrqSafeSpinlock::new(clock_mapping),
reset_mapping: IrqSafeSpinlock::new(reset_mapping),
parents,
_pd: PhantomData,
}))
}
fn clock(&self, index: usize) -> Result<&(&str, ClockDef), Error> {
C::CLOCKS
.get(index)
.ok_or(Error::DoesNotExist)?
.as_ref()
.ok_or(Error::DoesNotExist)
.inspect_err(|_| {
log::error!(
"{}: clock {:#x} does not exist/is not implemented",
C::NAME,
index
)
})
}
fn read_clock_reg(&self, index: usize) -> u32 {
self.clock_mapping.lock()[index]
}
fn enable_clock_inner(&self, index: usize) -> Result<(), Error> {
if index >= C::CLOCKS.len() {
return self.parents[index - C::CLOCKS.len()].enable();
}
let (clk_name, clk_def) = self.clock(index)?;
let (parent, write_gate) = match clk_def {
ClockDef::GateDiv(_, parent) | ClockDef::Gate(parent) => (*parent, true),
ClockDef::GateMux(parents) => (parents[0], true),
ClockDef::Inv(parent) | ClockDef::Div(_, parent) => (*parent, false),
ClockDef::Mux(parents) | ClockDef::MuxDiv(_, parents) => (parents[0], false),
};
// Enable parent
self.enable_clock_inner(parent)?;
// Enable clock
if write_gate {
let mut lock = self.clock_mapping.lock();
let ptr = &raw mut lock[index];
unsafe {
let val = ptr.read_volatile();
if val & (1 << 31) == 0 {
log::info!("{}: enable clock {:?}", C::NAME, clk_name);
ptr.write_volatile(val | (1 << 31));
}
}
}
Ok(())
}
fn clock_rate_inner(&self, index: usize) -> Result<Hertz, Error> {
if index >= C::CLOCKS.len() {
return self.parents[index - C::CLOCKS.len()].rate();
}
let (_, clk_def) = self.clock(index)?;
let reg = self.read_clock_reg(index);
let reg_div = reg & 0xFFFFFF;
let (parent, div) = match clk_def {
&ClockDef::Gate(parent) | &ClockDef::Inv(parent) => (parent, 1),
&ClockDef::Div(_max, parent) => (parent, reg_div),
// TODO read actual parent for muxes
&ClockDef::GateDiv(_max, parent) => (parent, reg_div),
ClockDef::GateMux(parents) | ClockDef::Mux(parents) => (parents[0], 1),
ClockDef::MuxDiv(_max, parents) => (parents[0], reg_div),
};
let parent_rate = self.clock_rate_inner(parent)?;
Ok(parent_rate / div)
}
fn set_reset_asserted_inner(&self, index: usize, asserted: bool) -> Result<(), Error> {
let reg_index = index / 32;
let reg_shift = index % 32;
let mut lock = self.reset_mapping.lock();
let ptr = &raw mut lock[reg_index];
unsafe {
let val = ptr.read_volatile();
if asserted {
ptr.write_volatile(val | (1 << reg_shift));
} else {
ptr.write_volatile(val & !(1 << reg_shift));
}
}
Ok(())
}
}
impl<C: ClockDefinition, P> Device for Crg<C, P> {
fn display_name(&self) -> &str {
C::NAME
}
}
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> ClockController for Crg<C, P> {
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let index = clock.ok_or(Error::InvalidArgument)? as usize;
self.clock_rate_inner(index)
}
fn set_clock_rate(&self, _clock: Option<u32>, _rate: Hertz) -> Result<Hertz, Error> {
todo!("Clock rate update not supported for jh71x0 CRGs")
}
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
let index = clock.ok_or(Error::InvalidArgument)? as usize;
self.enable_clock_inner(index)
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
todo!("Clock disable not supported for jh71x0 CRGs")
}
}
impl<C: ClockDefinition, P: Index<usize, Output = ClockHandle>> ResetController for Crg<C, P> {
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
let reset = reset.ok_or(Error::InvalidArgument)? as usize;
self.set_reset_asserted_inner(reset, true)
}
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
let reset = reset.ok_or(Error::InvalidArgument)? as usize;
self.set_reset_asserted_inner(reset, false)
}
}
impl<C: ClockDefinition + 'static, P: Index<usize, Output = ClockHandle> + 'static>
DeviceTreeClockController for Crg<C, P>
{
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
clock: Some(clock),
parent: self.clone(),
},
1,
))
}
}
impl<C: ClockDefinition + 'static, P: Index<usize, Output = ClockHandle> + 'static>
DeviceTreeResetController for Crg<C, P>
{
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)> {
let reset = property.read_cell(offset, 1)? as u32;
Some((
ResetHandle {
reset: Some(reset),
parent: self.clone(),
},
1,
))
}
}
impl Pll {
const PLLS: &[PllDef] = &[
PllDef {
pd_offset: 0x18,
fbdiv_offset: 0x1C,
frac_offset: 0x20,
prediv_offset: 0x24,
dacpd_bit: 1 << 24,
dsmpd_bit: 1 << 25,
fbdiv_shift: 0,
},
PllDef {
pd_offset: 0x24,
fbdiv_offset: 0x24,
frac_offset: 0x28,
prediv_offset: 0x2C,
dacpd_bit: 1 << 15,
dsmpd_bit: 1 << 16,
fbdiv_shift: 17,
},
PllDef {
pd_offset: 0x2C,
fbdiv_offset: 0x2C,
frac_offset: 0x30,
prediv_offset: 0x34,
dacpd_bit: 1 << 15,
dsmpd_bit: 1 << 16,
fbdiv_shift: 17,
},
];
const FBDIV_MASK: u32 = 0xFFF;
const FRAC_MASK: u32 = 0xFFFFFF;
const POSTDIV1_MASK: u32 = 0x3;
const PREDIV_MASK: u32 = 0x3F;
const POSTDIV1_SHIFT: usize = 28;
const PREDIV_SHIFT: usize = 0;
const FRAC_SHIFT: usize = 0;
fn read_pll_regs(&self, index: usize) -> Result<PllRegs, Error> {
let pll = &Self::PLLS[index];
let val = self.syscon.read_register(pll.pd_offset)?;
let dacpd = val & pll.dacpd_bit != 0;
let dsmpd = val & pll.dsmpd_bit != 0;
let val = self.syscon.read_register(pll.fbdiv_offset)?;
let fbdiv = (val >> pll.fbdiv_shift) & Self::FBDIV_MASK;
let val = self.syscon.read_register(pll.frac_offset)?;
let frac = (val >> Self::FRAC_SHIFT) & Self::FRAC_MASK;
let postdiv1 = (val >> Self::POSTDIV1_SHIFT) & Self::POSTDIV1_MASK;
let val = self.syscon.read_register(pll.prediv_offset)?;
let prediv = (val >> Self::PREDIV_SHIFT) & Self::PREDIV_MASK;
Ok(PllRegs {
dacpd,
dsmpd,
fbdiv,
frac,
postdiv1,
prediv,
})
}
}
impl Device for Pll {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
// Dump PLL rates
for i in 0..3 {
let regs = self.read_pll_regs(i as usize)?;
let rate = self.clock_rate(Some(i))?;
log::info!("PLL{i} rate: {rate}");
log::info!(" {regs:?}");
}
Ok(())
}
fn display_name(&self) -> &str {
"jh7110-pll"
}
}
impl ClockController for Pll {
fn set_clock_rate(&self, _clock: Option<u32>, _rate: Hertz) -> Result<Hertz, Error> {
todo!("PLL rate configuration not yet implemented")
}
fn clock_rate(&self, clock: Option<u32>) -> Result<Hertz, Error> {
let index = clock.ok_or(Error::InvalidArgument)? as usize;
if index >= 3 {
return Err(Error::InvalidArgument);
}
let osc = self.clk_osc.rate()?;
let pll = self.read_pll_regs(index)?;
if pll.dacpd && pll.dsmpd {
// Integer mode
Ok((osc * pll.fbdiv) / (pll.prediv << pll.postdiv1))
} else if !pll.dacpd && !pll.dsmpd {
// Fraction mode
todo!()
} else {
todo!("Invalid PLL dacpd/dsmpd combination")
}
}
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
// Assumed always enabled
Ok(())
}
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
Err(Error::InvalidOperation)
}
}
impl DeviceTreeClockController for Pll {
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
let clock = property.read_cell(offset, 1)? as u32;
Some((
ClockHandle {
clock: Some(clock),
parent: self.clone(),
},
1,
))
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-syscrg"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
struct Syscrg;
impl ClockDefinition for Syscrg {
const NAME: &'static str = "jh7110-syscrg";
const CLOCKS: &'static [Option<(&'static str, ClockDef)>] = SYSCRG_CLOCKS;
}
let base = node.map_base(context, 0)?;
let parents = [
node.named_clock("osc")?,
node.named_clock("gmac1_rmii_refin")?,
node.named_clock("gmac1_rgmii_rxin")?,
node.named_clock("i2stx_bclk_ext")?,
node.named_clock("i2stx_lrck_ext")?,
node.named_clock("i2srx_bclk_ext")?,
node.named_clock("i2srx_lrck_ext")?,
node.named_clock("tdm_ext")?,
node.named_clock("mclk_ext")?,
node.named_clock("pll0_out")?,
node.named_clock("pll1_out")?,
node.named_clock("pll2_out")?
];
let syscrg = unsafe { Crg::<Syscrg, _>::map(base, parents) }.ok()?;
node.make_clock_controller(syscrg.clone());
node.make_reset_controller(syscrg.clone());
Some(syscrg)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-aoncrg"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
struct Aoncrg;
impl ClockDefinition for Aoncrg {
const NAME: &'static str = "jh7110-aoncrg";
const CLOCKS: &'static [Option<(&'static str, ClockDef)>] = AONCRG_CLOCKS;
}
let base = node.map_base(context, 0)?;
let parents = [
node.named_clock("osc")?,
node.named_clock("gmac0_rmii_refin")?,
node.named_clock("gmac0_rgmii_rxin")?,
node.named_clock("stg_axiahb")?,
node.named_clock("apb_bus")?,
node.named_clock("gmac0_gtxclk")?,
node.named_clock("rtc_osc")?,
];
let aoncrg = unsafe { Crg::<Aoncrg, _>::map(base, parents) }.ok()?;
node.make_clock_controller(aoncrg.clone());
node.make_reset_controller(aoncrg.clone());
Some(aoncrg)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-stgcrg"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
struct Stgcrg;
impl ClockDefinition for Stgcrg {
const NAME: &'static str = "jh7110-stgcrg";
const CLOCKS: &'static [Option<(&'static str, ClockDef)>] = STGCRG_CLOCKS;
}
let base = node.map_base(context, 0)?;
let parents = [
node.named_clock("osc").unwrap(),
node.named_clock("hifi4_core").unwrap(),
node.named_clock("stg_axiahb").unwrap(),
node.named_clock("usb_125m").unwrap(),
node.named_clock("cpu_bus").unwrap(),
node.named_clock("hifi4_axi").unwrap(),
node.named_clock("nocstg_bus").unwrap(),
node.named_clock("apb_bus").unwrap()
];
let stgcrg = unsafe { Crg::<Stgcrg, _>::map(base, parents) }.ok()?;
node.make_clock_controller(stgcrg.clone());
node.make_reset_controller(stgcrg.clone());
Some(stgcrg)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-pll"],
driver: {
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let clk_osc = node.clock(0)?; // 24MHz
// Make sure parent syscon is probed
let syscon_phandle = node.parent()?.prop_usize("phandle")? as u32;
let syscon = lookup_phandle(syscon_phandle, true)?.as_system_controller()?;
let pll = Arc::new(Pll {
syscon,
clk_osc,
});
node.make_clock_controller(pll.clone());
Some(pll)
}
}
}
+7
View File
@@ -0,0 +1,7 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod clocks;
mod pinctrl;
+445
View File
@@ -0,0 +1,445 @@
use alloc::{sync::Arc, vec::Vec};
use device_api::{
clock::{ClockHandle, ResetHandle},
device::{Device, DeviceInitContext},
gpio::{
GpioController, GpioInterruptEvent, GpioPinLevel, OutputPinBias, PinHandle,
SinglePinDirection,
},
};
use device_tree::{
driver::{
device_tree_driver,
util::{generic_gpio_config, GenericPinctrlBiasConfig, GenericPinctrlConfig},
DeviceTreeGpioPins, DeviceTreePinController, Node, ProbeContext,
},
DeviceTreePropertyRead, TProp,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
use libk_util::{bit::BitField, sync::IrqSafeSpinlock, OneTimeInit};
use yggdrasil_abi::error::Error;
#[derive(Debug)]
struct PinMuxConfig {
pin: u8,
function: u8,
doen: u8,
dout: u8,
din: u8,
}
struct SysRegs(DeviceMemoryIoMut<'static, [u32]>);
struct AonRegs(DeviceMemoryIoMut<'static, [u32]>);
unsafe impl Send for SysRegs {}
unsafe impl Sync for SysRegs {}
unsafe impl Send for AonRegs {}
unsafe impl Sync for AonRegs {}
struct Gpio<R: GpioRegs> {
regs: IrqSafeSpinlock<R>,
init: OneTimeInit<Result<(), Error>>,
clocks: Vec<ClockHandle>,
resets: Vec<ResetHandle>,
}
trait GpioRegs: Sized + Send + 'static {
const NAME: &'static str;
const DOUT_OFFSET: usize;
const DOEN_OFFSET: usize;
const GPI_OFFSET: usize;
const DOUT_MASK: u32;
const DOEN_MASK: u32;
const GPI_MASK: u32;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error>;
fn reg_mut(&mut self, index: usize) -> *mut u32;
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)>;
fn write_output_pin(&mut self, pin: usize, value: bool) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val.set_bit(shift, value);
reg_dout.write_volatile(val);
}
}
fn configure_pin(&mut self, pin: usize, doen: u32, dout: u32, din: u32) {
let reg = pin / 4;
let shift = (pin % 4) * 8;
let reg_doen = self.reg_mut(Self::DOEN_OFFSET + reg);
let reg_dout = self.reg_mut(Self::DOUT_OFFSET + reg);
unsafe {
let mut val = reg_dout.read_volatile();
val &= !(Self::DOUT_MASK << shift);
val |= dout << shift;
reg_dout.write_volatile(val);
let mut val = reg_doen.read_volatile();
val &= !(Self::DOEN_MASK << shift);
val |= doen << shift;
reg_doen.write_volatile(val);
if din != 0xFF {
let din_reg = din as usize / 4;
let din_shift = (din % 4) * 8;
let reg_din = self.reg_mut(Self::GPI_OFFSET + din_reg);
let mut val = reg_din.read_volatile();
val &= !(Self::GPI_MASK << din_shift);
val |= (pin as u32 + 2) << din_shift;
reg_din.write_volatile(val);
}
}
}
fn configure_pin_function(&mut self, pin: usize, func: u32) {
let Some((offset, shift, max)) = self.pin_functions(pin) else {
return;
};
if func > max {
return;
}
log::info!("jh7110-sys-pinctrl: set pad{pin} function={func}");
let reg = self.reg_mut(offset / 4);
unsafe {
let mut val = reg.read_volatile();
val &= !(0x3 << shift);
val |= func << shift;
reg.write_volatile(val);
}
}
fn set_input_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_output_enabled(&mut self, _pin: usize, _enabled: bool) {}
fn set_bias(&mut self, _pin: usize, _bias: GenericPinctrlBiasConfig) {}
}
// sys-pinctrl pads
const fn pad_gpio(n: usize) -> usize {
n
}
const PAD_GMAC1_RXC: usize = 82;
const SYS_PIN_COUNT: usize = 95;
const SYS_PIN_FUNCTIONS: [Option<(usize, usize, u32)>; SYS_PIN_COUNT] = const {
let mut t = [const { None }; SYS_PIN_COUNT];
t[PAD_GMAC1_RXC] = Some((0x29C, 0, 1));
t[pad_gpio(10)] = Some((0x29C, 2, 3));
t[pad_gpio(11)] = Some((0x29C, 5, 3));
t[pad_gpio(12)] = Some((0x29C, 8, 3));
t[pad_gpio(13)] = Some((0x29C, 11, 3));
t[pad_gpio(14)] = Some((0x29C, 14, 3));
t[pad_gpio(15)] = Some((0x29C, 17, 3));
t[pad_gpio(16)] = Some((0x29C, 20, 3));
t[pad_gpio(17)] = Some((0x29C, 23, 3));
t[pad_gpio(18)] = Some((0x29C, 26, 3));
t[pad_gpio(19)] = Some((0x29C, 29, 3));
t[pad_gpio(20)] = Some((0x2A0, 0, 3));
t[pad_gpio(21)] = Some((0x2A0, 3, 3));
t[pad_gpio(22)] = Some((0x2A0, 6, 3));
t[pad_gpio(23)] = Some((0x2A0, 9, 3));
t[pad_gpio(24)] = Some((0x2A0, 12, 3));
t[pad_gpio(25)] = Some((0x2A0, 15, 3));
t[pad_gpio(26)] = Some((0x2A0, 18, 3));
t[pad_gpio(27)] = Some((0x2A0, 21, 3));
t[pad_gpio(28)] = Some((0x2A0, 24, 3));
t[pad_gpio(29)] = Some((0x2A0, 27, 3));
t[pad_gpio(30)] = Some((0x2A4, 0, 3));
t[pad_gpio(31)] = Some((0x2A4, 3, 3));
t[pad_gpio(32)] = Some((0x2A4, 6, 3));
t[pad_gpio(33)] = Some((0x2A4, 9, 3));
t[pad_gpio(34)] = Some((0x2A4, 12, 3));
t[pad_gpio(35)] = Some((0x2A4, 15, 3));
t[pad_gpio(36)] = Some((0x2A4, 17, 3));
t[pad_gpio(37)] = Some((0x2A4, 20, 3));
t[pad_gpio(38)] = Some((0x2A4, 23, 3));
t[pad_gpio(39)] = Some((0x2A4, 26, 3));
t[pad_gpio(40)] = Some((0x2A4, 29, 3));
t[pad_gpio(41)] = Some((0x2A8, 0, 3));
t[pad_gpio(42)] = Some((0x2A8, 3, 3));
t[pad_gpio(43)] = Some((0x2A8, 6, 3));
t[pad_gpio(44)] = Some((0x2A8, 9, 3));
t[pad_gpio(45)] = Some((0x2A8, 12, 3));
t[pad_gpio(46)] = Some((0x2A8, 15, 3));
t[pad_gpio(47)] = Some((0x2A8, 18, 3));
t[pad_gpio(48)] = Some((0x2A8, 21, 3));
t[pad_gpio(49)] = Some((0x2A8, 24, 3));
t[pad_gpio(50)] = Some((0x2A8, 27, 3));
t[pad_gpio(51)] = Some((0x2A8, 30, 3));
t[pad_gpio(52)] = Some((0x2AC, 0, 3));
t[pad_gpio(53)] = Some((0x2AC, 2, 3));
t[pad_gpio(54)] = Some((0x2AC, 4, 3));
t[pad_gpio(55)] = Some((0x2AC, 6, 3));
t[pad_gpio(56)] = Some((0x2AC, 9, 3));
t[pad_gpio(57)] = Some((0x2AC, 12, 3));
t[pad_gpio(58)] = Some((0x2AC, 15, 3));
t[pad_gpio(59)] = Some((0x2AC, 18, 3));
t[pad_gpio(60)] = Some((0x2AC, 21, 3));
t[pad_gpio(61)] = Some((0x2AC, 24, 3));
t[pad_gpio(62)] = Some((0x2AC, 27, 3));
t[pad_gpio(63)] = Some((0x2AC, 30, 3));
t[pad_gpio(6)] = Some((0x2B0, 0, 3));
t[pad_gpio(7)] = Some((0x2B0, 2, 3));
t[pad_gpio(8)] = Some((0x2B0, 5, 3));
t[pad_gpio(9)] = Some((0x2B0, 8, 3));
t
};
impl PinMuxConfig {
fn from_fdt(fdt: u32) -> Self {
let pin = fdt as u8;
let function = ((fdt >> 8) & 0x3) as u8;
let doen = ((fdt >> 10) & 0x3F) as u8;
let dout = (fdt >> 16) as u8;
let din = (fdt >> 24) as u8;
Self {
pin,
function,
doen,
dout,
din,
}
}
}
impl<R: GpioRegs> Gpio<R> {
fn from_device_tree(node: &Arc<Node>, context: &mut ProbeContext) -> Result<Arc<Self>, Error> {
let base = node.map_base(context, 0).ok_or(Error::DoesNotExist)?;
let regs = unsafe { R::map(base) }?;
let clocks = node.clocks().map(|c| c.collect()).unwrap_or_default();
let resets = node.resets().map(|c| c.collect()).unwrap_or_default();
let gpio = Arc::new(Self {
regs: IrqSafeSpinlock::new(regs),
init: OneTimeInit::new(),
clocks,
resets,
});
Ok(gpio)
}
fn configure_pinmux(&self, mux: PinMuxConfig, cfg: &GenericPinctrlConfig) {
let mut regs = self.regs.lock();
regs.configure_pin_function(mux.pin as _, mux.function as _);
regs.configure_pin(mux.pin as _, mux.doen as _, mux.dout as _, mux.din as _);
if let Some(enable) = cfg.input_enable {
regs.set_input_enabled(mux.pin as _, enable);
}
if let Some(enable) = cfg.output_enable {
regs.set_output_enabled(mux.pin as _, enable);
}
if let Some(bias) = cfg.bias {
regs.set_bias(mux.pin as _, bias);
}
}
fn configure_pin_output(
&self,
pin: usize,
_active_level: GpioPinLevel,
initial_level: GpioPinLevel,
_output_bias: Option<OutputPinBias>,
) -> Result<(), Error> {
// TODO configure pull up/pull down?
let mut regs = self.regs.lock();
let dout_val = initial_level == GpioPinLevel::High;
regs.configure_pin_function(pin, 0);
regs.configure_pin(pin, 0, dout_val as u32, 0);
Ok(())
}
fn ensure_init(&self) -> Result<(), Error> {
let res = self.init.or_init_with(|| {
for clock in self.clocks.iter() {
clock.enable()?;
}
for reset in self.resets.iter() {
reset.deassert()?;
}
// TODO disable all pin IRQs
Ok(())
});
*res
}
}
impl<R: GpioRegs> Device for Gpio<R> {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
self.ensure_init()
}
fn display_name(&self) -> &str {
R::NAME
}
}
impl<R: GpioRegs> GpioController for Gpio<R> {
fn setup_gpio(&self, pin: &PinHandle, _event: Option<GpioInterruptEvent>) -> Result<(), Error> {
log::warn!(
"TODO: setup gpio #{} input={}, output={}",
pin.index,
pin.config.input,
pin.config.output
);
// TODO bidi pins?
match pin.config.force_single_direction() {
Some(SinglePinDirection::Input) => todo!(),
Some(SinglePinDirection::Output) => self.configure_pin_output(
pin.index as usize,
pin.config.active_level,
pin.config.initial_level,
pin.config.output_bias,
),
None => {
todo!("{}: support for bidi gpio", R::NAME);
}
}
}
fn read_gpio(&self, _pin: &PinHandle) -> Result<bool, Error> {
Err(Error::NotImplemented)
}
fn write_gpio(&self, pin: &PinHandle, value: bool) -> Result<(), Error> {
self.regs.lock().write_output_pin(pin.index as _, value);
Ok(())
}
}
impl<R: GpioRegs> DeviceTreePinController for Gpio<R> {
fn configure_pin_group(self: Arc<Self>, pins: &Arc<Node>) -> Result<(), Error> {
self.ensure_init()?;
for child in pins.children() {
let pinmux = child.property("pinmux").ok_or(Error::DoesNotExist)?;
let cfg = GenericPinctrlConfig::from_node(child);
for i in 0..pinmux.len() / 4 {
let mux = pinmux.read_cell(i, 1).unwrap() as u32;
let mux = PinMuxConfig::from_fdt(mux);
self.configure_pinmux(mux, &cfg);
}
}
Ok(())
}
fn map_gpio(
self: Arc<Self>,
property: &TProp,
offset: usize,
info: &DeviceTreeGpioPins,
) -> Option<(PinHandle, usize)> {
let (pin, options) = property.read_cells_at(offset, (1, 1))?;
let config = generic_gpio_config(options as u32, info);
Some((
PinHandle {
index: pin as u32,
config,
parent: self,
},
2,
))
}
}
impl GpioRegs for SysRegs {
const NAME: &'static str = "jh7110-sys-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x40 / 4;
const GPI_OFFSET: usize = 0x80 / 4;
const DOEN_MASK: u32 = 0x1F;
const DOUT_MASK: u32 = 0x3F;
const GPI_MASK: u32 = 0x3F;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 192, Default::default()).map(Self)
}
fn pin_functions(&self, pin: usize) -> Option<(usize, usize, u32)> {
SYS_PIN_FUNCTIONS.get(pin).copied()?
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
impl GpioRegs for AonRegs {
const NAME: &'static str = "jh7110-aon-pinctrl";
const DOEN_OFFSET: usize = 0x00;
const DOUT_OFFSET: usize = 0x04 / 4;
const GPI_OFFSET: usize = 0x08 / 4;
const DOEN_MASK: u32 = 0x7;
const DOUT_MASK: u32 = 0xF;
const GPI_MASK: u32 = 0xF;
unsafe fn map(base: PhysicalAddress) -> Result<Self, Error> {
DeviceMemoryIoMut::map_slice(base, 64, Default::default()).map(Self)
}
fn pin_functions(&self, _pin: usize) -> Option<(usize, usize, u32)> {
None
}
fn reg_mut(&mut self, index: usize) -> *mut u32 {
&raw mut self.0[index]
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-sys-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<SysRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-sys-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
device_tree_driver! {
compatible: ["starfive,jh7110-aon-pinctrl"],
driver: {
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
let gpio = Gpio::<AonRegs>::from_device_tree(node, context)
.inspect_err(|e| log::error!("jh7110-aon-pinctrl: probe error: {e:?}"))
.ok()?;
node.make_pin_controller(gpio.clone());
Some(gpio)
}
}
}
+17
View File
@@ -0,0 +1,17 @@
[package]
name = "ygg_driver_bsp_riscv"
version = "0.1.0"
edition = "2024"
[dependencies]
device-tree.workspace = true
device-api.workspace = true
yggdrasil-abi.workspace = true
libk-mm.workspace = true
libk-util.workspace = true
libk.workspace = true
kernel-arch-riscv64.workspace = true
tock-registers.workspace = true
log.workspace = true
bytemuck.workspace = true
+6
View File
@@ -0,0 +1,6 @@
#![allow(unsafe_op_in_unsafe_fn)]
#![no_std]
extern crate alloc;
mod plic;
@@ -1,7 +1,3 @@
//! RISC-V PLIC driver
use core::sync::atomic::Ordering;
use abi::{error::Error, primitive_enum};
use alloc::{sync::Arc, vec::Vec};
use device_api::{
device::{Device, DeviceInitContext},
@@ -16,6 +12,7 @@ use device_tree::{
},
DeviceTreePropertyRead, TProp,
};
use kernel_arch_riscv64::boot_hart_id;
use libk::{arch::Cpu, device::register_external_interrupt_controller};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
@@ -24,8 +21,7 @@ use tock_registers::{
register_structs,
registers::{ReadOnly, ReadWrite},
};
use crate::arch::riscv64::BOOT_HART_ID;
use yggdrasil_abi::{error::Error, primitive_enum};
const MAX_IRQS: usize = 1024;
@@ -138,7 +134,7 @@ impl ExternalInterruptController for Plic {
fn enable_irq(&self, irq: Irq) -> Result<(), Error> {
// TODO balance IRQs between harts?
let irq = self.validate_irq(irq)?;
let bsp_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
let bsp_hart_id = boot_hart_id() as u32;
let context = self
.hart_context(bsp_hart_id)
.ok_or(Error::InvalidArgument)
@@ -159,7 +155,7 @@ impl ExternalInterruptController for Plic {
_options: IrqOptions,
handler: Arc<dyn InterruptHandler>,
) -> Result<(), Error> {
let bsp_hart_id = BOOT_HART_ID.load(Ordering::Acquire);
let bsp_hart_id = boot_hart_id() as u32;
let irq = self.validate_irq(irq)?;
let context = self
.hart_context(bsp_hart_id)
+2 -2
View File
@@ -1,7 +1,7 @@
//! PCI/PCIe bus interfaces
#![no_std]
#![feature(let_chains, decl_macro)]
#![allow(clippy::missing_transmute_annotations)]
#![allow(clippy::missing_transmute_annotations, clippy::identity_op)]
extern crate alloc;
@@ -466,7 +466,7 @@ impl PciBusManager {
for segment in this.segments.iter_mut() {
for device in segment.devices.iter_mut() {
let mut device = device.lock();
if !f(&mut *device)? {
if !f(&mut device)? {
return Ok(());
}
}
+2 -2
View File
@@ -349,7 +349,7 @@ pub trait PciConfigurationSpace {
}
/// Returns an iterator over the PCI capabilities
fn capability_iter(&self) -> CapabilityIterator<Self> {
fn capability_iter(&self) -> CapabilityIterator<'_, Self> {
let status = PciStatusRegister::from_bits_retain(self.status());
let current = if status.contains(PciStatusRegister::CAPABILITIES_LIST) {
@@ -374,7 +374,7 @@ pub trait PciConfigurationSpace {
/// Locates a capability within this configuration space
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
self.capability_iter().find_map(|(id, offset, len)| {
if id.map_or(false, |id| id == C::ID) && C::check(self, offset, len) {
if id == Some(C::ID) && C::check(self, offset, len) {
Some(C::data(self, offset, len))
} else {
None
@@ -152,7 +152,7 @@ impl UsbDriver for UsbHidKeyboardDriver {
event_count += state.retain(&buffer[2..], &mut events[event_count..]);
event_count += state.press(&buffer[2..], &mut events[event_count..]);
let events = unsafe { MaybeUninit::slice_assume_init_ref(&events[..event_count]) };
let events = unsafe { events[..event_count].assume_init_ref() };
for &event in events {
log::trace!("Generic Keyboard: {:?}", event);
@@ -131,7 +131,7 @@ impl Bbb {
&mut self,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<usize, Error> {
if buffer.len() == 0 {
if buffer.is_empty() {
return Ok(0);
}
let len = self

Some files were not shown because too many files have changed in this diff Show More