Compare commits

..

1 Commits

Author SHA1 Message Date
alnyan 7e5979c1a2 unwind: basic unwinding for x86-64 2024-12-05 22:21:46 +02:00
1063 changed files with 26646 additions and 174126 deletions
+1 -1
View File
@@ -1,2 +1,2 @@
[alias]
xtask = "run --manifest-path ./xtask/Cargo.toml --release --"
xtask = "run --manifest-path ./xtask/Cargo.toml --"
-5
View File
@@ -1,8 +1,3 @@
/target
/toolchain
/xtask.toml
/qemu.toml
/etc/boot/yboot.cfg
/disk-*.img
/tmp-*.txt
/*.log
Generated
+325 -1056
View File
File diff suppressed because it is too large Load Diff
+4 -19
View File
@@ -6,18 +6,17 @@ exclude = [
"tool/abi-generator",
"toolchain",
"userspace/dynload-program",
"userspace/lib/ygglibc",
"toolchain-c"
"userspace/lib/ygglibc"
]
members = [
"xtask",
"kernel/tools/gentables",
"kernel",
"lib/abi",
"lib/libyalloc",
"lib/runtime",
"lib/qemu",
"lib/abi-serde",
"lib/libutil"
"lib/unwind"
]
[workspace.dependencies]
@@ -39,7 +38,6 @@ ahash = { version = "0.8.11", default-features = false, features = ["no-rng"] }
# acpi
acpi = { git = "https://github.com/alnyan/acpi.git", package = "acpi", branch = "acpi-system" }
rsdp = { git = "https://github.com/alnyan/acpi.git", package = "rsdp", branch = "acpi-system" }
aml = { git = "https://github.com/alnyan/acpi.git", branch = "acpi-system" }
acpi-system = { git = "https://github.com/alnyan/acpi-system.git" }
@@ -48,14 +46,12 @@ yboot-proto.path = "boot/yboot-proto"
# Local libs
abi-lib.path = "lib/abi-lib"
abi-serde.path = "lib/abi-serde"
yggdrasil-abi.path = "lib/abi"
abi-generator.path = "tool/abi-generator"
# Kernel parts
kernel-arch-interface.path = "kernel/arch/interface"
kernel-arch-aarch64.path = "kernel/arch/aarch64"
kernel-arch-riscv64.path = "kernel/arch/riscv64"
kernel-arch-x86_64.path = "kernel/arch/x86_64"
kernel-arch-i686.path = "kernel/arch/i686"
kernel-arch-x86.path = "kernel/arch/x86"
@@ -66,6 +62,7 @@ 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"
@@ -90,15 +87,3 @@ features = ["no_std_stream"]
unexpected_cfgs = { level = "allow", check-cfg = ['cfg(rust_analyzer)'] }
[workspace.lints.clippy]
derivable_impls = { level = "allow" }
[profile.dev]
opt-level = 1
# split-debuginfo = "packed"
lto = "thin"
panic = "abort"
[profile.test]
split-debuginfo = "none"
[profile.dev.package."*"]
opt-level = 3
-9
View File
@@ -1,9 +0,0 @@
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.
+38 -58
View File
@@ -8,38 +8,22 @@ Rust Unix-like operating system.
Main features
-------------
* Architecture support:
* [aarch64](kernel/src/arch/aarch64)
* [x86_64](kernel/src/arch/x86_64)
* [i686](kernel/src/arch/i686) (Pentium Pro and later)
* Core features:
* Kernel/userspace preemptive multithreading
* Kernel-space multitasking with `async`/`await` runtime
* Symmetric Multiprocessing
* Rust-style interfaces for most of the stuff like memory management, devices etc.
* Filesystem features:
* Unix-like virtual filesystem:
* Architecture support: [aarch64](/kernel/src/arch/aarch64) and [x86_64](/kernel/src/arch/x86_64)
* Kernel/userspace preemptive multithreading
* Kernel-space multitasking with `async`/`await` runtime
* Symmetric Multiprocessing
* Unix-like virtual filesystem:
files, directories, block/char devices, symlinks, mounts
* In-memory read-write filesystem for tar-based initrd
* sysfs
* devfs
* ext2
* Userspace features:
* [Kernel-user ABI](lib/abi-def/yggdrasil.abi) generated from a rust-like description language
* Sanitized system calls better suited for use in Rust
* Binary formats: ELF + `#!/...` shebangs
* C compatibility through a [custom Rust libc](userspace/lib/ygglibc)
* Userspace multithreading
* Synchronization primitives through futex-like interface
* Unix-like signals and exceptions
* [Dynamic loader](userspace/dyn-loader) for linking with shared libraries
* Hardware features:
* PCI Express devices
* NVMe drive support (read/write, currently x86_64 only, due to lack of MSI-X support on aarch64/i686).
* AHCI SATA drive support (read/write)
* xHCI USB host controller
* VirtIO Network + GPU framebuffer support
* USB HID keyboards
* [Kernel-user ABI](/lib/abi-def/yggdrasil.abi) generated from a rust-like description language
* In-memory read-write filesystem for tar-based initrd
* sysfs/devfs
* Binary formats: ELF + `#!/...` shebangs
* Rust-style interfaces for most of the stuff like memory management, devices etc.
* PCI Express devices
* NVMe drive support (read/write, currently x86_64 only, due to lack of MSI-X support on aarch64).
* AHCI SATA drive support (read/write)
* xHCI USB host controller
* USB HID keyboards
aarch64-specific:
@@ -48,34 +32,39 @@ aarch64-specific:
* ARM generic timer as system/monotonic timer
* GICv2 IRQ controller
x86-specific:
x86_64-specific:
* Boot options:
* x86_64: UEFI [yboot](https://git.alnyan.me/yggdrasil/yboot)
* i686: multiboot/grub
* UEFI boot through [yboot](https://git.alnyan.me/yggdrasil/yboot)
(no plans for legacy boot)
* I/O and Local APIC IRQ controllers
* PS/2 keyboard
* HPET for x86_64
* i8253-based timer for i686 or as a fallback timer
* i8253-based timer (got some problems with HPET on
real hw, had to revert, lol)
* COM ports
* ACPI, [work in progress](https://github.com/rust-osdev/acpi), mostly broken
on real hardware, so currently disabled
on real hardware
* ACPI shutdown
* PCI IRQ pin routing
* Events like power button, etc.
* Fancy framebuffer console
Userspace features:
* Sanitized system calls better suited for Rust
* Userspace threads
* Synchronization primitives through futex-like interface
* Unix-like signals and exceptions
Building the OS
---------------
**NOTE** This project uses `xtask`-style build system. To see help, use `cargo xtask --help`.
Prerequisites:
* Decent CPU and a sizable amount of RAM
* ~40G of free disk space for a full build
* ~20G of free disk space
* Patience
**NOTE** Full OS build requires you to build the `*-unknown-yggdrasil`
**NOTE** Full OS build requires you to build the `*-unknown-yggdrasil`
Rust toolchain, which may take quite a while, so be prepared.
Steps:
@@ -105,30 +94,21 @@ Steps:
```
2. Run `cargo xtask toolchain` to fetch, build and link the toolchain
**NOTE** if toolchain fetch fails for some reason, try cloning directly
from `https://git.alnyan.me/yggdrasil/yggdrasil-rust.git` with appropriate
branch.
3. Run `cargo xtask` to build the OS.
Once the OS has been built, you can run it in QEMU by executing
Once the OS has been built, you can run it in QEMU by executing
`cargo xtask qemu`. For more `xtask` commands, see `cargo xtask --help`.
General plans (in no particular order)
--------------------------------------
1. Get it running on more real hardware
2. Get a full LLVM build to work
3. Get rustc to work
4. Get self-hosted
5. Run doom (?)
In addition to eternal code cleanup, I've been doing quite a lazy job at that lately...
* Better unification of architecture code
* `async` for VFS (?)
* Code cleanup, I've been doing quite a lazy job at that lately...
Navigation
----------
* Kernel: [`kernel`](kernel)
* Userspace: [`userspace`](userspace)
* ABI definitions: [`lib/abi-def`](lib/abi-def)
* Kernel: [`/kernel`](/kernel)
* Userspace: [`/userspace`](/userspace)
* ABI definitions: [`/lib/abi-def`](/lib/abi-def)
Binary file not shown.
-1
View File
@@ -1,4 +1,3 @@
#![allow(unused)]
#![no_std]
use bytemuck::{Pod, Zeroable};
-8
View File
@@ -4,9 +4,6 @@ use bytemuck::{Pod, Zeroable};
use crate::{AvailableRegion, IterableMemoryMap, LoadProtocolHeader};
pub const PIXEL_R8G8B8A8: u32 = 1;
pub const PIXEL_B8G8R8A8: u32 = 2;
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
pub struct LoadProtocol {
@@ -15,9 +12,6 @@ pub struct LoadProtocol {
pub memory_map: MemoryMap,
pub cmdline: u64,
pub cmdline_len: u64,
pub rsdp_address: u64,
pub initrd_address: u64,
pub initrd_size: u64,
@@ -52,8 +46,6 @@ pub struct FramebufferOption {
pub res_stride: u64,
pub res_address: u64,
pub res_size: u64,
pub res_format: u32,
pub _0: u32,
}
impl AvailableRegion for AvailableMemoryRegion {
-39
View File
@@ -1,39 +0,0 @@
use uefi::{
proto::media::file::{Directory, File, FileAttribute, FileMode},
CStr16, Result, Status,
};
#[derive(Debug)]
pub struct Config {
pub cmdline: [u8; 4096],
pub cmdline_len: usize,
}
impl Default for Config {
fn default() -> Self {
Self {
cmdline: [0; 4096],
cmdline_len: 0,
}
}
}
impl Config {
pub fn load(root: &mut Directory, path: &CStr16) -> Result<Self> {
let file = match root.open(path, FileMode::Read, FileAttribute::empty()) {
Ok(file) => file,
Err(error) => {
root.reset_entry_readout().ok();
log::warn!("Couldn't open {path:?}: {error:?}");
return Ok(Self::default());
}
};
root.reset_entry_readout().ok();
let mut this = Self::default();
let mut file = file.into_regular_file().ok_or(Status::INVALID_PARAMETER)?;
this.cmdline_len = file.read(&mut this.cmdline)?;
Ok(this)
}
}
+38 -111
View File
@@ -1,8 +1,7 @@
use core::mem::size_of;
use bytemuck::Zeroable;
use log::{error, info};
use types::{Rela, SHT_RELA};
use log::{debug, error, info};
// TODO use 'elf' crate
use uefi::{
prelude::BootServices,
@@ -16,7 +15,6 @@ 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};
@@ -25,18 +23,14 @@ 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 {
@@ -83,20 +77,6 @@ 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
@@ -112,8 +92,6 @@ pub struct LoadedObject {
pub image_start: u64,
pub image_end: u64,
pub load_address: u64,
pub entry: u64,
pub protocol_struct_paddr: u64,
@@ -127,12 +105,6 @@ 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>;
}
@@ -147,23 +119,6 @@ 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())?;
@@ -185,11 +140,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 })
}
@@ -215,6 +170,11 @@ 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;
@@ -249,22 +209,16 @@ impl Object {
assert_eq!(image_start & 0xFFF, 0);
assert_eq!(image_end & 0xFFF, 0);
// Allocate memory to load the kernel into
let kernel_load_address = bs
info!("Image start: {:#x}, end: {:#x}", image_start, image_end);
// Reserve the kernel memory
let reserved_addr = bs
.allocate_pages(
AllocateType::MaxAddress(0xFFFFFFFF),
AllocateType::Address(image_start),
MemoryType::LOADER_DATA,
(image_end - image_start) as usize / 0x1000,
)
.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}");
.expect("Could not allocate memory for kernel image");
assert_eq!(reserved_addr, image_start);
// 3. Load the segments
for i in 0..self.ehdr.phnum {
@@ -274,80 +228,53 @@ impl Object {
continue;
}
let segment_load_base = phdr.paddr + kernel_load_address;
info!(
"[{i}] Load {:#x?}",
segment_load_base..segment_load_base + phdr.memsz
"Load segment {}: {:#x?}",
i,
phdr.paddr..phdr.paddr + phdr.memsz
);
if phdr.filesz > 0 {
let dst_slice = unsafe {
core::slice::from_raw_parts_mut(
segment_load_base as *mut u8,
phdr.filesz as usize,
)
// The section has load data
let dst = unsafe {
core::slice::from_raw_parts_mut(phdr.paddr 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_slice)?;
self.file.read_exact(dst)?;
}
if phdr.memsz > phdr.filesz {
let dst_slice = unsafe {
let dst = unsafe {
core::slice::from_raw_parts_mut(
(segment_load_base + phdr.filesz) as *mut u8,
(phdr.paddr + phdr.filesz) as *mut u8,
(phdr.memsz - phdr.filesz) as usize,
)
};
dst_slice.fill(0);
}
}
debug!(
"Zero data {:#x?}",
phdr.paddr + phdr.filesz..phdr.paddr + phdr.memsz
);
// 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}"),
}
dst.fill(0);
}
}
// 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 + kernel_load_address; // (loc_proto.address as u64) - proto_data.kernel_virt_offset;
let protocol_struct_paddr = (loc_proto.address as u64) - proto_data.kernel_virt_offset;
let protocol_version = proto_data.header.version;
let entry = self.ehdr.entry + kernel_load_address;
let entry = self.ehdr.entry;
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.div_ceil(0x1000);
let page_count = (size + 0xFFF) / 0x1000;
let base = bs.allocate_pages(
AllocateType::MaxAddress(MAXIMUM_ADDRESS),
+25 -93
View File
@@ -2,7 +2,6 @@
#![no_std]
#![no_main]
pub mod config;
pub mod elf;
pub mod initrd;
pub mod mem;
@@ -10,16 +9,13 @@ pub mod protocol_ext;
use core::{arch::asm, mem::size_of, ops::Deref};
use config::Config;
use elf::Object;
use log::{debug, error, info};
use uefi::{
prelude::*,
proto::{
console::gop::{self, GraphicsOutput, PixelFormat},
device_path::DevicePath,
loaded_image::LoadedImage,
media::{file::Directory, fs::SimpleFileSystem},
console::gop::GraphicsOutput, device_path::DevicePath, loaded_image::LoadedImage,
media::fs::SimpleFileSystem,
},
table::{
boot::{AllocateType, MemoryType, ScopedProtocol},
@@ -28,49 +24,34 @@ use uefi::{
Error,
};
use yboot_proto::{
v1::{self, AvailableMemoryRegion, FramebufferOption},
v1::{AvailableMemoryRegion, FramebufferOption},
LoadProtocolV1, LOADER_MAGIC,
};
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
}
use crate::{mem::MemoryDescriptorExt, protocol_ext::GraphicsOutputExt};
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)?;
let mode = gop.modes().max_by_key(mode_score).ok_or_else(|| {
error!("No mode found");
// 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
);
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() {
PixelFormat::Bgr => v1::PIXEL_B8G8R8A8,
PixelFormat::Rgb => v1::PIXEL_R8G8B8A8,
_ => 0,
};
fb.res_width = res_width as _;
fb.res_height = res_height as _;
fb.res_width = fb.req_width;
fb.res_height = fb.req_height;
fb.res_address = result.as_mut_ptr() as _;
fb.res_stride = mode.info().stride() as u64 * 4;
fb.res_size = result.size() as _;
fb.res_format = format;
info!(
"Framebuffer: {}x{} @ {:#x}",
@@ -93,7 +74,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();
@@ -104,45 +85,27 @@ fn boot_partition(
bs.open_protocol_exclusive::<SimpleFileSystem>(fs_handle)
}
fn open_root(image: Handle, bs: &BootServices) -> Result<Directory, Error> {
let mut boot_partition = boot_partition(image, bs)?;
boot_partition.open_volume()
}
fn load_kernel<'a>(
config: &Config,
root: &mut Directory,
ih: Handle,
st: &SystemTable<Boot>,
) -> Result<(u64, u64, u64, &'a mut LoadProtocolV1), Error> {
) -> Result<(u64, u64, &'a mut LoadProtocolV1), Error> {
let bs = st.boot_services();
let mut kernel_obj = Object::open(root, cstr16!("kernel.elf"))?;
let mut fs = boot_partition(ih, bs)?;
let mut root = fs.open_volume()?;
let mut kernel_obj = Object::open(&mut root, cstr16!("kernel.elf"))?;
let loaded_obj = kernel_obj.load(bs)?;
debug!("Loaded object: {:#x?}", loaded_obj);
// Load initrd
let (initrd_start, initrd_size) = initrd::load_somewhere(bs, root, cstr16!("initrd.img"))?;
let (initrd_start, initrd_size) = initrd::load_somewhere(bs, &mut root, cstr16!("initrd.img"))?;
debug!(
"Loaded initrd: {:#x?}",
initrd_start..initrd_start + initrd_size
);
// Load cmdline
let cmdline = if config.cmdline_len != 0 {
let address = bs.allocate_pages(AllocateType::AnyPages, MemoryType::LOADER_DATA, 1)?;
let dst =
unsafe { core::slice::from_raw_parts_mut(address as *mut u8, config.cmdline_len) };
dst.copy_from_slice(&config.cmdline[..config.cmdline_len]);
debug!("Cmdline at {:#x?}", address);
address
} else {
0
};
// Other versions are not existent yet
assert_eq!(loaded_obj.protocol_version, 1);
let proto_data = unsafe { &mut *(loaded_obj.protocol_struct_paddr as *mut LoadProtocolV1) };
@@ -153,9 +116,6 @@ fn load_kernel<'a>(
})?;
info!("RSDP at {:#x}", rsdp);
proto_data.cmdline = cmdline;
proto_data.cmdline_len = config.cmdline_len as _;
proto_data.rsdp_address = rsdp;
proto_data.initrd_address = initrd_start;
proto_data.initrd_size = initrd_size;
@@ -183,14 +143,13 @@ fn load_kernel<'a>(
let entry = loaded_obj.entry + proto_data.kernel_virt_offset;
Ok((entry, loaded_obj.load_address, mmap_memory, proto_data))
Ok((entry, 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();
@@ -217,43 +176,16 @@ 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, in("ecx") load_base, options(noreturn, att_syntax));
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, options(noreturn, att_syntax));
}
#[entry]
fn efi_main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status {
if uefi_services::init(&mut system_table).is_err() {
return Status::LOAD_ERROR;
}
uefi_services::init(&mut system_table).unwrap();
let bs = system_table.boot_services();
let mut root = match open_root(image_handle, bs) {
Ok(root) => root,
Err(error) => {
error!("Could not open boot partition root: {error:?}");
return Status::LOAD_ERROR;
}
};
let config = match Config::load(&mut root, cstr16!("yboot.cfg")) {
Ok(config) => config,
Err(error) => {
error!("Malformed yboot.cfg: {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;
}
};
let (entry, mmap_memory, proto_data) = load_kernel(image_handle, &system_table).unwrap();
unsafe {
map_and_enter_kernel(system_table, proto_data, mmap_memory, load_base, entry);
map_and_enter_kernel(system_table, proto_data, mmap_memory, 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 })
}
-98
View File
@@ -1,98 +0,0 @@
**NOTE** I haven't yet tested direct boot through Raspberry's
proprietary bootloader.
Booting Yggdrasil on Raspberry Pi 4B with u-boot:
1. Clone u-boot sources and build with rpi_4_defconfig
**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)
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
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
5. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
6. Connect an ethernet and serial to the Pi and run the following commands in u-boot shell:
### 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}
###### 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")
-130
View File
@@ -1,130 +0,0 @@
Booting Yggdrasil OS on Starfive VisionFive 2 RISC-V board:
* TODO: proper format for initrd image
Prerequisites:
* OpenSBI + u-boot (you can use the regular debian installation from Starfive)
* yggdrasil-kernel.bin
* initrd.img
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 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
dhcp
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")
@@ -1,9 +1,9 @@
{
"is-builtin": false,
"arch": "aarch64",
"os": "none",
"abi": "softfloat",
"llvm-target": "aarch64-unknown-none",
"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",
"data-layout": "e-m:e-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,7 +13,6 @@
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"eh-frame-header": false,
"linker": "rust-lld",
+53
View File
@@ -0,0 +1,53 @@
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) {
*(.eh_frame*)
. = ALIGN(16);
PROVIDE(__dt_probes_start = .);
KEEP(*(.dt_probes));
PROVIDE(__dt_probes_end = .);
*(.rodata*)
}
. = 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(__kernel_end = .);
};
Binary file not shown.
-433
View File
@@ -1,433 +0,0 @@
/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>;
};
};
Binary file not shown.
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
@@ -1,217 +0,0 @@
/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
@@ -0,0 +1,29 @@
{
"is-builtin": false,
"arch": "x86",
"cpu": "pentium4",
"os": "none",
"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"
}
+53
View File
@@ -0,0 +1,53 @@
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 = .);
};
-75
View File
@@ -1,75 +0,0 @@
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)
}
}
-75
View File
@@ -1,75 +0,0 @@
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)
}
}
-73
View File
@@ -1,73 +0,0 @@
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)
}
}
-27
View File
@@ -1,27 +0,0 @@
{
"arch": "riscv64",
"os": "none",
"abi": "softfloat",
"cpu": "generic-rv64",
"llvm-target": "riscv64",
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
"max-atomic-width": 64,
"target-pointer-width": "64",
"features": "+m,+a,+c",
"disable-redzone": true,
"executables": true,
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"code-model": "medium",
"eh-frame-header": false,
"crt-objects-fallback": "false",
"emit-debug-gdb-scripts": false,
"llvm-abiname": "lp64",
"linker": "rust-lld",
"linker-flavor": "ld.lld"
}
+2 -4
View File
@@ -1,21 +1,19 @@
{
"is-builtin": false,
"arch": "x86_64",
"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,-avx2,+soft-float",
"features": "-avx,-sse,+soft-float",
"disable-redzone": true,
"executables": true,
"panic-strategy": "abort",
"dynamic-linking": true,
"relocation-model": "pic",
"position-independent-executables": true,
"has-thread-local": false,
+52
View File
@@ -0,0 +1,52 @@
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 = .);
};
+12 -28
View File
@@ -7,18 +7,19 @@ authors = ["Mark Poliakov <mark@alnyan.me>"]
[dependencies]
abi-lib.workspace = true
abi-serde.workspace = true
yggdrasil-abi.workspace = true
kernel-arch-interface.workspace = true
libk.workspace = true
libk-util.workspace = true
libk-mm.workspace = true
libk-device.workspace = true
elf.workspace = true
chrono.workspace = true
device-api = { workspace = true, features = ["derive"] }
device-api-macros.workspace = true
memtables.workspace = true
vmalloc.workspace = true
kernel-arch.workspace = true
@@ -28,17 +29,13 @@ ygg_driver_usb = { path = "driver/bus/usb" }
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_usb_xhci = { path = "driver/usb/xhci" }
ygg_driver_input = { path = "driver/input" }
ygg_driver_usb_xhci.path = "driver/usb/xhci"
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
kernel-fs = { path = "driver/fs/kernel-fs" }
memfs = { path = "driver/fs/memfs" }
ext2 = { path = "driver/fs/ext2" }
ygg_driver_fat32.path = "driver/fs/fat32"
log.workspace = true
bitflags.workspace = true
@@ -48,7 +45,6 @@ bytemuck.workspace = true
futures-util.workspace = true
crossbeam-queue.workspace = true
async-trait.workspace = true
cfg-if.workspace = true
git-version = "0.3.9"
@@ -56,26 +52,21 @@ 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
kernel-arch-x86_64.workspace = true
kernel-arch-x86.workspace = true
ygg_driver_acpi.path = "driver/acpi"
ygg_driver_net_igbe.path = "driver/net/igbe"
ygg_driver_nvme = { path = "driver/block/nvme" }
acpi.workspace = true
aml.workspace = true
acpi-system.workspace = true
[target.'cfg(target_arch = "x86")'.dependencies]
kernel-arch-i686.workspace = true
kernel-arch-x86.workspace = true
[build-dependencies]
abi-generator.workspace = true
@@ -87,16 +78,9 @@ 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"]
+8 -6
View File
@@ -3,19 +3,21 @@ name = "kernel-arch"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[target.'cfg(all(target_os = "none", target_arch = "x86_64"))'.dependencies]
kernel-arch-x86_64.path = "x86_64"
kernel-arch-x86_64 = { path = "x86_64" }
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
kernel-arch-aarch64.path = "aarch64"
kernel-arch-aarch64 = { path = "aarch64" }
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
kernel-arch-riscv64.path = "riscv64"
[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies]
kernel-arch-i686 = { path = "i686" }
[target.'cfg(not(target_os = "none"))'.dependencies]
kernel-arch-hosted.path = "hosted"
kernel-arch-hosted = { path = "hosted" }
[dependencies]
kernel-arch-interface.path = "interface"
kernel-arch-interface = { path = "interface" }
cfg-if.workspace = true
+1 -3
View File
@@ -7,6 +7,7 @@ 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
@@ -16,6 +17,3 @@ tock-registers.workspace = true
[build-dependencies]
cc = "1.0"
[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)
+1 -9
View File
@@ -23,8 +23,6 @@
.endm
.macro LOAD_TASK_STATE
dsb ishst
// x19 == tpidr_el0, x20 = ttbr0_el1
ldp x19, x20, [sp, #16 * 6]
msr tpidr_el0, x19
@@ -38,12 +36,6 @@
ldp x29, x30, [sp, #16 * 5]
add sp, sp, #{context_size}
isb sy
tlbi vmalle1is
ic iallu
dsb ish
isb sy
.endm
__aarch64_task_enter_kernel:
@@ -95,7 +87,7 @@ __aarch64_task_enter_user:
mov lr, xzr
dsb ish
dmb ish
isb sy
eret
+1 -3
View File
@@ -208,12 +208,10 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
stack.push(mdscr_el1);
stack.push(context.stack_pointer);
let ttbr0 = context.address_space | (context.asid << 48) | 1;
setup_common_context(
&mut stack,
__aarch64_task_enter_user as _,
ttbr0,
context.address_space,
context.thread_pointer as _,
);
+17 -42
View File
@@ -1,19 +1,16 @@
#![no_std]
#![feature(decl_macro)]
#![feature(naked_functions, trait_upcasting)]
#![allow(clippy::new_without_default)]
extern crate alloc;
use core::sync::atomic::{AtomicUsize, Ordering};
use aarch64_cpu::{
asm::barrier,
registers::{DAIF, MPIDR_EL1, TPIDR_EL1},
};
use alloc::{boxed::Box, sync::Arc, vec::Vec};
use device_api::interrupt::LocalInterruptController;
use aarch64_cpu::registers::{DAIF, MPIDR_EL1, TPIDR_EL1};
use alloc::{boxed::Box, vec::Vec};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuData, CpuImpl, IpiQueue},
cpu::{CpuImpl, IpiQueue},
guard::IrqGuard,
task::Scheduler,
util::OneTimeInit,
@@ -32,21 +29,20 @@ pub struct ArchitectureImpl;
pub trait GicInterface: LocalInterruptController {}
pub struct PerCpuData {
pub gic: OneTimeInit<Arc<dyn GicInterface>>,
pub gic: OneTimeInit<&'static dyn GicInterface>,
}
impl CpuData for PerCpuData {}
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
#[unsafe(naked)]
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
core::arch::naked_asm!("1: nop; b 1b");
unsafe {
core::arch::naked_asm!("1: nop; b 1b");
}
}
impl ArchitectureImpl {
#[inline]
pub fn local_cpu_data() -> Option<&'static mut PerCpuData> {
unsafe { (Self::local_cpu() as *mut PerCpuData).as_mut() }
}
@@ -67,7 +63,6 @@ impl Architecture for ArchitectureImpl {
DAIF.read(DAIF::I) != 0
}
#[inline(never)]
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
@@ -125,7 +120,13 @@ impl Architecture for ArchitectureImpl {
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
None
let local = Self::local_cpu_data()?;
let intc = *local.gic.try_get()?;
Some(intc)
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
todo!()
}
fn cpu_available_features<S: Scheduler>(_cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
@@ -135,30 +136,4 @@ impl Architecture for ArchitectureImpl {
fn cpu_enabled_features<S: Scheduler>(_cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
None
}
// Cache/barrier operation
fn load_barrier() {
barrier::dmb(barrier::ISHLD);
}
fn store_barrier() {
barrier::dmb(barrier::ISHST);
}
fn memory_barrier() {
barrier::dsb(barrier::SY);
}
fn flush_virtual_range(range: core::ops::Range<usize>) {
// TODO cache line assumed to be 64 bytes
const CLSIZE: usize = 64;
let start = range.start & !(CLSIZE - 1);
let end = (range.end + (CLSIZE - 1)) & !(CLSIZE - 1);
for line in (start..end).step_by(CLSIZE) {
unsafe {
core::arch::asm!("dc ivac, {address}", address = in(reg) line);
}
}
}
}
-124
View File
@@ -1,124 +0,0 @@
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
@@ -1,141 +0,0 @@
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);
}
}
+352 -101
View File
@@ -1,49 +1,99 @@
#![allow(clippy::missing_safety_doc)]
use aarch64_cpu::{
asm::barrier,
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1},
use core::{
alloc::Layout,
ops::{Deref, DerefMut},
ptr::addr_of,
sync::atomic::AtomicUsize,
sync::atomic::Ordering,
};
use aarch64_cpu::registers::{TTBR0_EL1, TTBR1_EL1};
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
sync::IrqSafeSpinlock,
sync::split_spinlock,
KERNEL_VIRT_OFFSET,
};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use tock_registers::interfaces::{ReadWriteable, Writeable};
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::Writeable;
use yggdrasil_abi::error::Error;
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;
static KERNEL_MEMORY_LOCK: IrqSafeSpinlock<ArchitectureImpl, ()> = IrqSafeSpinlock::new(());
// TODO eliminate this requirement by using precomputed indices
const MAPPING_OFFSET: usize = KERNEL_VIRT_OFFSET;
const KERNEL_PHYS_BASE: usize = 0x40080000;
// 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
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<lock: ArchitectureImpl>: KernelImageObject<FixedTables> =
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
}
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
if address < MEMORY_LIMIT.load(Ordering::Acquire) {
address + RAM_MAPPING_OFFSET
} else {
panic!("Invalid physical address: {address:#x}");
panic!("Invalid physical address: {:#x}", address);
}
}
fn physicalize(address: usize) -> u64 {
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
if address < RAM_MAPPING_OFFSET
|| address - RAM_MAPPING_OFFSET >= MEMORY_LIMIT.load(Ordering::Acquire)
{
panic!("Invalid virtual (-> physical) address {address:#x}");
panic!("Not a virtualized physical address: {:#x}", address);
}
(address - KERNEL_VIRT_OFFSET) as u64
(address - RAM_MAPPING_OFFSET) as _
}
unsafe fn map_device_pages(
@@ -51,96 +101,297 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
map_device_memory(PhysicalAddress::from_u64(base), count, attrs)
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _lock = KERNEL_MEMORY_LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
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_INNER
| 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());
}
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;
// TODO invalidate tlb
}
/// # 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 {
todo!()
}
let mut tables = KERNEL_TABLES.lock();
assert_eq!(tables.l1.data[index + RAM_MAPPING_START_L1I], 0);
tables.l1.data[index + RAM_MAPPING_START_L1I] =
((index * L1::SIZE) as u64) | ram_block_flags().bits();
}
// 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));
}
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>::device_block(base.add(j * L2::SIZE));
}
// log::debug!(
// "map l2s: base={:#x}, count={} -> {:#x}",
// base,
// count,
// DEVICE_MAPPING_OFFSET + i * 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(
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(
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!(),
}
}
#[inline]
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
pub fn tlb_flush_vaae1(mut page: usize) {
page >>= 12;
unsafe {
core::arch::asm!("tlbi vaae1, {page}", page = in(reg) page);
}
}
#[inline]
pub fn auto_upper_address<T>(ptr: *const T) -> usize {
let address = ptr.addr();
if address < KERNEL_VIRT_OFFSET {
address + KERNEL_VIRT_OFFSET
} else {
address
/// (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 = KERNEL_TABLES.lock().l1.data.as_ptr().addr() 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.lock();
let early_mapping_l3_phys = addr_of!(EARLY_MAPPING_L3) as usize - KERNEL_VIRT_OFFSET;
let device_mapping_l2_phys = addr_of!(DEVICE_MAPPING_L2) as usize - KERNEL_VIRT_OFFSET;
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());
}
}
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,
);
}
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::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();
}
pub unsafe fn init_lower(bsp: bool) {
setup_memory_attributes();
if bsp {
fixed::setup();
}
fixed::load();
enable_mmu();
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();
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();
}
+10 -55
View File
@@ -7,7 +7,7 @@ use core::{
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
process::ProcessAddressSpaceManager,
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
@@ -17,9 +17,8 @@ use yggdrasil_abi::error::Error;
use crate::{mem::table::PageEntry, KernelTableManagerImpl};
use super::{
dc_cvac, ic_iallu,
table::{PageAttributes, PageTable, L1, L2, L3},
tlb_flush_asid, tlb_flush_vaae1,
table::{PageTable, L1, L2, L3},
tlb_flush_vaae1,
};
/// AArch64 implementation of a process address space table
@@ -50,8 +49,6 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
l1[i] = PageEntry::INVALID;
}
tlb_flush_asid(asid);
Ok(Self {
l1,
asid,
@@ -71,29 +68,17 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
) -> Result<(), Error> {
self.write_l3_entry(
address,
PageEntry::normal_page(
physical,
PageAttributes::from(flags) | PageAttributes::NON_GLOBAL,
),
PageEntry::normal_page(physical, flags.into()),
false,
)
}
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> {
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
self.pop_l3_entry(address)
}
fn as_address_with_asid(&self) -> (u64, u64) {
let physical = unsafe { u64::from(self.l1.as_physical_address()) };
(physical, self.asid as u64)
fn as_address_with_asid(&self) -> u64 {
unsafe { u64::from(self.l1.as_physical_address()) | ((self.asid as u64) << 48) }
}
unsafe fn clear(&mut self) {
@@ -122,38 +107,12 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
}
l3[l3i] = entry;
dc_cvac((&raw const l3[l3i]).addr());
tlb_flush_vaae1(virt);
Ok(())
}
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> {
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
@@ -162,16 +121,12 @@ 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 entry = l3[l3i];
let page = entry.as_page().ok_or(Error::DoesNotExist)?;
let dirty = entry.is_dirty();
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
l3[l3i] = PageEntry::INVALID;
ic_iallu();
dc_cvac((&raw const l3[l3i]).addr());
tlb_flush_vaae1(virt);
Ok((page, dirty))
Ok(page)
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
+7 -103
View File
@@ -1,15 +1,12 @@
use core::{
fmt,
marker::PhantomData,
ops::{Index, IndexMut, Range},
};
use bitflags::bitflags;
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,
@@ -19,8 +16,6 @@ use yggdrasil_abi::error::Error;
use crate::KernelTableManagerImpl;
use super::dc_cvac;
bitflags! {
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PageAttributes: u64 {
@@ -42,12 +37,10 @@ bitflags! {
const SH_INNER = 3 << 8;
const PAGE_ATTR_NORMAL = 0 << 2;
const PAGE_ATTR_NORMAL_NC = 1 << 2;
const PAGE_ATTR_DEVICE = 2 << 2;
const PAGE_ATTR_DEVICE = 1 << 2;
const NON_GLOBAL = 1 << 11;
const DIRTY = 1 << 51;
const PXN = 1 << 53;
const UXN = 1 << 54;
}
@@ -69,13 +62,6 @@ pub struct L2;
#[derive(Clone, Copy)]
pub struct L3;
#[derive(Debug, Clone, Copy)]
pub enum EntryType {
Table(PhysicalAddress),
Page(PhysicalAddress),
Invalid,
}
impl NonTerminalEntryLevel for L1 {
type NextLevel = L2;
}
@@ -115,55 +101,11 @@ impl<L: EntryLevel> PageTable<L> {
Ok(table)
}
/// Creates a reference to [PageTable] from a physical address.
///
/// # Safety
///
/// The function takes in a raw physical address.
pub unsafe fn from_physical(
physical: PhysicalAddress,
) -> Option<PhysicalRefMut<'static, Self, KernelTableManagerImpl>> {
if physical.into_usize() >= KERNEL_VIRT_OFFSET {
// Looks fishy
return None;
}
if !physical.is_aligned_for::<L3>() {
return None;
}
let inner = PhysicalRefMut::map(physical);
Some(inner)
}
}
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
}
@@ -173,12 +115,6 @@ impl<L: EntryLevel> PageEntry<L> {
}
}
impl<L: NonTerminalEntryLevel> PageTable<L> {
pub fn walk(&self, index: usize) -> EntryType {
self[index].classify()
}
}
impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
type NextLevel = PageTable<L::NextLevel>;
type TableRef = PhysicalRef<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
@@ -210,7 +146,6 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
unsafe { table.as_physical_address() },
PageAttributes::empty(),
);
dc_cvac((&raw const self[index]).addr());
Ok(table)
}
}
@@ -249,7 +184,6 @@ where
}
self[index] = PageEntry::INVALID;
dc_cvac((&raw const self[index]).addr());
}
}
}
@@ -268,7 +202,7 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
| (PageAttributes::BLOCK
| PageAttributes::PRESENT
| PageAttributes::ACCESS
| PageAttributes::SH_OUTER
| PageAttributes::SH_INNER
| PageAttributes::PAGE_ATTR_NORMAL
| attrs)
.bits(),
@@ -297,21 +231,11 @@ impl<L: NonTerminalEntryLevel> PageEntry<L> {
if self.0 & PageAttributes::PRESENT.bits() != 0
&& self.0 & PageAttributes::BLOCK.bits() == 0
{
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
} else {
None
}
}
pub fn classify(self) -> EntryType {
if !self.is_present() {
EntryType::Invalid
} else if let Some(table) = self.as_table() {
EntryType::Table(table)
} else {
EntryType::Page(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
}
}
}
impl PageEntry<L3> {
@@ -336,20 +260,18 @@ impl PageEntry<L3> {
| PageAttributes::PRESENT
| PageAttributes::ACCESS
| PageAttributes::SH_OUTER
| PageAttributes::PAGE_ATTR_DEVICE)
| PageAttributes::PAGE_ATTR_DEVICE
| PageAttributes::UXN
| PageAttributes::PXN)
.bits(),
PhantomData,
)
}
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 & !Self::ATTR_MASK))
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
} else {
None
}
@@ -385,10 +307,6 @@ 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;
}
@@ -411,10 +329,6 @@ impl From<PageAttributes> for MapAttributes {
_ => unreachable!(),
};
if value.contains(PageAttributes::DIRTY) {
out |= MapAttributes::DIRTY;
}
if value.contains(PageAttributes::NON_GLOBAL) {
out |= MapAttributes::NON_GLOBAL;
}
@@ -422,13 +336,3 @@ impl From<PageAttributes> for MapAttributes {
out
}
}
impl fmt::Display for EntryType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Table(address) => write!(f, "table @ {address:#x}"),
Self::Page(address) => write!(f, "page @ {address:#x}"),
Self::Invalid => f.write_str("<invalid>"),
}
}
}
-1
View File
@@ -7,4 +7,3 @@ edition = "2021"
kernel-arch-interface.workspace = true
yggdrasil-abi.workspace = true
libk-mm-interface.workspace = true
device-api.workspace = true
+7 -37
View File
@@ -1,13 +1,11 @@
#![feature(never_type, allocator_api, slice_ptr_get)]
#![feature(never_type)]
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},
cpu::IpiQueue,
mem::{
DeviceMemoryAttributes, KernelTableManager, PhysicalMemoryAllocator, RawDeviceMemoryMapping,
},
@@ -38,21 +36,17 @@ pub struct TaskContextImpl<K: KernelTableManager, PA: PhysicalMemoryAllocator>(
static DUMMY_INTERRUPT_MASK: AtomicBool = AtomicBool::new(true);
pub struct DummyCpuData;
impl CpuData for DummyCpuData {}
impl Architecture for ArchitectureImpl {
type PerCpuData = DummyCpuData;
type PerCpuData = ();
type CpuFeatures = ();
type BreakpointType = u8;
const BREAKPOINT_VALUE: Self::BreakpointType = 0x00;
fn local_cpu() -> *mut () {
fn local_cpu() -> *mut Self::PerCpuData {
unimplemented!()
}
unsafe fn set_local_cpu(_cpu: *mut ()) {
unsafe fn set_local_cpu(_cpu: *mut Self::PerCpuData) {
unimplemented!()
}
@@ -107,14 +101,6 @@ 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 {
@@ -160,7 +146,7 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
unimplemented!()
}
unsafe fn unmap_page(&mut self, _address: usize) -> Result<(PhysicalAddress, bool), Error> {
unsafe fn unmap_page(&mut self, _address: usize) -> Result<PhysicalAddress, Error> {
unimplemented!()
}
@@ -168,7 +154,7 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
unimplemented!()
}
fn as_address_with_asid(&self) -> (u64, u64) {
fn as_address_with_asid(&self) -> u64 {
unimplemented!()
}
}
@@ -212,19 +198,3 @@ 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 _,
})
}
}
@@ -1,19 +1,16 @@
[package]
name = "kernel-arch-riscv64"
name = "kernel-arch-i686"
version = "0.1.0"
edition = "2024"
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
tock-registers.workspace = true
bitflags.workspace = true
static_assertions.workspace = true
tock-registers.workspace = true
log.workspace = true
cfg-if.workspace = true
[lints]
workspace = true
+116
View File
@@ -0,0 +1,116 @@
// 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
@@ -0,0 +1,462 @@
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
@@ -0,0 +1,108 @@
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));
}
}
+140
View File
@@ -0,0 +1,140 @@
#![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::{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,
}
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 message_interrupt_controller() -> &'static dyn MessageInterruptController {
unimplemented!()
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
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
@@ -0,0 +1,141 @@
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;
}
}
+125
View File
@@ -0,0 +1,125 @@
use fixed::FixedTables;
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
sync::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<lock: ArchitectureImpl>: 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 end = base + count as u64;
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, 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
@@ -0,0 +1,137 @@
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 {
unsafe { self.l0.as_physical_address().into_u64() }
}
}
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
@@ -0,0 +1,256 @@
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
}
}
-20
View File
@@ -30,18 +30,6 @@ pub struct IpiQueue<A: Architecture> {
data: IrqSafeSpinlock<A, Option<IpiMessage>>,
}
pub trait CpuData {
fn is_bootstrap(&self, id: u32) -> bool {
// On most architectures
id == 0
}
fn queue_index(&self, id: u32) -> usize {
// On most architectures
id as usize
}
}
pub trait CpuFeatureSet {
fn iter(&self) -> impl Iterator<Item = &'static str>;
}
@@ -62,14 +50,6 @@ impl<A: Architecture, S: Scheduler + 'static> CpuImpl<A, S> {
unsafe { A::init_ipi_queues(queues) }
}
pub fn is_bootstrap(&self) -> bool {
self.inner.is_bootstrap(self.id)
}
pub fn queue_index(&self) -> usize {
self.inner.queue_index(self.id)
}
pub fn set_current_thread_id(&mut self, id: Option<S::ThreadId>) {
self.current_thread_id = id;
}
+12 -24
View File
@@ -2,18 +2,13 @@
#![feature(step_trait, const_trait_impl, never_type, decl_macro)]
#![allow(clippy::new_without_default)]
use core::ops::Range;
use alloc::vec::Vec;
use cpu::{CpuData, CpuFeatureSet, CpuImpl, IpiQueue};
use device_api::interrupt::LocalInterruptController;
use cpu::{CpuFeatureSet, CpuImpl, IpiQueue};
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use task::Scheduler;
extern crate alloc;
#[macro_use]
pub mod macros;
pub mod cpu;
pub mod guard;
pub mod mem;
@@ -21,13 +16,14 @@ pub mod sync;
pub mod task;
pub mod util;
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", rust_analyzer))]
#[cfg(any(target_pointer_width = "32", rust_analyzer))]
pub const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
#[cfg(any(target_pointer_width = "64", rust_analyzer))]
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFFF000000000;
pub trait Architecture: Sized + 'static {
type PerCpuData: CpuData;
type PerCpuData;
type CpuFeatures: CpuFeatureSet;
type BreakpointType;
@@ -69,7 +65,11 @@ pub trait Architecture: Sized + 'static {
// Architectural devices
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
None
unimplemented!()
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
unimplemented!()
}
#[allow(unused)]
@@ -80,16 +80,4 @@ pub trait Architecture: Sized + 'static {
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
None
}
// Cache/barrier operation
fn load_barrier();
fn store_barrier();
fn memory_barrier() {
Self::store_barrier();
Self::load_barrier();
}
/// Flushes/invalidates a range of virtual memory from the CPU's data cache.
fn flush_virtual_range(range: Range<usize>);
}
-92
View File
@@ -1,92 +0,0 @@
/// Helper macro to implement "split" locks. This may be needed when a very specific storage
/// layout for the locked type is required.
// pub macro split_spinlock(
// ) {
#[macro_export]
macro_rules! split_spinlock {
(
$(use $use:path;)*
$(#[$meta:meta])*
static $name:ident: $ty:ty = $init:expr;
) => {
pub use $name::$name;
#[allow(non_snake_case)]
pub mod $name {
$(use $use;)*
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicU32, Ordering};
#[repr(transparent)]
pub struct __Wrapper {
inner: UnsafeCell<$ty>
}
$(#[$meta])*
pub static $name: __Wrapper = __Wrapper {
inner: UnsafeCell::new($init)
};
static __LOCK: AtomicU32 = AtomicU32::new(0);
pub struct __Guard($crate::guard::IrqGuard<ArchitectureImpl>);
pub struct __UnsafeGuard($crate::guard::IrqGuard<ArchitectureImpl>);
impl __Wrapper {
#[inline(never)]
pub fn lock(&self) -> __Guard {
let irq = $crate::guard::IrqGuard::acquire();
while __LOCK.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed).is_err() {
core::hint::spin_loop();
}
__Guard(irq)
}
#[inline(never)]
pub unsafe fn grab(&self) -> __UnsafeGuard {
let irq = $crate::guard::IrqGuard::acquire();
__UnsafeGuard(irq)
}
}
unsafe impl Sync for __Wrapper {}
impl core::ops::Deref for __Guard {
type Target = $ty;
fn deref(&self) -> &Self::Target {
unsafe { &*$name.inner.get() }
}
}
impl core::ops::DerefMut for __Guard {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *$name.inner.get() }
}
}
impl core::ops::Deref for __UnsafeGuard {
type Target = $ty;
fn deref(&self) -> &Self::Target {
unsafe { &*$name.inner.get() }
}
}
impl core::ops::DerefMut for __UnsafeGuard {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *$name.inner.get() }
}
}
impl Drop for __Guard {
fn drop(&mut self) {
__LOCK.store(0, Ordering::Release)
}
}
}
};
}
+5 -7
View File
@@ -2,6 +2,9 @@ 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;
@@ -32,8 +35,6 @@ pub struct DeviceMemoryAttributes {
/// Describes a single device memory mapping
#[derive(Debug)]
pub struct RawDeviceMemoryMapping<A: KernelTableManager> {
/// Physical base address of the object
pub physical_base: u64,
/// Virtual address of the mapped object
pub address: usize,
/// Base address of the mapping start
@@ -97,8 +98,7 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
address
}
pub fn into_raw_parts(self) -> (u64, usize, usize, usize, usize) {
let physical_base = self.physical_base;
pub fn into_raw_parts(self) -> (usize, usize, usize, usize) {
let address = self.address;
let base_address = self.base_address;
let page_count = self.page_count;
@@ -106,7 +106,7 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
core::mem::forget(self);
(physical_base, address, base_address, page_count, page_size)
(address, base_address, page_count, page_size)
}
/// # Safety
@@ -114,14 +114,12 @@ impl<A: KernelTableManager> RawDeviceMemoryMapping<A> {
/// Preconditions: all the fields must come from a [RawDeviceMemoryMapping::into_raw_parts]
/// call.
pub unsafe fn from_raw_parts(
physical_base: u64,
address: usize,
base_address: usize,
page_count: usize,
page_size: usize,
) -> Self {
Self {
physical_base,
address,
base_address,
page_count,
+63 -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();
@@ -154,6 +154,67 @@ impl<A: Architecture, T> DerefMut for IrqSafeSpinlockGuard<'_, A, T> {
}
}
/// Helper macro to implement "split" locks. This may be needed when a very specific storage
/// layout for the locked type is required.
pub macro split_spinlock(
$(use $use:path;)*
$(#[$meta:meta])*
static $name:ident<$lock:ident: $arch:ty>: $ty:ty = $init:expr;
) {
pub use $name::$name;
#[allow(non_snake_case)]
pub mod $name {
$(use $use;)*
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
#[repr(transparent)]
pub struct __Wrapper(UnsafeCell<$ty>);
$(#[$meta])*
pub static $name: __Wrapper = __Wrapper(UnsafeCell::new($init));
static __LOCK: AtomicBool = AtomicBool::new(false);
pub struct __Guard($crate::guard::IrqGuard<$arch>);
impl __Wrapper {
pub fn $lock(&self) -> __Guard {
let irq = $crate::guard::IrqGuard::acquire();
while __LOCK.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() {
core::hint::spin_loop();
}
__Guard(irq)
}
}
unsafe impl Sync for __Wrapper {}
impl core::ops::Deref for __Guard {
type Target = $ty;
fn deref(&self) -> &Self::Target {
unsafe { &*$name.0.get() }
}
}
impl core::ops::DerefMut for __Guard {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *$name.0.get() }
}
}
impl Drop for __Guard {
fn drop(&mut self) {
__LOCK.store(false, Ordering::Release)
}
}
}
}
static LOCK_HACK: AtomicBool = AtomicBool::new(false);
/// "Hacks" all the locks in the kernel to make them function as "NULL"-locks instead of spinlocks.
-1
View File
@@ -83,7 +83,6 @@ pub struct UserContextInfo {
pub stack_pointer: usize,
pub thread_pointer: usize,
pub address_space: u64,
pub asid: u64,
pub single_step: bool,
}
+36 -86
View File
@@ -45,80 +45,31 @@ impl<T> OneTimeInit<T> {
}
pub fn try_init_with_opt<F: FnOnce() -> Result<T, Error>>(&self, f: F) -> Result<&T, Error> {
if !self.try_begin_init() {
if self
.state
.compare_exchange(
Self::STATE_UNINITIALIZED,
Self::STATE_INITIALIZING,
Ordering::Release,
Ordering::Relaxed,
)
.is_err()
{
// Already initialized
return Err(Error::AlreadyExists);
}
match f() {
Ok(value) => {
let value = unsafe { (*self.value.get()).write(value) };
self.finish_init();
Ok(value)
let value = match f() {
Ok(val) => val,
Err(err) => {
self.state
.store(Self::STATE_UNINITIALIZED, Ordering::Release);
return Err(err);
}
Err(error) => {
self.fail_init();
Err(error)
}
}
}
};
pub fn or_init_with<F: FnOnce() -> T>(&self, f: F) -> &T {
if !self.try_begin_init() {
return self.wait_for_init();
}
let value = unsafe { (*self.value.get()).write(f()) };
self.finish_init();
value
}
let value = unsafe { (*self.value.get()).write(value) };
pub fn or_init_with_opt<F: FnOnce() -> Option<T>>(&self, f: F) -> Option<&T> {
if !self.try_begin_init() {
return Some(self.wait_for_init());
}
match f() {
Some(value) => {
let value = unsafe { (*self.value.get()).write(value) };
self.finish_init();
Some(value)
}
None => {
self.fail_init();
None
}
}
}
pub fn or_try_init_with<F: FnOnce() -> Result<T, Error>>(&self, f: F) -> Result<&T, Error> {
if !self.try_begin_init() {
return Ok(self.wait_for_init());
}
match f() {
Ok(value) => {
let value = unsafe { (*self.value.get()).write(value) };
self.finish_init();
Ok(value)
}
Err(error) => {
// Init failed
self.fail_init();
Err(error)
}
}
}
fn try_begin_init(&self) -> bool {
self.state
.compare_exchange(
Self::STATE_UNINITIALIZED,
Self::STATE_INITIALIZING,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
}
fn finish_init(&self) {
self.state
.compare_exchange(
Self::STATE_INITIALIZING,
@@ -127,38 +78,37 @@ impl<T> OneTimeInit<T> {
Ordering::Relaxed,
)
.unwrap();
Ok(value)
}
fn fail_init(&self) {
self.state
pub fn try_init_with<F: FnOnce() -> T>(&self, f: F) -> Option<&T> {
if self
.state
.compare_exchange(
Self::STATE_INITIALIZING,
Self::STATE_UNINITIALIZED,
Self::STATE_INITIALIZING,
Ordering::Release,
Ordering::Relaxed,
)
.unwrap();
}
fn wait_for_init(&self) -> &T {
while self.state.load(Ordering::Acquire) != Self::STATE_INITIALIZED {
core::hint::spin_loop();
}
unsafe { (*self.value.get()).assume_init_ref() }
}
pub fn try_init_with<F: FnOnce() -> T>(&self, f: F) -> Result<&T, Error> {
if !self.try_begin_init() {
.is_err()
{
// Already initialized
return Err(Error::AlreadyExists);
return None;
}
let value = unsafe { (*self.value.get()).write(f()) };
self.finish_init();
self.state
.compare_exchange(
Self::STATE_INITIALIZING,
Self::STATE_INITIALIZED,
Ordering::Release,
Ordering::Relaxed,
)
.unwrap();
Ok(value)
Some(value)
}
/// Sets the underlying value of the [OneTimeInit]. If already initialized, panics.
-128
View File
@@ -1,128 +0,0 @@
// vi:ft=asm:
.section .text
.macro SAVE_TASK_STATE
addi sp, sp, -{context_size}
sd ra, 0 * 8(sp)
sd gp, 1 * 8(sp)
sd s11, 2 * 8(sp)
sd s10, 3 * 8(sp)
sd s9, 4 * 8(sp)
sd s8, 5 * 8(sp)
sd s7, 6 * 8(sp)
sd s6, 7 * 8(sp)
sd s5, 8 * 8(sp)
sd s4, 9 * 8(sp)
sd s3, 10 * 8(sp)
sd s2, 11 * 8(sp)
sd s1, 12 * 8(sp)
sd s0, 13 * 8(sp)
.endm
.macro LOAD_TASK_STATE
ld ra, 0 * 8(sp)
ld gp, 1 * 8(sp)
ld s11, 2 * 8(sp)
ld s10, 3 * 8(sp)
ld s9, 4 * 8(sp)
ld s8, 5 * 8(sp)
ld s7, 6 * 8(sp)
ld s6, 7 * 8(sp)
ld s5, 8 * 8(sp)
ld s4, 9 * 8(sp)
ld s3, 10 * 8(sp)
ld s2, 11 * 8(sp)
ld s1, 12 * 8(sp)
ld s0, 13 * 8(sp)
addi sp, sp, {context_size}
.endm
.option push
.option norvc
.global __rv64_task_enter_kernel
.global __rv64_task_enter_user
.global __rv64_switch_task
.global __rv64_switch_task_and_drop
.global __rv64_enter_task
// Context switching
.type __rv64_enter_task, @function
__rv64_enter_task:
// a0 - task ctx
ld sp, (a0)
LOAD_TASK_STATE
ret
.size __rv64_enter_task, . - __rv64_enter_task
.type __rv64_switch_task, @function
__rv64_switch_task:
// a0 - destination task ctx
// a1 - source task ctx
SAVE_TASK_STATE
sd sp, (a1)
ld sp, (a0)
LOAD_TASK_STATE
ret
.size __rv64_switch_task, . - __rv64_switch_task
.type __rv64_switch_task_and_drop, @function
__rv64_switch_task_and_drop:
// a0 - destination task ctx
// a1 - thread struct to drop
ld sp, (a0)
mv a0, a1
call __arch_drop_thread
LOAD_TASK_STATE
ret
.size __rv64_switch_task_and_drop, . - __rv64_switch_task_and_drop
// Entry functions
.type __rv64_task_enter_kernel, @function
__rv64_task_enter_kernel:
ld a0, (sp) // argument
ld ra, 8(sp) // entry
addi sp, sp, 16
// Set SPIE to enable interrupts
// Set SPP = 1 to indicate a return to S-mode
csrr t0, sstatus
ori t0, t0, (1 << 5)
ori t0, t0, (1 << 8)
csrw sstatus, t0
csrw sepc, ra
csrw sscratch, zero
sret
.size __rv64_task_enter_kernel, . - __rv64_task_enter_kernel
.type __rv64_task_enter_user, @function
__rv64_task_enter_user:
csrw sscratch, tp
ld a0, 0 * 8(sp) // argument
ld ra, 1 * 8(sp) // entry
ld tp, 2 * 8(sp) // thread pointer
ld sp, 3 * 8(sp) // user stack
// Set SPIE to enable interrupts
// Set SPP = 0 to indicate a return to U-mode
li t1, (1 << 8)
not t1, t1
csrr t0, sstatus
ori t0, t0, (1 << 5)
and t0, t0, t1
csrw sstatus, t0
csrw sepc, ra
sret
.size __rv64_task_enter_user, . - __rv64_task_enter_user
.option pop
-221
View File
@@ -1,221 +0,0 @@
use core::{arch::global_asm, cell::UnsafeCell, marker::PhantomData};
use kernel_arch_interface::{
mem::{KernelTableManager, PhysicalMemoryAllocator},
task::{StackBuilder, TaskContext, UserContextInfo},
Architecture,
};
use libk_mm_interface::address::PhysicalAddress;
use tock_registers::{
interfaces::{Readable, Writeable},
registers::InMemoryRegister,
};
use yggdrasil_abi::error::Error;
use crate::{
mem::{self},
registers::SATP,
ArchitectureImpl, PerCpuData,
};
pub const CONTEXT_SIZE: usize = 14 * size_of::<usize>();
#[repr(C, align(0x10))]
struct TaskContextInner {
// 0x00
sp: usize,
satp: InMemoryRegister<u64, SATP::Register>,
}
pub struct TaskContextImpl<
K: KernelTableManager,
PA: PhysicalMemoryAllocator<Address = PhysicalAddress>,
> {
inner: UnsafeCell<TaskContextInner>,
// fp_context: UnsafeCell<FpContext>,
stack_base_phys: PhysicalAddress,
stack_top: usize,
stack_size: usize,
_pd: PhantomData<(K, PA)>,
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContextImpl<K, PA>
{
unsafe fn load_state(&self) {
// TODO load new SATP value
let inner = unsafe { &*self.inner.get() };
let cpu = unsafe { &mut *ArchitectureImpl::local_cpu().cast::<PerCpuData>() };
// Copy new SATP
let satp = inner.satp.get();
let asid = inner.satp.read(SATP::ASID);
if satp != SATP.get() {
mem::tlb_flush_asid(asid as usize);
SATP.set(satp);
}
cpu.smode_sp = self.stack_top;
}
unsafe fn store_state(&self) {}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>>
TaskContext<K, PA> for TaskContextImpl<K, PA>
{
const USER_STACK_EXTRA_ALIGN: usize = 8;
const SIGNAL_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);
log::debug!(
"Set up user task: pc={:#x}, sp={:#x}, tp={:#x}",
context.entry,
context.stack_pointer,
context.thread_pointer
);
stack.push(context.stack_pointer);
stack.push(context.thread_pointer);
stack.push(context.entry);
stack.push(context.argument);
setup_common_context(&mut stack, __rv64_task_enter_user as _);
let sp = stack.build();
let satp = InMemoryRegister::new(0);
satp.write(
SATP::MODE::Sv39
+ SATP::ASID.val(context.asid)
+ SATP::PPN.val(context.address_space >> 12),
);
Ok(Self {
inner: UnsafeCell::new(TaskContextInner { sp, satp }),
// fp_context: UnsafeCell::new(FpContext::new()),
stack_base_phys,
stack_top: stack_base + USER_TASK_PAGES * 0x1000,
stack_size: USER_TASK_PAGES * 0x1000,
_pd: PhantomData,
})
}
fn kernel(entry: extern "C" fn(usize) -> !, arg: usize) -> Result<Self, Error> {
const KERNEL_TASK_PAGES: usize = 8;
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(entry as _);
stack.push(arg);
setup_common_context(&mut stack, __rv64_task_enter_kernel as _);
let sp = stack.build();
// TODO stack is leaked
let satp = InMemoryRegister::new(0);
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 {
inner: UnsafeCell::new(TaskContextInner { sp, satp }),
// fp_context: UnsafeCell::new(FpContext::new()),
stack_base_phys,
stack_top: 0,
stack_size: KERNEL_TASK_PAGES * 0x1000,
_pd: PhantomData,
})
}
fn set_thread_pointer(&self, tp: usize) {
let _ = tp;
todo!()
}
fn align_stack_for_entry(sp: usize) -> usize {
sp
}
unsafe fn enter(&self) -> ! {
unsafe {
self.load_state();
__rv64_enter_task(self.inner.get())
}
}
unsafe fn switch(&self, from: &Self) {
if core::ptr::addr_eq(self, from) {
return;
}
unsafe {
from.store_state();
self.load_state();
__rv64_switch_task(self.inner.get(), from.inner.get())
}
}
unsafe fn switch_and_drop(&self, thread: *const ()) {
unsafe {
self.load_state();
__rv64_switch_task_and_drop(self.inner.get(), thread)
}
}
}
impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddress>> Drop
for TaskContextImpl<K, PA>
{
fn drop(&mut self) {
assert_eq!(self.stack_size % 0x1000, 0);
for offset in (0..self.stack_size).step_by(0x1000) {
unsafe {
PA::free_page(self.stack_base_phys.add(offset));
}
}
}
}
fn setup_common_context(builder: &mut StackBuilder, entry: usize) {
builder.push(0); // x8/s0/fp
builder.push(0); // x9/s1
builder.push(0); // x18/s2
builder.push(0); // x19/s3
builder.push(0); // x20/s4
builder.push(0); // x21/s5
builder.push(0); // x22/s6
builder.push(0); // x23/s7
builder.push(0); // x24/s8
builder.push(0); // x25/s9
builder.push(0); // x26/s10
builder.push(0); // x27/s11
builder.push(0); // x4/gp
builder.push(entry); // x1/ra return address
}
unsafe extern "C" {
fn __rv64_enter_task(to: *mut TaskContextInner) -> !;
fn __rv64_switch_task(to: *mut TaskContextInner, from: *mut TaskContextInner);
fn __rv64_switch_task_and_drop(to: *mut TaskContextInner, thread: *const ()) -> !;
fn __rv64_task_enter_kernel();
fn __rv64_task_enter_user();
// fn __rv64_fp_store_context(to: *mut c_void);
// fn __rv64_fp_restore_context(from: *const c_void);
}
global_asm!(
include_str!("context.S"),
context_size = const CONTEXT_SIZE,
);
-6
View File
@@ -1,6 +0,0 @@
#[inline]
pub fn rdtime() -> u64 {
let mut output: u64;
unsafe { core::arch::asm!("rdtime {0}", out(reg) output) };
output
}
-190
View File
@@ -1,190 +0,0 @@
#![feature(decl_macro)]
#![no_std]
extern crate alloc;
use core::{
ops::Range,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, vec::Vec};
use device_api::interrupt::LocalInterruptController;
use kernel_arch_interface::{
cpu::{CpuData, CpuImpl, IpiQueue},
sync::IrqSafeSpinlock,
task::Scheduler,
util::OneTimeInit,
Architecture,
};
use tock_registers::interfaces::{ReadWriteable, Readable};
use registers::SSTATUS;
pub mod mem;
pub use mem::{process::ProcessAddressSpaceImpl, KernelTableManagerImpl};
pub mod context;
pub use context::TaskContextImpl;
pub mod intrinsics;
pub mod registers;
pub mod sbi;
pub struct ArchitectureImpl;
#[repr(C)]
pub struct PerCpuData {
// Used in assembly
pub tmp_t0: usize, // 0x00
pub umode_sp: usize, // 0x08
pub smode_sp: usize, // 0x10
// Used elsewhere
pub bootstrap: bool,
pub queue_index: usize,
}
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());
impl CpuData for PerCpuData {
fn is_bootstrap(&self, id: u32) -> bool {
let _ = id;
self.bootstrap
}
fn queue_index(&self, id: u32) -> usize {
let _ = id;
self.queue_index
}
}
/// 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) -> ! {
core::arch::naked_asm!("1: nop; j 1b");
}
impl ArchitectureImpl {
pub fn for_each_hart<F: FnMut(u32, usize, &IpiQueue<ArchitectureImpl>)>(mut f: F) {
let map = HART_TO_QUEUE.lock();
map.iter().for_each(|(&hart_id, &queue_index)| {
let queue = &IPI_QUEUES.get()[queue_index];
f(hart_id, queue_index, queue);
});
}
}
impl Architecture for ArchitectureImpl {
type PerCpuData = PerCpuData;
type CpuFeatures = ();
type BreakpointType = u32;
const BREAKPOINT_VALUE: Self::BreakpointType = 0;
fn halt() -> ! {
loop {
unsafe { Self::set_interrupt_mask(true) };
Self::wait_for_interrupt();
}
}
unsafe fn set_local_cpu(cpu: *mut ()) {
unsafe { core::arch::asm!("mv tp, {0}", in(reg) cpu) };
}
#[inline]
fn local_cpu() -> *mut () {
let value: u64;
unsafe { core::arch::asm!("mv {0}, tp", out(reg) value) };
value as _
}
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
let id = id.expect("riscv64 requires an explicit HART ID in its per-processor struct");
let queue_index = data.queue_index;
HART_TO_QUEUE.lock().insert(id, queue_index);
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(id, data)));
unsafe { cpu.set_local() };
}
unsafe fn init_ipi_queues(queues: Vec<IpiQueue<Self>>) {
IPI_QUEUES.init(queues);
}
fn ipi_queue(cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
let queue_index = *HART_TO_QUEUE.lock().get(&cpu_id)?;
IPI_QUEUES.try_get().and_then(|q| q.get(queue_index))
}
#[inline]
unsafe fn set_interrupt_mask(mask: bool) -> bool {
let old = Self::interrupt_mask();
if mask {
SSTATUS.modify(SSTATUS::SIE::CLEAR);
} else {
SSTATUS.modify(SSTATUS::SIE::SET);
}
old
}
#[inline]
fn interrupt_mask() -> bool {
SSTATUS.matches_all(SSTATUS::SIE::CLEAR)
}
fn wait_for_interrupt() {
unsafe {
core::arch::asm!("wfi");
}
}
fn cpu_count() -> usize {
CPU_COUNT.load(Ordering::Acquire)
}
fn cpu_index<S: Scheduler + 'static>() -> u32 {
CpuImpl::<Self, S>::local().id()
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
let _ = cpu;
todo!()
}
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
let _ = cpu;
todo!()
}
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
todo!()
}
fn idle_task() -> extern "C" fn(usize) -> ! {
idle_task
}
// Cache/barrier operation
fn load_barrier() {
unsafe { core::arch::asm!("fence r, w") };
}
fn store_barrier() {
unsafe { core::arch::asm!("fence w, r") };
}
fn memory_barrier() {
unsafe { core::arch::asm!("fence rw, rw") };
}
fn flush_virtual_range(_range: Range<usize>) {
// TODO
}
}
-32
View File
@@ -1,32 +0,0 @@
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
@@ -1,51 +0,0 @@
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) };
}
-105
View File
@@ -1,105 +0,0 @@
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
};
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::error::Error;
use crate::{
mem::table::{PageTable, L1, L3},
registers::SATP,
};
pub use intrinsics::*;
pub mod fixed;
pub mod intrinsics;
pub mod process;
pub mod table;
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
/// Any VAs above this one are sign-extended
pub const USER_BOUNDARY: usize = 0x40_00000000;
#[derive(Debug)]
pub struct KernelTableManagerImpl;
impl KernelTableManager for KernelTableManagerImpl {
fn virtualize(address: u64) -> usize {
let address = address as usize;
if address < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
} else {
panic!("Invalid physical address: {address:#x}")
}
}
fn physicalize(address: usize) -> u64 {
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
{
panic!("Invalid virtualized address: {address:#x}");
}
(address - KERNEL_VIRT_OFFSET) as u64
}
unsafe fn map_device_pages(
base: u64,
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
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>) {
let _ = mapping;
}
}
pub fn auto_lower_address<T>(x: *const T) -> usize {
let x = x.addr();
if x >= KERNEL_VIRT_OFFSET {
x - KERNEL_VIRT_OFFSET
} else {
x
}
}
/// Enables the memory translation.
///
/// # Safety
///
/// Only meant to be called once per each HART during their early init.
pub unsafe fn enable_mmu() {
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);
}
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
let _lock = fixed::LOCK.lock();
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
dst[l1i] = unsafe { fixed::KERNEL_L1[l1i] };
}
}
-238
View File
@@ -1,238 +0,0 @@
use core::{
marker::PhantomData,
sync::atomic::{AtomicU16, Ordering},
};
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
};
use yggdrasil_abi::error::Error;
use crate::mem::{
clone_kernel_tables,
table::{PageAttributes, PageEntry},
};
use super::{
table::{DroppableRange, PageTable, L1, L2, L3},
KernelTableManagerImpl, USER_BOUNDARY,
};
pub struct ProcessAddressSpaceImpl<TA: TableAllocator> {
l1: PhysicalRefMut<'static, PageTable<L1>, KernelTableManagerImpl>,
asid: u16,
_pd: PhantomData<TA>,
}
impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceImpl<TA> {
const LOWER_LIMIT_PFN: usize = 8;
const UPPER_LIMIT_PFN: usize = (16 << 30) / L3::SIZE;
fn new() -> Result<Self, Error> {
static LAST_ASID: AtomicU16 = AtomicU16::new(1);
let mut l1 = unsafe {
PhysicalRefMut::<'static, PageTable<L1>, KernelTableManagerImpl>::map(
TA::allocate_page_table()?,
)
};
for i in 0..512 {
l1[i] = PageEntry::INVALID;
}
// Copy the kernel mappings
clone_kernel_tables(&mut l1);
let asid = LAST_ASID.fetch_add(1, Ordering::AcqRel);
Ok(Self {
l1,
asid,
_pd: PhantomData,
})
}
unsafe fn map_page(
&mut self,
address: usize,
physical: PhysicalAddress,
flags: MapAttributes,
) -> Result<(), Error> {
self.write_l3_entry(
address,
PageEntry::page(physical, to_page_attributes(flags)),
false,
)
.unwrap();
Ok(())
}
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)
}
fn translate(&self, address: usize) -> Result<(PhysicalAddress, MapAttributes), Error> {
self.read_l3_entry(address).ok_or(Error::DoesNotExist)
}
fn as_address_with_asid(&self) -> (u64, u64) {
let physical = unsafe { self.l1.as_physical_address() }.into_u64();
(physical, self.asid as u64)
}
unsafe fn clear(&mut self) {
unsafe { self.l1.drop_range::<TA>(L1::DROPPABLE_RANGE) };
}
}
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> {
if virt >= USER_BOUNDARY {
log::warn!("Tried to map a userspace page to a non-userspace virtual region");
return Err(Error::InvalidArgument);
}
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_or_alloc::<TA>(l1i)?;
let mut l3 = l2.get_mut_or_alloc::<TA>(l2i)?;
if l3[l3i].is_present() && !overwrite {
todo!();
}
l3[l3i] = entry;
super::tlb_flush_va_asid(virt, self.asid as usize);
Ok(())
}
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>();
// 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 = &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, dirty))
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
if virt >= USER_BOUNDARY {
log::warn!("Tried read an userspace page to a non-userspace virtual region");
return None;
}
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
let l3i = virt.page_index::<L3>();
let l2 = self.l1.get(l1i)?;
let l3 = l2.get(l2i)?;
let page = l3[l3i].as_page()?;
Some((
page.add(virt & 0xFFF),
to_map_attributes(l3[l3i].attributes()),
))
}
}
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 l1_phys = self.l1.as_physical_address();
TA::free_page_table(l1_phys);
super::tlb_flush_asid(self.asid as usize);
}
}
}
fn to_page_attributes(src: MapAttributes) -> PageAttributes {
let mut result = PageAttributes::R | PageAttributes::X;
if src.contains(MapAttributes::USER_WRITE) {
result |= PageAttributes::W;
}
if src.intersects(MapAttributes::USER_READ | MapAttributes::USER_WRITE) {
result |= PageAttributes::U;
}
if src.contains(MapAttributes::DIRTY) {
result |= PageAttributes::SW_DIRTY;
}
result
}
fn to_map_attributes(src: PageAttributes) -> MapAttributes {
let mut result = MapAttributes::NON_GLOBAL;
if src.contains(PageAttributes::U) {
result |= MapAttributes::USER_READ;
if src.contains(PageAttributes::W) {
result |= MapAttributes::USER_WRITE;
}
}
if src.contains(PageAttributes::SW_DIRTY) {
result |= MapAttributes::DIRTY;
}
result
}
-343
View File
@@ -1,343 +0,0 @@
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,
},
};
use yggdrasil_abi::error::Error;
use super::{KernelTableManagerImpl, USER_BOUNDARY};
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)]
pub struct L3;
/// L2 - entry is 2MiB
#[derive(Debug, Clone, Copy)]
pub struct L2;
/// L1 - entry is 1GiB
#[derive(Debug, Clone, Copy)]
pub struct L1;
impl EntryLevel for L3 {
const SHIFT: usize = 12;
}
impl EntryLevel for L2 {
const SHIFT: usize = 21;
}
impl EntryLevel for L1 {
const SHIFT: usize = 30;
}
#[repr(C, align(0x1000))]
pub struct PageTable<L: EntryLevel> {
pub(crate) entries: [PageEntry<L>; 512],
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PageEntry<L: EntryLevel>(pub u64, PhantomData<L>);
pub(super) trait DroppableRange {
const DROPPABLE_RANGE: Range<usize>;
}
impl DroppableRange for L1 {
const DROPPABLE_RANGE: Range<usize> = 0..page_index::<L1>(USER_BOUNDARY);
}
impl DroppableRange for L2 {
const DROPPABLE_RANGE: Range<usize> = 0..512;
}
impl NonTerminalEntryLevel for L1 {
type NextLevel = L2;
}
impl NonTerminalEntryLevel for L2 {
type NextLevel = L3;
}
impl<L: EntryLevel> PageTable<L> {
pub const fn zeroed() -> Self {
Self {
entries: [PageEntry::INVALID; 512],
}
}
pub fn new_zeroed<'a, TA: TableAllocator>(
) -> Result<PhysicalRefMut<'a, PageTable<L>, KernelTableManagerImpl>, Error> {
let physical = TA::allocate_page_table()?;
let mut table =
unsafe { PhysicalRefMut::<'a, Self, KernelTableManagerImpl>::map(physical) };
for i in 0..512 {
table[i] = PageEntry::INVALID;
}
Ok(table)
}
}
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.
///
/// # Safety
///
/// The caller must ensure `value` is actually a "valid" PTE.
pub const unsafe fn from_raw(value: u64) -> Self {
Self(value, PhantomData)
}
pub const fn is_present(&self) -> bool {
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)
}
}
impl<L: NonTerminalEntryLevel + DroppableRange> EntryLevelDrop for PageTable<L>
where
PageTable<L::NextLevel>: EntryLevelDrop,
{
const FULL_RANGE: Range<usize> = L::DROPPABLE_RANGE;
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() {
unsafe {
let mut table_ref: PhysicalRefMut<
PageTable<L::NextLevel>,
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;
// dc_cvac((&raw const self[index]).addr());
}
}
}
impl EntryLevelDrop for PageTable<L3> {
const FULL_RANGE: Range<usize> = 0..512;
// Do nothing
unsafe fn drop_range<TA: TableAllocator>(&mut self, _range: Range<usize>) {}
}
impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
type NextLevel = PageTable<L::NextLevel>;
type TableRef = PhysicalRef<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
type TableRefMut = PhysicalRefMut<'static, PageTable<L::NextLevel>, KernelTableManagerImpl>;
fn get(&self, index: usize) -> Option<Self::TableRef> {
let table = self[index].as_table()?;
Some(unsafe { PhysicalRef::map(table) })
}
fn get_mut(&mut self, index: usize) -> Option<Self::TableRefMut> {
let table = self[index].as_table()?;
Some(unsafe { PhysicalRefMut::map(table) })
}
fn get_mut_or_alloc<TA: TableAllocator>(
&mut self,
index: usize,
) -> Result<Self::TableRefMut, Error> {
if let Some(table) = self[index].as_table() {
Ok(unsafe { PhysicalRefMut::map(table) })
} else {
let table = PageTable::new_zeroed::<TA>()?;
self[index] = PageEntry::<L>::table(
unsafe { table.as_physical_address() },
PageAttributes::empty(),
);
// dc_cvac((&raw const self[index]).addr());
Ok(table)
}
}
}
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(
(address.into_u64() >> 2)
| (PageAttributes::R
| PageAttributes::A
| PageAttributes::D
| PageAttributes::V
| attrs)
.bits(),
PhantomData,
)
}
pub fn table(address: PhysicalAddress, mut attrs: PageAttributes) -> Self {
attrs.remove(PageAttributes::R | PageAttributes::W | PageAttributes::X);
Self(
(address.into_u64() >> 2) | (PageAttributes::V | attrs).bits(),
PhantomData,
)
}
pub fn as_table(&self) -> Option<PhysicalAddress> {
(self.0
& (PageAttributes::R | PageAttributes::W | PageAttributes::X | PageAttributes::V)
.bits()
== PageAttributes::V.bits())
.then_some((self.0 & !Self::ATTR_MASK) << 2)
.map(PhysicalAddress::from_u64)
}
}
impl PageEntry<L3> {
pub fn page(address: PhysicalAddress, attrs: PageAttributes) -> Self {
Self(
(address.into_u64() >> 2)
| (PageAttributes::R
| PageAttributes::A
| PageAttributes::D
| PageAttributes::V
| attrs)
.bits(),
PhantomData,
)
}
pub fn as_page(&self) -> Option<PhysicalAddress> {
(self.0 & PageAttributes::V.bits() != 0)
.then_some((self.0 & !Self::ATTR_MASK) << 2)
.map(PhysicalAddress::from_u64)
}
}
impl<L: EntryLevel> Index<usize> for PageTable<L> {
type Output = PageEntry<L>;
fn index(&self, index: usize) -> &Self::Output {
&self.entries[index]
}
}
impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&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(())
}
}
-221
View File
@@ -1,221 +0,0 @@
macro impl_csr_read($struct:ident, $repr:ty, $reg:ident, $register:ty) {
impl tock_registers::interfaces::Readable for $struct {
type T = $repr;
type R = $register;
#[inline]
fn get(&self) -> $repr {
let mut value: $repr;
unsafe {
core::arch::asm!(concat!("csrr {0}, ", stringify!($reg)), out(reg) value);
}
value
}
}
}
macro impl_csr_write($struct:ident, $repr:ty, $reg:ident, $register:ty) {
impl tock_registers::interfaces::Writeable for $struct {
type T = $repr;
type R = $register;
#[inline]
fn set(&self, value: $repr) {
unsafe {
core::arch::asm!(concat!("csrw ", stringify!($reg), ", {0}"), in(reg) value);
}
}
}
}
pub mod satp {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SATP [
PPN OFFSET(0) NUMBITS(44) [],
ASID OFFSET(44) NUMBITS(16) [],
MODE OFFSET(60) NUMBITS(4) [
Bare = 0,
Sv39 = 8,
Sv48 = 9,
Sv57 = 10,
Sv64 = 11,
],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, satp, SATP::Register);
impl_csr_write!(Reg, u64, satp, SATP::Register);
pub const SATP: Reg = Reg;
}
pub mod stvec {
use tock_registers::{interfaces::ReadWriteable, register_bitfields};
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub STVEC [
MODE OFFSET(0) NUMBITS(2) [
Direct = 0,
Vectored = 1
],
BASE OFFSET(2) NUMBITS(62) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, stvec, STVEC::Register);
impl_csr_write!(Reg, u64, stvec, STVEC::Register);
impl Reg {
pub fn set_base(&self, base: usize) {
debug_assert_eq!(base & 0xF, 0);
let mask = match base & 63 != 0 {
false => 0,
true => 0x3 << 62,
};
self.modify(STVEC::BASE.val(((base as u64) >> 2) | mask));
}
}
pub const STVEC: Reg = Reg;
}
pub mod scause {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SCAUSE [
CODE OFFSET(0) NUMBITS(63) [],
INTERRUPT OFFSET(63) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, scause, SCAUSE::Register);
impl_csr_write!(Reg, u64, scause, SCAUSE::Register);
pub const SCAUSE: Reg = Reg;
}
pub mod stval {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, stval, ());
impl_csr_write!(Reg, u64, stval, ());
pub const STVAL: Reg = Reg;
}
pub mod sepc {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, sepc, ());
impl_csr_write!(Reg, u64, sepc, ());
pub const SEPC: Reg = Reg;
}
pub mod sstatus {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SSTATUS [
SUM OFFSET(18) NUMBITS(1) [],
SPP OFFSET(8) NUMBITS(1) [],
SIE OFFSET(1) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sstatus, SSTATUS::Register);
impl_csr_write!(Reg, u64, sstatus, SSTATUS::Register);
pub const SSTATUS: Reg = Reg;
}
pub mod sscratch {
use super::{impl_csr_read, impl_csr_write};
pub struct Reg;
impl_csr_read!(Reg, u64, sscratch, ());
impl_csr_write!(Reg, u64, sscratch, ());
pub const SSCRATCH: Reg = Reg;
}
pub mod sip {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SIP [
SSIP OFFSET(1) NUMBITS(1) [],
STIP OFFSET(5) NUMBITS(1) [],
SEIP OFFSET(9) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sip, SIP::Register);
impl_csr_write!(Reg, u64, sip, SIP::Register);
pub const SIP: Reg = Reg;
}
pub mod sie {
use tock_registers::register_bitfields;
use super::{impl_csr_read, impl_csr_write};
register_bitfields!(
u64,
pub SIE [
SSIE OFFSET(1) NUMBITS(1) [],
STIE OFFSET(5) NUMBITS(1) [],
SEIE OFFSET(9) NUMBITS(1) [],
]
);
pub struct Reg;
impl_csr_read!(Reg, u64, sie, SIE::Register);
impl_csr_write!(Reg, u64, sie, SIE::Register);
pub const SIE: Reg = Reg;
}
pub use satp::SATP;
pub use scause::SCAUSE;
pub use sepc::SEPC;
pub use sie::SIE;
pub use sip::SIP;
pub use sscratch::SSCRATCH;
pub use sstatus::SSTATUS;
pub use stval::STVAL;
pub use stvec::STVEC;
-110
View File
@@ -1,110 +0,0 @@
use yggdrasil_abi::{error::Error, primitive_enum};
const EXT_HSM: u64 = 0x48534D;
const EXT_TIME: u64 = 0x54494D45;
const EXT_DBCN: u64 = 0x4442434E;
const EXT_SPI: u64 = 0x735049;
primitive_enum! {
pub enum Status: i64 {
Failed = -1,
NotSupported = -2,
InvalidParam = -3,
Denied = -4,
InvalidAddress = -5,
AlreadyAvailable = -6,
AlreadyStarted = -7,
AlreadyStopped = -8,
NoShmem = -9,
InvalidState = -10,
BadRange = -11,
Timeout = -12,
Io = -13,
}
}
primitive_enum! {
pub enum HartState: u64 {
Started = 0,
Stopped = 1,
StartPending = 2,
StopPending = 3,
Suspended = 4,
SuspendPending = 5,
ResumePending = 6,
}
}
pub enum SbiError {
Status(Status),
Other(i64),
}
impl From<i64> for SbiError {
#[inline]
fn from(value: i64) -> Self {
match Status::try_from(value) {
Ok(value) => Self::Status(value),
Err(_) => Self::Other(value),
}
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
unsafe fn sbi_do_call(
extension: u64,
function: u64,
mut a0: u64,
mut a1: u64,
a2: u64,
a3: u64,
a4: u64,
a5: u64,
) -> Result<u64, SbiError> {
unsafe {
core::arch::asm!(
"ecall",
inlateout("a0") a0,
inlateout("a1") a1,
in("a2") a2,
in("a3") a3,
in("a4") a4,
in("a5") a5,
in("a6") function,
in("a7") extension,
);
}
let a0 = a0 as i64;
if a0 == 0 {
Ok(a1)
} else {
Err(a0.into())
}
}
pub fn sbi_hart_start(hart_id: u64, start_addr: u64, opaque: u64) -> Result<(), Error> {
match unsafe { sbi_do_call(EXT_HSM, 0x00, hart_id, start_addr, opaque, 0, 0, 0) } {
Ok(_) => Ok(()),
Err(SbiError::Status(Status::AlreadyAvailable)) => Err(Error::AlreadyExists),
Err(SbiError::Status(Status::InvalidParam)) => Err(Error::DoesNotExist),
Err(SbiError::Status(Status::InvalidAddress)) => Err(Error::InvalidArgument),
Err(_) => Err(Error::InvalidOperation),
}
}
pub fn sbi_send_ipi(hart_mask: u64, hart_mask_base: u64) -> Result<(), Error> {
match unsafe { sbi_do_call(EXT_SPI, 0x00, hart_mask, hart_mask_base, 0, 0, 0, 0) } {
Ok(_) => Ok(()),
Err(SbiError::Status(Status::InvalidParam)) => Err(Error::DoesNotExist),
Err(_) => Err(Error::InvalidOperation),
}
}
pub fn sbi_debug_console_write_byte(byte: u8) {
unsafe { sbi_do_call(EXT_DBCN, 0x02, byte as u64, 0, 0, 0, 0, 0) }.ok();
}
pub fn sbi_set_timer(next_event: u64) {
unsafe { sbi_do_call(EXT_TIME, 0x00, next_event, 0, 0, 0, 0, 0) }.ok();
}
+2 -2
View File
@@ -26,8 +26,8 @@ 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 = "riscv64")] {
extern crate kernel_arch_riscv64 as imp;
} else if #[cfg(target_arch = "x86")] {
extern crate kernel_arch_i686 as imp;
} else {
compile_error!("Unsupported architecture");
}
+10 -31
View File
@@ -95,17 +95,10 @@ cpuid_features! {
]
}
cpuid_features! {
pub ExtEdxFeatures: u32 [
PDPE1GB: 26
]
}
#[derive(Clone, Copy, Debug)]
pub struct CpuFeatures {
pub ecx: EcxFeatures,
pub edx: EdxFeatures,
pub ext_edx: ExtEdxFeatures,
}
impl CpuFeatures {
@@ -113,7 +106,6 @@ impl CpuFeatures {
Self {
ecx: EcxFeatures::empty(),
edx: EdxFeatures::empty(),
ext_edx: ExtEdxFeatures::empty(),
}
}
@@ -128,7 +120,6 @@ impl CpuFeatures {
Err(Self {
ecx: features.ecx & !self.ecx,
edx: features.edx & !self.edx,
ext_edx: features.ext_edx & !self.ext_edx,
})
}
}
@@ -141,7 +132,6 @@ impl BitAnd<CpuFeatures> for CpuFeatures {
Self {
ecx: self.ecx & rhs.ecx,
edx: self.edx & rhs.edx,
ext_edx: self.ext_edx & rhs.ext_edx,
}
}
}
@@ -153,7 +143,6 @@ impl BitOr<CpuFeatures> for CpuFeatures {
Self {
ecx: self.ecx | rhs.ecx,
edx: self.edx | rhs.edx,
ext_edx: self.ext_edx | rhs.ext_edx,
}
}
}
@@ -162,9 +151,8 @@ impl CpuFeatureSet for CpuFeatures {
fn iter(&self) -> impl Iterator<Item = &'static str> {
let ecx = self.ecx.iter().map(|e| e.as_str());
let edx = self.edx.iter().map(|e| e.as_str());
let ext_edx = self.ext_edx.iter().map(|e| e.as_str());
core::iter::chain(core::iter::chain(ecx, edx), ext_edx)
core::iter::chain(ecx, edx)
}
}
@@ -202,26 +190,20 @@ unsafe fn raw_cpuid(eax: u32, result: &mut [u32]) {
);
}
fn cpuid_features() -> (EcxFeatures, EdxFeatures, ExtEdxFeatures) {
fn cpuid_features() -> (EcxFeatures, EdxFeatures) {
let mut raw = [0; 3];
unsafe {
raw_cpuid(0x1, &mut raw);
}
let ecx = EcxFeatures::from_bits_truncate(raw[2]);
let edx = EdxFeatures::from_bits_truncate(raw[1]);
unsafe {
raw_cpuid(0x80000001, &mut raw);
}
let ext_edx = ExtEdxFeatures::from_bits_truncate(raw[1]);
(ecx, edx, ext_edx)
(
EcxFeatures::from_bits_truncate(raw[2]),
EdxFeatures::from_bits_truncate(raw[1]),
)
}
fn enable_features(ecx: EcxFeatures, edx: EdxFeatures, _ext_edx: ExtEdxFeatures) {
fn enable_features(ecx: EcxFeatures, edx: EdxFeatures) {
if ecx.contains(EcxFeatures::XSAVE) {
CR4.modify(CR4::OSXSAVE::SET);
}
@@ -248,16 +230,13 @@ fn enable_features(ecx: EcxFeatures, edx: EdxFeatures, _ext_edx: ExtEdxFeatures)
if ecx.contains(EcxFeatures::PCID) {
CR4.modify(CR4::PCIDE::SET);
}
if edx.contains(EdxFeatures::PSE) {
CR4.modify(CR4::PSE::SET);
}
CR0.modify(CR0::TS::CLEAR);
}
fn read_features() -> CpuFeatures {
let (ecx, edx, ext_edx) = cpuid_features();
CpuFeatures { ecx, edx, ext_edx }
let (ecx, edx) = cpuid_features();
CpuFeatures { ecx, edx }
}
pub fn setup_features(
@@ -271,7 +250,7 @@ pub fn setup_features(
return (have_features, Err(missing_features));
}
enable_features(will_features.ecx, will_features.edx, will_features.ext_edx);
enable_features(will_features.ecx, will_features.edx);
(have_features, Ok(will_features))
}
+3 -3
View File
@@ -1,5 +1,5 @@
#[allow(dead_code)]
#[repr(C, packed)]
#[repr(packed)]
pub struct Entry {
pub limit_lo: u16,
pub base_lo: u16,
@@ -10,7 +10,7 @@ pub struct Entry {
}
#[allow(dead_code)]
#[repr(C, packed)]
#[repr(packed)]
pub struct Pointer {
pub limit: u16,
pub offset: usize,
@@ -121,7 +121,7 @@ mod imp {
use super::{Entry, Pointer};
#[allow(dead_code)]
#[repr(C, packed)]
#[repr(packed)]
pub struct Tss {
_0: u32,
rsp0: u64,
-6
View File
@@ -6,10 +6,4 @@ extern crate alloc;
pub mod cpuid;
pub mod gdt;
pub mod intrinsics;
pub mod registers;
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = 1024;
#[cfg(any(target_arch = "x86", rust_analyzer))]
pub const ISA_IRQ_OFFSET: u32 = 0;
+2 -2
View File
@@ -146,7 +146,7 @@ mod cr2 {
}
mod cr3 {
use tock_registers::{interfaces::Writeable, register_bitfields};
use tock_registers::{interfaces::ReadWriteable, register_bitfields};
register_bitfields! {
usize,
@@ -164,7 +164,7 @@ mod cr3 {
impl Reg {
pub fn set_address(&self, address: usize) {
assert_eq!(address & 0xFFF, 0);
self.write(CR3::ADDR.val(address >> 12))
self.modify(CR3::ADDR.val(address >> 12))
}
}
+1
View File
@@ -7,6 +7,7 @@ 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
+4 -7
View File
@@ -5,14 +5,11 @@ 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::PhysicalAddress;
use libk_mm_interface::address::{AsPhysicalAddress, PhysicalAddress};
use tock_registers::interfaces::Writeable;
use yggdrasil_abi::{arch::SavedFrame, error::Error};
use crate::{
mem::{auto_lower_address, fixed},
ArchitectureImpl,
};
use crate::{mem::KERNEL_TABLES, ArchitectureImpl};
/// Frame saved onto the stack when taking an IRQ
#[derive(Debug)]
@@ -434,7 +431,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 = auto_lower_address(&raw const fixed::KERNEL_PML4); // unsafe { KERNEL_TABLES.lock().as_physical_address() }.into();
let cr3: usize = 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>();
@@ -539,7 +536,7 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
for TaskContextImpl<K, PA>
{
fn drop(&mut self) {
log::trace!("Drop Context {:#p}", self);
log::info!("Drop Context {:#p}", self);
assert_eq!(self.stack_size % 0x1000, 0);
for offset in (0..self.stack_size).step_by(0x1000) {
+20 -41
View File
@@ -1,17 +1,18 @@
#![no_std]
#![allow(clippy::new_without_default)]
#![feature(naked_functions, trait_upcasting)]
extern crate alloc;
use core::{
ops::{DerefMut, Range},
ops::DerefMut,
sync::atomic::{AtomicUsize, Ordering},
};
use alloc::{sync::Arc, vec::Vec};
use alloc::vec::Vec;
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
use kernel_arch_interface::{
cpu::{CpuData, CpuImpl, IpiQueue},
cpu::{CpuImpl, IpiQueue},
task::Scheduler,
util::OneTimeInit,
Architecture,
@@ -51,33 +52,32 @@ pub struct PerCpuData {
// 0x10, used in assembly
pub tmp_address: usize,
pub local_apic: Arc<dyn LocalApicInterface>,
pub local_apic: &'static dyn LocalApicInterface,
pub available_features: CpuFeatures,
pub enabled_features: CpuFeatures,
}
impl CpuData for PerCpuData {}
impl PerCpuData {
#[inline]
pub fn local_apic(&self) -> &dyn LocalApicInterface {
self.local_apic.as_ref()
pub fn local_apic(&self) -> &'static dyn LocalApicInterface {
self.local_apic
}
}
static IPI_QUEUES: OneTimeInit<Vec<IpiQueue<ArchitectureImpl>>> = OneTimeInit::new();
pub static CPU_COUNT: AtomicUsize = AtomicUsize::new(1);
#[unsafe(naked)]
#[naked]
extern "C" fn idle_task(_: usize) -> ! {
core::arch::naked_asm!(
r#"
unsafe {
core::arch::naked_asm!(
r#"
1:
nop
jmp 1b
"#,
options(att_syntax)
);
options(att_syntax)
);
}
}
impl ArchitectureImpl {
@@ -182,7 +182,12 @@ impl Architecture for ArchitectureImpl {
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
let cpu = Self::local_cpu_data()?;
Some(cpu.local_apic.as_ref())
Some(cpu.local_apic)
}
fn message_interrupt_controller() -> &'static dyn MessageInterruptController {
let local = Self::local_cpu_data().unwrap();
local.local_apic
}
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
@@ -192,30 +197,4 @@ impl Architecture for ArchitectureImpl {
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
Some(&cpu.available_features)
}
// Cache/barrier
fn load_barrier() {
unsafe { core::arch::x86_64::_mm_lfence() };
}
fn store_barrier() {
unsafe { core::arch::x86_64::_mm_sfence() };
}
fn memory_barrier() {
unsafe { core::arch::x86_64::_mm_mfence() };
}
fn flush_virtual_range(range: Range<usize>) {
// TODO I assume 64-byte cache line on all CPUs
// TODO clflush instruction may not be available, test for it
const CLSIZE: usize = 64;
let start = range.start & !(CLSIZE - 1);
let end = (range.end + (CLSIZE - 1)) & !(CLSIZE - 1);
for line in (start..end).step_by(CLSIZE) {
unsafe { core::arch::x86_64::_mm_clflush(line as _) };
}
}
}
-131
View File
@@ -1,131 +0,0 @@
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));
}
+327 -33
View File
@@ -1,37 +1,100 @@
use kernel_arch_interface::mem::{
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
use core::{
alloc::Layout,
ops::{Deref, DerefMut},
ptr::addr_of,
sync::atomic::{AtomicUsize, Ordering},
};
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
use kernel_arch_interface::{
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
sync::split_spinlock,
};
use kernel_arch_x86::registers::CR3;
use libk_mm_interface::{
address::PhysicalAddress,
table::{page_index, EntryLevel, EntryLevelExt},
};
use memtables::x86_64::FixedTables;
use static_assertions::{const_assert_eq, const_assert_ne};
use yggdrasil_abi::error::Error;
use crate::KERNEL_VIRT_OFFSET;
use self::table::{PageTable, L0, L1};
use self::table::{PageAttributes, PageEntry, PageTable, L0, L1, L2, L3};
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 crate::ArchitectureImpl;
use crate::mem::FixedTables;
use libk_mm_interface::KernelImageObject;
#[link_section = ".data.tables"]
static KERNEL_TABLES<lock: ArchitectureImpl>: 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 < fixed::IDENTITY_SIZE_L1 * L1::SIZE {
address + KERNEL_VIRT_OFFSET
if address < MEMORY_LIMIT.load(Ordering::Acquire) {
address + RAM_MAPPING_OFFSET
} else {
panic!("Invalid physical address: {address:#x}");
panic!("Invalid physical address: {:#x}", address);
}
}
fn physicalize(address: usize) -> u64 {
if address < KERNEL_VIRT_OFFSET
|| address - KERNEL_VIRT_OFFSET >= fixed::IDENTITY_SIZE_L1 * L1::SIZE
if address < RAM_MAPPING_OFFSET
|| address - RAM_MAPPING_OFFSET >= MEMORY_LIMIT.load(Ordering::Acquire)
{
panic!("Invalid virtualized address: {address:#x}");
panic!("Not a virtualized physical address: {:#x}", address);
}
(address - KERNEL_VIRT_OFFSET) as u64
(address - RAM_MAPPING_OFFSET) as _
}
unsafe fn map_device_pages(
@@ -39,30 +102,238 @@ impl KernelTableManager for KernelTableManagerImpl {
count: usize,
attrs: DeviceMemoryAttributes,
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.map_device_pages(PhysicalAddress::from_u64(base), count, attrs)
map_device_memory(PhysicalAddress::from_u64(base), count, attrs)
}
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
let _lock = fixed::LOCK.lock();
#[allow(static_mut_refs)]
fixed::DEVICE_MEMORY.unmap_device_pages(mapping);
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);
}
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(
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(
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);
}
}
}
}
pub fn clone_kernel_tables(dst: &mut PageTable<L0>) {
let tables = KERNEL_TABLES.lock();
unsafe {
dst[fixed::KERNEL_L0I] = fixed::KERNEL_PML4[fixed::KERNEL_L0I];
}
}
pub fn auto_lower_address<T>(pointer: *const T) -> usize {
let address = pointer.addr();
if address < KERNEL_VIRT_OFFSET {
address
} else {
address - KERNEL_VIRT_OFFSET
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]);
}
}
@@ -82,12 +353,35 @@ pub fn auto_lower_address<T>(pointer: *const T) -> usize {
/// # Safety
///
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
#[inline(never)]
pub unsafe fn init_fixed_tables(have_1gib_pages: bool, bsp: bool) {
fixed::setup(have_1gib_pages);
if bsp {
fixed::load();
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 = addr_of!(EARLY_MAPPING_L3) as usize - KERNEL_VIRT_OFFSET;
let device_mapping_l2_phys = addr_of!(DEVICE_MAPPING_L2) as usize - KERNEL_VIRT_OFFSET;
let ram_mapping_l1_phys = addr_of!(RAM_MAPPING_L1) as usize - KERNEL_VIRT_OFFSET;
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::WRITABLE);
}
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 = (&raw const tables.l0).addr() - KERNEL_VIRT_OFFSET;
CR3.set_address(cr3);
}
/// # Safety
+6 -41
View File
@@ -4,7 +4,7 @@ use core::marker::PhantomData;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::PhysicalRefMut,
process::{PageAttributeUpdate, ProcessAddressSpaceManager},
process::ProcessAddressSpaceManager,
table::{
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
},
@@ -61,15 +61,7 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
self.write_l3_entry(address, PageEntry::page(physical, flags.into()), false)
}
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> {
unsafe fn unmap_page(&mut self, address: usize) -> Result<PhysicalAddress, Error> {
self.pop_l3_entry(address)
}
@@ -79,9 +71,9 @@ impl<TA: TableAllocator> ProcessAddressSpaceManager<TA> for ProcessAddressSpaceI
.ok_or(Error::InvalidMemoryOperation)
}
fn as_address_with_asid(&self) -> (u64, u64) {
fn as_address_with_asid(&self) -> u64 {
// TODO x86-64 PCID/ASID?
(unsafe { self.l0.as_physical_address().into_u64() }, 0)
unsafe { self.l0.as_physical_address().into_u64() }
}
unsafe fn clear(&mut self) {
@@ -119,33 +111,7 @@ impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
Ok(())
}
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> {
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
let l0i = virt.page_index::<L0>();
let l1i = virt.page_index::<L1>();
let l2i = virt.page_index::<L2>();
@@ -157,14 +123,13 @@ 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, dirty))
Ok(page)
}
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
+2 -26
View File
@@ -8,7 +8,6 @@ use bitflags::bitflags;
use libk_mm_interface::{
address::{AsPhysicalAddress, PhysicalAddress},
pointer::{PhysicalRef, PhysicalRefMut},
process::PageAttributeUpdate,
table::{
EntryLevel, EntryLevelDrop, MapAttributes, NextPageTable, NonTerminalEntryLevel,
TableAllocator,
@@ -32,8 +31,6 @@ 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;
}
}
@@ -101,15 +98,11 @@ impl PageEntry<L3> {
/// not
pub fn as_page(self) -> Option<PhysicalAddress> {
if self.0 & PageAttributes::PRESENT.bits() != 0 {
Some(PhysicalAddress::from_u64(self.0 & !Self::ATTR_MASK))
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
} else {
None
}
}
pub fn is_dirty(&self) -> bool {
self.0 & PageAttributes::DIRTY.bits() != 0
}
}
impl PageEntry<L2> {
@@ -152,7 +145,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 & !Self::ATTR_MASK))
Some(PhysicalAddress::from_u64(self.0 & !0xFFF))
} else {
None
}
@@ -165,8 +158,6 @@ 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);
@@ -188,21 +179,6 @@ 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 -4
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!("{assembler}: could not assemble {AP_BOOTSTRAP_S}");
panic!("{}: could not assemble {}", assembler, AP_BOOTSTRAP_S);
}
}
@@ -92,7 +92,6 @@ fn main() {
"x86" => (),
"x86_64" => build_x86_64(),
"aarch64" => (),
"riscv64" => (),
_ => panic!("Unknown target arch: {arch:?}"),
_ => panic!("Unknown target arch: {:?}", arch),
}
}
-18
View File
@@ -1,18 +0,0 @@
[package]
name = "ygg_driver_acpi"
version = "0.1.0"
edition = "2024"
[dependencies]
libk-util.workspace = true
libk-mm.workspace = true
libk.workspace = true
device-api.workspace = true
kernel-arch-x86.path = "../../arch/x86"
acpi.workspace = true
rsdp.workspace = true
aml.workspace = true
acpi-system.workspace = true
log.workspace = true
-131
View File
@@ -1,131 +0,0 @@
use core::time::Duration;
use crate::AcpiHandlerImpl;
impl aml::Handler for AcpiHandlerImpl {
fn read_io_u8(&self, port: u16) -> u8 {
<Self as acpi_system::Handler>::io_read_u8(port)
}
fn read_io_u16(&self, port: u16) -> u16 {
<Self as acpi_system::Handler>::io_read_u16(port)
}
fn read_io_u32(&self, port: u16) -> u32 {
<Self as acpi_system::Handler>::io_read_u32(port)
}
fn write_io_u8(&self, port: u16, value: u8) {
<Self as acpi_system::Handler>::io_write_u8(port, value)
}
fn write_io_u16(&self, port: u16, value: u16) {
<Self as acpi_system::Handler>::io_write_u16(port, value)
}
fn write_io_u32(&self, port: u16, value: u32) {
<Self as acpi_system::Handler>::io_write_u32(port, value)
}
fn read_u8(&self, address: usize) -> u8 {
<Self as acpi_system::Handler>::mem_read_u8(address as u64)
}
fn read_u16(&self, address: usize) -> u16 {
<Self as acpi_system::Handler>::mem_read_u16(address as u64)
}
fn read_u32(&self, address: usize) -> u32 {
<Self as acpi_system::Handler>::mem_read_u32(address as u64)
}
fn read_u64(&self, address: usize) -> u64 {
<Self as acpi_system::Handler>::mem_read_u64(address as u64)
}
fn write_u8(&self, address: usize, value: u8) {
<Self as acpi_system::Handler>::mem_write_u8(address as u64, value)
}
fn write_u16(&self, address: usize, value: u16) {
<Self as acpi_system::Handler>::mem_write_u16(address as u64, value)
}
fn write_u32(&self, address: usize, value: u32) {
<Self as acpi_system::Handler>::mem_write_u32(address as u64, value)
}
fn write_u64(&self, address: usize, value: u64) {
<Self as acpi_system::Handler>::mem_write_u64(address as u64, value)
}
fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 {
0xFF
}
fn read_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u16 {
0xFFFF
}
fn read_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
) -> u32 {
0xFFFFFFFF
}
fn write_pci_u8(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u8,
) {
}
fn write_pci_u16(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u16,
) {
}
fn write_pci_u32(
&self,
_segment: u16,
_bus: u8,
_device: u8,
_function: u8,
_offset: u16,
_value: u32,
) {
}
fn read_ec_u8(&self, _address: u64) -> u8 {
0x00
}
fn write_ec_u8(&self, _address: u64, _value: u8) {}
fn sleep(&self, _duration: Duration) {
todo!()
// util::polling_sleep(duration).unwrap();
}
}
-171
View File
@@ -1,171 +0,0 @@
use core::{ptr::NonNull, time::Duration};
use acpi::PhysicalMapping;
use acpi_system::AcpiSystemError;
use alloc::sync::Arc;
use device_api::{
device::Device,
interrupt::{InterruptHandler, Irq, IrqVector},
};
use kernel_arch_x86::{intrinsics, ISA_IRQ_OFFSET};
use libk::device::external_interrupt_controller;
use libk_mm::{
address::{PhysicalAddress, Virtualize},
pointer::PhysicalRef,
};
use crate::{
mem::{read_memory, write_memory},
ACPI_SYSTEM,
};
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiHandlerImpl;
struct SciHandler;
impl acpi_system::Handler for AcpiHandlerImpl {
type MappedSlice = PhysicalRef<'static, [u8]>;
unsafe fn map_slice(address: u64, length: u64) -> Self::MappedSlice {
unsafe {
PhysicalRef::map_slice(
PhysicalAddress::from_u64(address),
length.try_into().unwrap(),
)
}
}
fn io_read_u8(port: u16) -> u8 {
let value = unsafe { intrinsics::inb(port) };
log::trace!("io_read_u8 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u16(port: u16) -> u16 {
let value = unsafe { intrinsics::inw(port) };
log::trace!("io_read_u16 {:#x} <- {:#x}", port, value);
value
}
fn io_read_u32(port: u16) -> u32 {
let value = unsafe { intrinsics::inl(port) };
log::trace!("io_read_u32 {:#x} <- {:#x}", port, value);
value
}
fn io_write_u8(port: u16, value: u8) {
log::trace!("io_write_u8 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outb(port, value) }
}
fn io_write_u16(port: u16, value: u16) {
log::trace!("io_write_u16 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outw(port, value) }
}
fn io_write_u32(port: u16, value: u32) {
log::trace!("io_write_u32 {:#x}, {:#x}", port, value);
unsafe { intrinsics::outl(port, value) }
}
fn mem_read_u8(address: u64) -> u8 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u8 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u16(address: u64) -> u16 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u16 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u32(address: u64) -> u32 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u32 {:#x} -> {:#x}", address, value);
value
}
fn mem_read_u64(address: u64) -> u64 {
let value = unsafe { read_memory(PhysicalAddress::from_u64(address)) };
log::trace!("mem_read_u64 {:#x} -> {:#x}", address, value);
value
}
fn mem_write_u8(address: u64, value: u8) {
log::trace!("mem_write_u8 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u16(address: u64, value: u16) {
log::trace!("mem_write_u16 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u32(address: u64, value: u32) {
log::trace!("mem_write_u32 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn mem_write_u64(address: u64, value: u64) {
log::trace!("mem_write_u64 {:#x}, {:#x}", address, value);
unsafe { write_memory(PhysicalAddress::from_u64(address), value) }
}
fn install_interrupt_handler(irq: u32) -> Result<(), AcpiSystemError> {
log::info!("Installing ACPI SCI handler at IRQ #{}", irq);
let intc = external_interrupt_controller().expect("No external intc");
let handler = Arc::new(SciHandler);
let irq = Irq::External(irq + ISA_IRQ_OFFSET);
intc.register_irq(irq, Default::default(), handler).unwrap();
intc.enable_irq(irq).unwrap();
Ok(())
}
fn stall(_duration: Duration) {
// TODO polling_sleep is not yet implemented properly
todo!()
// util::polling_sleep(duration).ok();
}
}
impl rsdp::handler::AcpiHandler for AcpiHandlerImpl {
unsafe fn map_physical_region<T>(
&self,
physical_address: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
unsafe {
PhysicalMapping::new(
physical_address,
NonNull::new_unchecked(
PhysicalAddress::from_usize(physical_address).virtualize() as *mut T
),
size,
size,
*self,
)
}
}
fn unmap_physical_region<T>(_region: &acpi::PhysicalMapping<Self, T>) {}
}
impl InterruptHandler for SciHandler {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
log::trace!("ACPI SCI received");
ACPI_SYSTEM.get().lock().handle_sci();
true
}
}
impl Device for SciHandler {
fn display_name(&self) -> &str {
"ACPI SCI handler"
}
}
-89
View File
@@ -1,89 +0,0 @@
#![feature(allocator_api)]
#![no_std]
use acpi::AcpiTables;
use acpi_system::{AcpiInterruptMethod, AcpiSystem};
use alloc::boxed::Box;
use libk::error::Error;
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
extern crate alloc;
pub mod mem;
pub use mem::AcpiAllocator;
pub mod handler;
pub use handler::AcpiHandlerImpl;
pub mod aml_handler;
pub use acpi_system::{
EventAction, FixedEvent, InterruptPolarity, InterruptTrigger, IrqDescriptor, PciPin,
};
static ACPI_SYSTEM: OneTimeInit<IrqSafeSpinlock<AcpiSystem<AcpiHandlerImpl>>> = OneTimeInit::new();
pub fn add_event_handler<F: Fn(&AcpiSystem<AcpiHandlerImpl>) -> EventAction + 'static>(
event: &FixedEvent,
handler: F,
) -> Result<(), Error> {
ACPI_SYSTEM
.get()
.lock()
.enable_fixed_event(event, Box::new(handler))
.map_err(|_| Error::InvalidArgument)
}
pub fn get_pci_route(
aml_path: &str,
device: u16,
function: u16,
pin: PciPin,
) -> Option<IrqDescriptor> {
ACPI_SYSTEM
.get()
.lock()
.pci_route(aml_path, device, function, pin)
.ok()
}
/// Initializes ACPI management
pub fn switch_to_acpi(tables: &'static AcpiTables<AcpiHandlerImpl>) -> Result<(), Error> {
// NOTE mostly broken for real HW
let mut system = AcpiSystem::new(tables, Box::new(AcpiHandlerImpl)).unwrap();
system.initialize(AcpiInterruptMethod::Apic).unwrap();
// system
// .enable_fixed_event(
// &FixedEvent::POWER_BUTTON,
// Box::new(|_| {
// log::info!("Power button was pressed");
// // TODO the correct way would be to
// // 1. Nicely ask all the processes to quit
// // 2. Wait for some time
// // 3. Kill the remaining ones
// // 4. Halt other cores
// // 5. Sync filesystem
// // 6. Do something with the devices
// // 7. Actually enter the S5 state
// unsafe {
// PLATFORM
// .send_ipi(IpiDeliveryTarget::OtherCpus, IpiMessage::Shutdown)
// .unwrap();
// }
// SHUTDOWN_FENCE.signal();
// SHUTDOWN_FENCE.wait_all(CPU_COUNT.load(Ordering::Acquire));
// log::info!("CPUs are parked, can shutdown now");
// EventAction::EnterSleepState(AcpiSleepState::S5)
// }),
// )
// .unwrap();
ACPI_SYSTEM.init(IrqSafeSpinlock::new(system));
Ok(())
}
-70
View File
@@ -1,70 +0,0 @@
//! ACPI memory IO and management functions
use core::{
alloc::{AllocError, Allocator, GlobalAlloc, Layout},
ptr::NonNull,
};
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryMapping, heap::GLOBAL_HEAP};
#[derive(Clone, Copy)]
#[doc(hidden)]
pub struct AcpiAllocator;
unsafe impl Allocator for AcpiAllocator {
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
let ptr = unsafe { GLOBAL_HEAP.alloc(layout) };
log::trace!("ACPI alloc: {:?} -> {:p}", layout, ptr);
if ptr.is_null() {
Err(AllocError)
} else {
unsafe {
Ok(NonNull::slice_from_raw_parts(
NonNull::new_unchecked(ptr),
layout.size(),
))
}
}
}
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
log::trace!("ACPI dealloc: {:?}, {:?}", ptr, layout);
unsafe { GLOBAL_HEAP.dealloc(ptr.as_ptr(), layout) };
}
}
// 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() };
let address = io.address();
unsafe {
if address % align_of::<T>() == 0 {
(address as *const T).read_volatile()
} else {
(address as *const T).read_unaligned()
}
}
}
/// # 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() };
let address = io.address();
unsafe {
if address % align_of::<T>() == 0 {
(address as *mut T).write_volatile(value)
} else {
(address as *mut T).write_unaligned(value)
}
}
}
+21 -16
View File
@@ -1,10 +1,12 @@
use core::mem::{size_of, MaybeUninit};
use device_api::dma::DmaAllocator;
use libk::dma::{BusAddress, DmaBuffer, DmaSliceMut};
use libk_mm::{
address::{AsPhysicalAddress, PhysicalAddress},
PageBox, PageSlice,
};
use tock_registers::register_structs;
use crate::{data::AtaString, error::AhciError, MAX_PRD_SIZE};
use crate::{data::AtaString, error::AhciError, MAX_PRD_SIZE, SECTOR_SIZE};
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[repr(u8)]
@@ -20,7 +22,7 @@ pub trait AtaCommand {
fn lba(&self) -> u64;
fn sector_count(&self) -> usize;
fn buffer(&self) -> Option<(BusAddress, usize)>;
fn buffer(&self) -> Option<(PhysicalAddress, usize)>;
unsafe fn into_response(self) -> Self::Response;
fn prd_count(&self) -> usize {
@@ -62,41 +64,44 @@ register_structs! {
}
pub struct AtaIdentify {
buffer: DmaBuffer<MaybeUninit<AtaIdentifyResponse>>,
buffer: PageBox<MaybeUninit<AtaIdentifyResponse>>,
}
pub struct AtaReadDmaEx {
lba: u64,
sector_count: usize,
buffer_base: BusAddress,
buffer_base: PhysicalAddress,
buffer_size: usize,
}
impl AtaIdentify {
pub fn create(dma: &dyn DmaAllocator) -> Result<Self, AhciError> {
DmaBuffer::new_uninit(dma)
pub fn create() -> Result<Self, AhciError> {
PageBox::new_uninit()
.map(Self::with_data)
.map_err(AhciError::MemoryError)
}
pub fn with_data(buffer: DmaBuffer<MaybeUninit<AtaIdentifyResponse>>) -> Self {
pub fn with_data(buffer: PageBox<MaybeUninit<AtaIdentifyResponse>>) -> Self {
Self { buffer }
}
}
impl AtaReadDmaEx {
pub fn new(lba: u64, sector_count: usize, buffer: DmaSliceMut<MaybeUninit<u8>>) -> Self {
pub fn new(lba: u64, sector_count: usize, buffer: &PageSlice<MaybeUninit<u8>>) -> Self {
assert_eq!(buffer.len() % SECTOR_SIZE, 0);
assert_ne!(buffer.len(), 0);
Self {
lba,
sector_count,
buffer_base: buffer.bus_address(),
buffer_base: unsafe { buffer.as_physical_address() },
buffer_size: buffer.len(),
}
}
}
impl AtaCommand for AtaIdentify {
type Response = DmaBuffer<AtaIdentifyResponse>;
type Response = PageBox<AtaIdentifyResponse>;
const COMMAND_ID: AtaCommandId = AtaCommandId::Identify;
@@ -108,14 +113,14 @@ impl AtaCommand for AtaIdentify {
0
}
fn buffer(&self) -> Option<(BusAddress, usize)> {
let base = self.buffer.bus_address();
fn buffer(&self) -> Option<(PhysicalAddress, usize)> {
let base = unsafe { self.buffer.as_physical_address() };
let size = size_of::<AtaIdentifyResponse>();
Some((base, size))
}
unsafe fn into_response(self) -> Self::Response {
DmaBuffer::assume_init(self.buffer)
self.buffer.assume_init()
}
}
@@ -132,7 +137,7 @@ impl AtaCommand for AtaReadDmaEx {
self.sector_count
}
fn buffer(&self) -> Option<(BusAddress, usize)> {
fn buffer(&self) -> Option<(PhysicalAddress, usize)> {
Some((self.buffer_base, self.buffer_size))
}
+10 -6
View File
@@ -2,7 +2,7 @@ use core::mem::size_of;
use alloc::string::String;
use bytemuck::{Pod, Zeroable};
use libk::dma::BusAddress;
use libk_mm::address::PhysicalAddress;
use libk_util::{ConstAssert, IsTrue};
use static_assertions::const_assert_eq;
@@ -166,7 +166,7 @@ impl CommandTable {
}
assert_eq!(prd, command.prd_count());
self.prdt[prd..].fill_with(PhysicalRegionDescriptor::zeroed);
self.prdt[prd..].fill_with(|| PhysicalRegionDescriptor::zeroed());
}
Ok(())
@@ -174,7 +174,7 @@ impl CommandTable {
}
impl CommandListEntry {
pub fn new(command_table_entry: BusAddress, prd_count: usize) -> Result<Self, AhciError> {
pub fn new(command_table_entry: PhysicalAddress, prd_count: usize) -> Result<Self, AhciError> {
if prd_count > 0xFFFF {
todo!()
}
@@ -183,7 +183,7 @@ impl CommandListEntry {
attr: (size_of::<RegisterHostToDeviceFis>() / size_of::<u32>()) as _,
prdtl: prd_count as _,
prdbc: 0,
ctba: command_table_entry.into_u64(),
ctba: command_table_entry.into(),
_0: [0; 4],
})
}
@@ -201,14 +201,18 @@ unsafe impl Zeroable for CommandTable {
}
impl PhysicalRegionDescriptor {
pub fn new(address: BusAddress, byte_count: usize, is_last: bool) -> Result<Self, AhciError> {
pub fn new(
address: PhysicalAddress,
byte_count: usize,
is_last: bool,
) -> Result<Self, AhciError> {
if byte_count > MAX_PRD_SIZE {
return Err(AhciError::RegionTooLarge);
}
let dbc_mask = (is_last as u32) << 31;
Ok(Self {
buffer_address: address.into_u64(),
buffer_address: address.into(),
_0: 0,
dbc: ((byte_count as u32 - 1) << 1) | 1 | dbc_mask,
})
+84 -115
View File
@@ -4,27 +4,29 @@
extern crate alloc;
use alloc::{format, sync::Arc, vec::Vec};
use alloc::{boxed::Box, format, vec, vec::Vec};
use bytemuck::Zeroable;
use data::ReceivedFis;
use device_api::{
device::{Device, DeviceInitContext},
dma::DmaAllocator,
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
interrupt::{InterruptAffinity, InterruptHandler},
Device,
};
use error::AhciError;
use libk::{device::manager::probe_partitions, dma::DmaBuffer, fs::devfs, task::runtime};
use libk_mm::device::DeviceMemoryIo;
use kernel_fs::devfs;
use libk::{
task::runtime,
vfs::block::{probe_partitions, NgBlockDeviceWrapper},
};
use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox};
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
use port::AhciPort;
use regs::{PortRegs, Regs};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use ygg_driver_pci::{
device::{PciDeviceInfo, PreferredInterruptMode},
macros::pci_driver,
PciCommandRegister, PciConfigurationSpace,
};
use yggdrasil_abi::{error::Error, io::FileMode};
use yggdrasil_abi::error::Error;
use crate::regs::{Version, CAP, GHC, SSTS};
@@ -41,9 +43,8 @@ const MAX_DRIVES: usize = (b'z' - b'a') as usize;
pub struct AhciController {
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
dma: Arc<dyn DmaAllocator>,
ports: OneTimeInit<Vec<Arc<AhciPort>>>,
received_fis_buffers: OneTimeInit<[Option<DmaBuffer<ReceivedFis>>; 16]>,
ports: OneTimeInit<Vec<&'static AhciPort>>,
received_fis_buffers: OneTimeInit<[Option<PageBox<ReceivedFis>>; 16]>,
version: Version,
max_port_count: usize,
@@ -52,7 +53,7 @@ pub struct AhciController {
}
impl AhciController {
async fn late_init(self: Arc<Self>) -> Result<(), AhciError> {
async fn late_init(&'static self) -> Result<(), AhciError> {
log::info!("Initializing AHCI SATA Controller {:?}", self.version);
let regs = self.regs.lock();
@@ -69,7 +70,7 @@ impl AhciController {
let pi = regs.PI.get();
let mut ports = Vec::new();
let mut ports = vec![];
drop(regs);
@@ -83,9 +84,8 @@ impl AhciController {
let regs = self.regs.lock();
let port = &regs.PORTS[i];
let buffer = DmaBuffer::new(&*self.dma, ReceivedFis::zeroed())
.map_err(AhciError::MemoryError)?;
port.set_received_fis_address_64(buffer.bus_address());
let buffer = PageBox::new(ReceivedFis::zeroed()).map_err(AhciError::MemoryError)?;
port.set_received_fis_address_64(unsafe { buffer.as_physical_address() });
*fis_buffer_slot = Some(buffer);
}
@@ -117,7 +117,7 @@ impl AhciController {
drop(regs);
let port = match AhciPort::create(port, self.clone(), i) {
let port = match AhciPort::create(port, self, i) {
Ok(port) => port,
Err(error) => {
log::warn!("Port {} init error: {:?}", i, error);
@@ -134,13 +134,13 @@ impl AhciController {
self.regs.lock().GHC.modify(GHC::IE::SET);
// Setup the detected ports
for (i, port) in ports.iter().enumerate() {
for (i, &port) in ports.iter().enumerate() {
log::info!("Init port {}", i);
port.init_inner().await?;
port.init().await?;
}
// Dump info about the drives
for (i, port) in ports.iter().enumerate() {
for (i, &port) in ports.iter().enumerate() {
let info = port.info().unwrap();
log::info!(
"Port {}: model={:?}, serial={:?}, lba_count={}",
@@ -151,8 +151,25 @@ impl AhciController {
);
}
for port in ports.iter() {
register_sata_drive(port.clone(), true);
{
let mut lock = SATA_DRIVES.lock();
for &port in ports.iter() {
let n = lock.len();
if n >= MAX_DRIVES {
todo!("Too many drives, ran out of letters");
}
let n = n as u8;
lock.push(port);
let name = format!("sd{}", (n + b'a') as char);
let blk = NgBlockDeviceWrapper::new(port);
devfs::add_named_block_device(blk, name.clone()).ok();
probe_partitions(blk, move |index, partition| {
devfs::add_block_device_partition(name.clone(), index, partition)
})
.ok();
}
}
log::debug!("All ports initialized");
@@ -162,7 +179,7 @@ impl AhciController {
}
impl InterruptHandler for AhciController {
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
fn handle_irq(&self, _vector: Option<usize>) -> bool {
let regs = self.regs.lock();
let is = regs.IS.get();
@@ -171,7 +188,7 @@ impl InterruptHandler for AhciController {
// Clear global interrupt status
regs.IS.set(u32::MAX);
for port in ports {
for &port in ports {
if is & (1 << port.index) != 0 {
port.handle_pending_interrupts();
}
@@ -184,106 +201,58 @@ impl InterruptHandler for AhciController {
}
impl Device for AhciController {
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
unsafe fn init(&'static self) -> Result<(), Error> {
// Do the init in background
runtime::spawn(self.late_init())?;
Ok(())
}
fn display_name(&self) -> &str {
"AHCI Controller"
fn display_name(&self) -> &'static str {
"AHCI SATA Controller"
}
}
pub fn register_sata_drive(drive: Arc<AhciPort>, probe: bool) {
let index = {
let mut drives = SATA_DRIVES.lock();
let index = drives.len();
if index >= MAX_DRIVES {
log::error!("Cannot add a SATA drive: too many of them");
return;
}
drives.push(drive.clone());
index
};
let letter = (index as u8 + b'a') as char;
static SATA_DRIVES: IrqSafeSpinlock<Vec<&'static AhciPort>> = IrqSafeSpinlock::new(Vec::new());
let name = format!("sd{letter}");
log::info!("Register SATA drive: {name}");
pub fn probe(info: &PciDeviceInfo) -> Result<&'static dyn Device, Error> {
let bar5 = info.config_space.bar(5).ok_or(Error::InvalidOperation)?;
let bar5 = bar5.as_memory().ok_or(Error::InvalidOperation)?;
devfs::add_named_block_device(drive.clone(), name.clone(), FileMode::new(0o600)).ok();
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
info.config_space.set_command(cmd.bits());
if probe {
runtime::spawn(async move {
let name = name;
log::info!("Probing partitions for {name}");
probe_partitions(drive, |index, partition| {
let partition_name = format!("{name}{}", index + 1);
devfs::add_named_block_device(
Arc::new(partition),
partition_name,
FileMode::new(0o600),
)
.ok();
})
.await
.ok();
})
.ok();
}
}
static SATA_DRIVES: IrqSafeSpinlock<Vec<Arc<AhciPort>>> = IrqSafeSpinlock::new(Vec::new());
pci_driver! {
matches: [class (0x01:0x06:0x01)],
driver: {
fn driver_name(&self) -> &str {
"ahci"
}
fn probe(&self, info: &PciDeviceInfo, dma: &Arc<dyn DmaAllocator>) -> Result<Arc<dyn Device>, Error> {
let bar5 = info.config_space.bar(5).ok_or(Error::InvalidOperation)?;
let bar5 = bar5.as_memory().ok_or(Error::InvalidOperation)?;
let mut cmd = PciCommandRegister::from_bits_retain(info.config_space.command());
cmd &= !(PciCommandRegister::DISABLE_INTERRUPTS | PciCommandRegister::ENABLE_IO);
cmd |= PciCommandRegister::ENABLE_MEMORY | PciCommandRegister::BUS_MASTER;
info.config_space.set_command(cmd.bits());
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
// // TODO support regular PCI interrupts (ACPI dependency)
// let Some(mut msi) = info.config_space.capability::<MsiCapability>() else {
// log::warn!("Ignoring AHCI: does not support MSI (and the OS doesn't yet support PCI IRQ)");
// return Err(Error::InvalidOperation);
// };
// Map the registers
let regs = unsafe { DeviceMemoryIo::<Regs>::map(bar5, Default::default()) }?;
let version = Version::try_from(regs.VS.get())?;
let ahci_only = regs.CAP.matches_all(CAP::SAM::SET);
let max_port_count = regs.CAP.read(CAP::NP) as usize;
let has_64_bit = regs.CAP.matches_all(CAP::S64A::SET);
// TODO extract Number of Command Slots
let ahci = Arc::new(AhciController {
regs: IrqSafeSpinlock::new(regs),
dma: dma.clone(),
ports: OneTimeInit::new(),
received_fis_buffers: OneTimeInit::new(),
version,
max_port_count,
ahci_only,
has_64_bit,
});
// TODO use multiple vectors if capable
info.map_interrupt(InterruptAffinity::Any, ahci.clone())?;
Ok(ahci)
}
}
info.init_interrupts(PreferredInterruptMode::Msi)?;
// // TODO support regular PCI interrupts (ACPI dependency)
// let Some(mut msi) = info.config_space.capability::<MsiCapability>() else {
// log::warn!("Ignoring AHCI: does not support MSI (and the OS doesn't yet support PCI IRQ)");
// return Err(Error::InvalidOperation);
// };
// Map the registers
let regs = unsafe { DeviceMemoryIo::<Regs>::map(bar5, Default::default()) }?;
let version = Version::try_from(regs.VS.get())?;
let ahci_only = regs.CAP.matches_all(CAP::SAM::SET);
let max_port_count = regs.CAP.read(CAP::NP) as usize;
let has_64_bit = regs.CAP.matches_all(CAP::S64A::SET);
// TODO extract Number of Command Slots
let ahci = Box::leak(Box::new(AhciController {
regs: IrqSafeSpinlock::new(regs),
ports: OneTimeInit::new(),
received_fis_buffers: OneTimeInit::new(),
version,
max_port_count,
ahci_only,
has_64_bit,
}));
// TODO use multiple vectors if capable
info.map_interrupt(InterruptAffinity::Any, ahci)?;
Ok(ahci)
}
+37 -103
View File
@@ -5,20 +5,12 @@ use core::{
task::{Context, Poll},
};
use alloc::{boxed::Box, string::String, sync::Arc};
use alloc::{boxed::Box, string::String};
use async_trait::async_trait;
use bytemuck::Zeroable;
use device_api::{device::Device, dma::DmaAllocator};
use futures_util::task::AtomicWaker;
use libk::{
device::block::BlockDevice,
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
error::Error,
};
use libk_mm::{
address::PhysicalAddress, device::DeviceMemoryIo, table::MapAttributes, OnDemandPage,
PageProvider, VirtualPage,
};
use libk::vfs::block::NgBlockDevice;
use libk_mm::{address::AsPhysicalAddress, device::DeviceMemoryIo, PageBox, PageSlice};
use libk_util::{sync::IrqSafeSpinlock, waker::QueueWaker, OneTimeInit};
use tock_registers::interfaces::{Readable, Writeable};
@@ -39,8 +31,8 @@ struct PortInner {
regs: DeviceMemoryIo<'static, PortRegs>,
#[allow(unused)]
received_fis: DmaBuffer<ReceivedFis>,
command_list: DmaBuffer<[CommandListEntry]>,
received_fis: PageBox<ReceivedFis>,
command_list: PageBox<[CommandListEntry]>,
}
pub struct PortInfo {
@@ -52,7 +44,7 @@ pub struct PortInfo {
#[allow(unused)]
pub struct AhciPort {
inner: IrqSafeSpinlock<PortInner>,
ahci: Arc<AhciController>,
ahci: &'static AhciController,
ty: PortType,
pub(crate) index: usize,
info: OneTimeInit<PortInfo>,
@@ -92,16 +84,18 @@ impl Drop for SubmittedCommand<'_> {
impl PortInner {
fn submit_command<C: AtaCommand>(
&mut self,
dma: &dyn DmaAllocator,
index: usize,
command: &C,
) -> Result<(), AhciError> {
let list_entry = &mut self.command_list[index];
let mut table_entry =
DmaBuffer::new(dma, CommandTable::zeroed()).map_err(AhciError::MemoryError)?;
PageBox::new(CommandTable::zeroed()).map_err(AhciError::MemoryError)?;
table_entry.setup_command(command)?;
*list_entry = CommandListEntry::new(table_entry.bus_address(), command.prd_count())?;
*list_entry = CommandListEntry::new(
unsafe { table_entry.as_physical_address() },
command.prd_count(),
)?;
// Sync before send
// XXX do this properly
@@ -126,9 +120,9 @@ impl PortInner {
impl AhciPort {
pub fn create(
regs: DeviceMemoryIo<'static, PortRegs>,
ahci: Arc<AhciController>,
ahci: &'static AhciController,
index: usize,
) -> Result<Arc<Self>, AhciError> {
) -> Result<&'static Self, AhciError> {
log::debug!("Initialize port {}", index);
regs.stop()?;
@@ -137,14 +131,12 @@ impl AhciPort {
return Err(AhciError::DeviceError);
}
let received_fis =
DmaBuffer::new(&*ahci.dma, ReceivedFis::zeroed()).map_err(AhciError::MemoryError)?;
let command_list =
DmaBuffer::new_slice(&*ahci.dma, CommandListEntry::zeroed(), COMMAND_LIST_LENGTH)
.map_err(AhciError::MemoryError)?;
let received_fis = PageBox::new(ReceivedFis::zeroed()).map_err(AhciError::MemoryError)?;
let command_list = PageBox::new_slice(CommandListEntry::zeroed(), COMMAND_LIST_LENGTH)
.map_err(AhciError::MemoryError)?;
regs.set_received_fis_address_64(received_fis.bus_address());
regs.set_command_list_address_64(command_list.bus_address());
regs.set_received_fis_address_64(unsafe { received_fis.as_physical_address() });
regs.set_command_list_address_64(unsafe { command_list.as_physical_address() });
regs.IE.write(
IE::DPE::SET
@@ -168,7 +160,7 @@ impl AhciPort {
let command_available = QueueWaker::new();
let command_allocation = IrqSafeSpinlock::new(0);
let port = Arc::new(Self {
Ok(Box::leak(Box::new(Self {
inner: IrqSafeSpinlock::new(inner),
ty: PortType::Sata,
info: OneTimeInit::new(),
@@ -178,15 +170,11 @@ impl AhciPort {
command_completion,
command_allocation,
command_available,
});
Ok(port)
})))
}
pub async fn init_inner(&self) -> Result<(), AhciError> {
let identify = self
.perform_command(AtaIdentify::create(&*self.ahci.dma)?)
.await?;
pub async fn init(&'static self) -> Result<(), AhciError> {
let identify = self.perform_command(AtaIdentify::create()?).await?;
let model = identify.model_number.to_string();
let serial = identify.serial_number.to_string();
@@ -235,17 +223,13 @@ 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);
}
let index = self.allocate_command().await;
if let Err(error) = self
.inner
.lock()
.submit_command(&*self.ahci.dma, index, command)
{
if let Err(error) = self.inner.lock().submit_command(index, command) {
self.free_command(index);
return Err(error);
}
@@ -309,48 +293,32 @@ impl AhciPort {
}
#[async_trait]
impl BlockDevice for AhciPort {
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
DmaBuffer::new_uninit_slice(&*self.ahci.dma, size)
}
impl NgBlockDevice for AhciPort {
type Error = AhciError;
async fn read_aligned(
async fn read(
&self,
position: u64,
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
) -> Result<(), Error> {
lba: u64,
buffer: &mut PageSlice<MaybeUninit<u8>>,
) -> Result<(), AhciError> {
if buffer.len() % SECTOR_SIZE != 0 {
log::warn!("ahci: misaligned buffer size: {}", buffer.len());
return Err(Error::InvalidOperation);
}
if position % SECTOR_SIZE as u64 != 0 {
log::warn!("ahci: misaligned read");
return Err(Error::InvalidOperation);
return Err(AhciError::InvalidBufferSize(buffer.len()));
}
let lba = position / SECTOR_SIZE as u64;
let lba_count = buffer.len() / SECTOR_SIZE;
if lba + lba_count as u64 >= self.block_count() {
log::warn!("ahci: read crosses medium end");
return Err(Error::InvalidOperation);
}
let command = AtaReadDmaEx::new(lba, lba_count, buffer);
self.submit(&command).await?.wait_for_completion().await?;
Ok(())
let command = AtaReadDmaEx::new(lba, buffer.len() / SECTOR_SIZE, buffer);
self.submit(&command).await?.wait_for_completion().await
}
async fn write_aligned(&self, _position: u64, _buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
// TODO AtaWriteDmaEx
Err(Error::NotImplemented)
async fn write(&self, _lba: u64, _buffer: &PageSlice<u8>) -> Result<(), AhciError> {
// TODO AtaDmaWriteEx
Err(AhciError::FeatureNotImplemented)
}
fn block_size(&self) -> usize {
SECTOR_SIZE
}
fn block_count(&self) -> u64 {
fn block_count(&self) -> usize {
self.info().as_ref().map(|i| i.lba_count).unwrap() as _
}
@@ -358,37 +326,3 @@ impl BlockDevice for AhciPort {
(MAX_PRD_SIZE * 2) / SECTOR_SIZE
}
}
impl Device for AhciPort {
fn display_name(&self) -> &str {
"AHCI SATA Drive"
}
}
impl PageProvider for AhciPort {
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(
&self,
_offset: u64,
_src_phys: PhysicalAddress,
_src_attrs: MapAttributes,
) -> Result<PhysicalAddress, Error> {
unimplemented!()
}
}
+5 -5
View File
@@ -1,4 +1,4 @@
use libk::dma::BusAddress;
use libk_mm::address::PhysicalAddress;
use tock_registers::{
interfaces::{ReadWriteable, Readable, Writeable},
register_bitfields, register_structs,
@@ -141,14 +141,14 @@ impl PortRegs {
Ok(())
}
pub fn set_received_fis_address_64(&self, address: BusAddress) {
let address: u64 = address.into_u64();
pub fn set_received_fis_address_64(&self, address: PhysicalAddress) {
let address: u64 = address.into();
self.FB.set(address as u32);
self.FBU.set((address >> 32) as u32);
}
pub fn set_command_list_address_64(&self, address: BusAddress) {
let address: u64 = address.into_u64();
pub fn set_command_list_address_64(&self, address: PhysicalAddress) {
let address: u64 = address.into();
self.CLB.set(address as u32);
self.CLBU.set((address >> 32) as u32);
}
-1
View File
@@ -10,7 +10,6 @@ libk-util.workspace = true
libk-mm.workspace = true
libk.workspace = true
device-api = { workspace = true, features = ["derive"] }
kernel-arch.workspace = true
ygg_driver_pci = { path = "../../bus/pci" }
kernel-fs = { path = "../../fs/kernel-fs" }

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