Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b2f3ab1c7f | |||
| 6342f0fe07 |
Generated
+83
-2
@@ -532,6 +532,12 @@ dependencies = [
|
||||
"hashbrown 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "elf"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b"
|
||||
|
||||
[[package]]
|
||||
name = "encode_unicode"
|
||||
version = "1.0.0"
|
||||
@@ -735,6 +741,18 @@ dependencies = [
|
||||
"pin-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gentables"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
"clap",
|
||||
"elf 0.7.4",
|
||||
"memtables",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
@@ -1101,6 +1119,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"kernel-arch-aarch64",
|
||||
"kernel-arch-hosted",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86_64",
|
||||
@@ -1116,6 +1135,7 @@ dependencies = [
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
@@ -1131,6 +1151,21 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-i686"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-x86",
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel-arch-interface"
|
||||
version = "0.1.0"
|
||||
@@ -1149,6 +1184,7 @@ dependencies = [
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
@@ -1176,6 +1212,7 @@ dependencies = [
|
||||
"kernel-arch-x86",
|
||||
"libk-mm-interface",
|
||||
"log",
|
||||
"memtables",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
"yggdrasil-abi",
|
||||
@@ -1233,7 +1270,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-queue",
|
||||
"device-api",
|
||||
"elf",
|
||||
"elf 0.7.2",
|
||||
"futures-util",
|
||||
"kernel-arch",
|
||||
"libc",
|
||||
@@ -1428,6 +1465,14 @@ dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memtables"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.4"
|
||||
@@ -2663,6 +2708,24 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_intel_hda"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_sound_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_core"
|
||||
version = "0.1.0"
|
||||
@@ -2791,6 +2854,20 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_sound_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_usb"
|
||||
version = "0.1.0"
|
||||
@@ -2929,12 +3006,13 @@ dependencies = [
|
||||
"device-api",
|
||||
"device-api-macros",
|
||||
"device-tree",
|
||||
"elf",
|
||||
"elf 0.7.2",
|
||||
"ext2",
|
||||
"futures-util",
|
||||
"git-version",
|
||||
"kernel-arch",
|
||||
"kernel-arch-aarch64",
|
||||
"kernel-arch-i686",
|
||||
"kernel-arch-interface",
|
||||
"kernel-arch-riscv64",
|
||||
"kernel-arch-x86",
|
||||
@@ -2944,6 +3022,7 @@ dependencies = [
|
||||
"libk-util",
|
||||
"log",
|
||||
"memfs",
|
||||
"memtables",
|
||||
"prettyplease",
|
||||
"static_assertions",
|
||||
"tock-registers",
|
||||
@@ -2953,6 +3032,7 @@ dependencies = [
|
||||
"ygg_driver_ahci",
|
||||
"ygg_driver_fat32",
|
||||
"ygg_driver_input",
|
||||
"ygg_driver_intel_hda",
|
||||
"ygg_driver_net_core",
|
||||
"ygg_driver_net_igbe",
|
||||
"ygg_driver_net_loopback",
|
||||
@@ -2960,6 +3040,7 @@ dependencies = [
|
||||
"ygg_driver_net_stmmac",
|
||||
"ygg_driver_nvme",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_sound_core",
|
||||
"ygg_driver_usb",
|
||||
"ygg_driver_usb_xhci",
|
||||
"ygg_driver_virtio_blk",
|
||||
|
||||
@@ -11,6 +11,7 @@ exclude = [
|
||||
]
|
||||
members = [
|
||||
"xtask",
|
||||
"kernel/tools/gentables",
|
||||
"kernel",
|
||||
"lib/abi",
|
||||
"lib/libyalloc",
|
||||
@@ -66,6 +67,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"
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#![allow(unused)]
|
||||
#![no_std]
|
||||
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
|
||||
+38
-111
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
+22
-32
@@ -16,7 +16,7 @@ use log::{debug, error, info};
|
||||
use uefi::{
|
||||
prelude::*,
|
||||
proto::{
|
||||
console::gop::{self, GraphicsOutput, PixelFormat},
|
||||
console::gop::{GraphicsOutput, PixelFormat},
|
||||
device_path::DevicePath,
|
||||
loaded_image::LoadedImage,
|
||||
media::{file::Directory, fs::SimpleFileSystem},
|
||||
@@ -32,31 +32,23 @@ use yboot_proto::{
|
||||
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() {
|
||||
@@ -65,8 +57,8 @@ fn setup_framebuffer(bs: &BootServices, fb: &mut FramebufferOption) -> Result<()
|
||||
_ => 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 _;
|
||||
@@ -93,7 +85,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();
|
||||
|
||||
@@ -113,7 +105,7 @@ fn load_kernel<'a>(
|
||||
config: &Config,
|
||||
root: &mut Directory,
|
||||
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"))?;
|
||||
@@ -183,14 +175,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,7 +208,7 @@ unsafe fn map_and_enter_kernel(
|
||||
let cr3 = mem::map_image();
|
||||
asm!("cli; wbinvd; mov {0}, %cr3", in(reg) cr3, options(att_syntax));
|
||||
|
||||
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, in("ecx") load_base, options(noreturn, att_syntax));
|
||||
asm!("jmp *{0}", in(reg) entry, in("eax") LOADER_MAGIC, options(noreturn, att_syntax));
|
||||
}
|
||||
|
||||
#[entry]
|
||||
@@ -244,16 +235,15 @@ fn efi_main(image_handle: Handle, mut system_table: SystemTable<Boot>) -> Status
|
||||
}
|
||||
};
|
||||
|
||||
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) = match load_kernel(&config, &mut root, &system_table) {
|
||||
Ok(e) => e,
|
||||
Err(error) => {
|
||||
error!("Failed to load the kernel/initrd: {error:?}");
|
||||
return Status::LOAD_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
map_and_enter_kernel(system_table, proto_data, mmap_memory, load_base, entry);
|
||||
map_and_enter_kernel(system_table, proto_data, mmap_memory, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
|
||||
+38
-79
@@ -3,96 +3,55 @@
|
||||
|
||||
Booting Yggdrasil on Raspberry Pi 4B with u-boot:
|
||||
|
||||
1. Clone u-boot sources and build with rpi_4_defconfig
|
||||
1. Clone u-boot sources to some directory and checkout some
|
||||
stable branch. I've used v2024.10.
|
||||
2. Modify cmd/boot.c by replacing the do_go_exec function:
|
||||
|
||||
/* Allow ports to override the default behavior */
|
||||
__attribute__((weak))
|
||||
unsigned long do_go_exec(ulong (*entry)(int, char * const []), int argc,
|
||||
char *const argv[])
|
||||
{
|
||||
void *entry_ptr = (void *) entry;
|
||||
ulong fdt_addr_r = 0;
|
||||
if (argc >= 2) {
|
||||
fdt_addr_r = hextoul(argv[1], NULL);
|
||||
}
|
||||
void (*func)(ulong) = entry_ptr;
|
||||
func(fdt_addr_r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
3. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 rpi_4_defconfig
|
||||
4. make CROSS_COMPILE=aarch64-linux-gnu- ARCH=arm64 -j
|
||||
5. Copy u-boot.bin into your Pi SD-card's boot partition.
|
||||
**NOTE** I assume you have all the bootloader parts in the boot partition already.
|
||||
If not, clone raspberry fw repo and copy the following files to the boot partition:
|
||||
* bootcode.bin
|
||||
* start4.elf
|
||||
* all the .dtb files (a bcm2711-rpi-4-b.dtb should be enough though)
|
||||
|
||||
2. Copy u-boot.bin into the Pi's boot partition and edit the config.txt:
|
||||
6. 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
|
||||
7. Compile the OS with `cargo xtask --arch=aarch64 --board=raspi4b --release`
|
||||
8. Copy the following files into some directory:
|
||||
* target/aarch64-unknown-raspi4b/release/yggdrasil-kernel
|
||||
* userspace/target/aarch64-unknown-yggdrasil/release/initrd.tar
|
||||
5. cd into that directory and start a TFTP server of your choice. I used `uftpd`.
|
||||
9. 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:
|
||||
10. 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}
|
||||
tftpboot 0x04000000 <YOUR IP>:initrd.tar
|
||||
tftpboot ${loadaddr} <YOUR IP>:yggdrasil-kernel
|
||||
load mmc 0:1 ${fdt_addr_r} bcm2711-rpi-4-b.dtb
|
||||
fdt addr ${fdt_addr_r}
|
||||
fdt resize
|
||||
fdt memory 0x0 0x3C000000
|
||||
fdt chosen 0x04000000 <WHATEVER SIZE WAS PRINTED WHEN RUNNING THE FIRST COMMAND>
|
||||
bootelf -p
|
||||
go ${kernel_addr_r} ${fdt_addr_r}
|
||||
|
||||
###### 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")
|
||||
11. Yggdrasil OS should start!
|
||||
|
||||
+14
-114
@@ -1,6 +1,7 @@
|
||||
Booting Yggdrasil OS on Starfive VisionFive 2 RISC-V board:
|
||||
|
||||
* TODO: proper format for initrd image
|
||||
* TODO: 0x70000000 can be replaced with a builtin var?
|
||||
|
||||
Prerequisites:
|
||||
|
||||
@@ -12,119 +13,18 @@ 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
|
||||
3. Run the following commands:
|
||||
|
||||
# Get an IP address
|
||||
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")
|
||||
# [Optional] set some kernel cmdline params
|
||||
setenv bootargs "debug.serial-level=info"
|
||||
# Load initrd
|
||||
tftpboot 0x70000000 <your-ip-address>:initrd.img
|
||||
# Load kernel
|
||||
tftpboot ${loadaddr} <your-ip-address>:yggdrasil-kernel.bin
|
||||
# Load dtb
|
||||
load mmc 1:3 ${fdt_addr_r} dtbs/...-starfive/starfive/${fdtfile}
|
||||
fdt resize
|
||||
# Enter the kernel
|
||||
booti ${loadaddr} 0x70000000:<initrd-size> ${fdt_addr_r}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"position-independent-executables": true,
|
||||
"eh-frame-header": false,
|
||||
|
||||
"linker": "rust-lld",
|
||||
|
||||
Binary file not shown.
@@ -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>;
|
||||
};
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"arch": "x86",
|
||||
"cpu": "pentium4",
|
||||
"os": "none",
|
||||
"abi": "softfloat",
|
||||
"llvm-target": "i686-unknown-linux-gnu",
|
||||
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
||||
"max-atomic-width": 64,
|
||||
"target-pointer-width": "32",
|
||||
"features": "-avx,-sse,+soft-float",
|
||||
|
||||
"executables": true,
|
||||
"stack-probes": {
|
||||
"kind": "inline"
|
||||
},
|
||||
"dynamic-linking": true,
|
||||
"panic-strategy": "abort",
|
||||
"relocation-model": "pic",
|
||||
|
||||
"has-thread-local": false,
|
||||
|
||||
"supported-split-debuginfo": [
|
||||
"packed",
|
||||
"unpacked",
|
||||
"off"
|
||||
],
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld"
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
ENTRY(__aarch64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x40080000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -0,0 +1,55 @@
|
||||
ENTRY(__aarch64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x80000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFF8000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
ENTRY(__rv64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x40200000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
ENTRY(__rv64_entry);
|
||||
|
||||
KERNEL_PHYS_BASE = 0x80200000;
|
||||
KERNEL_VIRT_OFFSET = 0xFFFFFFF000000000;
|
||||
|
||||
SECTIONS {
|
||||
. = KERNEL_PHYS_BASE;
|
||||
PROVIDE(__kernel_start = . + KERNEL_VIRT_OFFSET);
|
||||
|
||||
.text.entry : {
|
||||
*(.text.entry)
|
||||
}
|
||||
|
||||
. = ALIGN(16);
|
||||
. = . + KERNEL_VIRT_OFFSET;
|
||||
|
||||
.text : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.text.vectors));
|
||||
*(.text*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.rodata : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.rodata*)
|
||||
*(.eh_frame*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data.tables : AT (. - KERNEL_VIRT_OFFSET) {
|
||||
KEEP(*(.data.tables))
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
.data : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(.data*)
|
||||
. = ALIGN(8);
|
||||
/* PROVIDE(__global_pointer = . + 0x800 - KERNEL_VIRT_OFFSET); */
|
||||
|
||||
. = ALIGN(16);
|
||||
PROVIDE(__init_array_start = .);
|
||||
KEEP(*(.init_array*))
|
||||
PROVIDE(__init_array_end = .);
|
||||
|
||||
*(.got*)
|
||||
}
|
||||
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_start_phys = . - KERNEL_VIRT_OFFSET);
|
||||
.bss : AT(. - KERNEL_VIRT_OFFSET) {
|
||||
*(COMMON)
|
||||
*(.bss*)
|
||||
}
|
||||
. = ALIGN(4K);
|
||||
PROVIDE(__bss_end_phys = . - KERNEL_VIRT_OFFSET);
|
||||
PROVIDE(__bss_size = __bss_end_phys - __bss_start_phys);
|
||||
|
||||
PROVIDE(__kernel_end = .);
|
||||
};
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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 = .);
|
||||
};
|
||||
@@ -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 = .);
|
||||
};
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"position-independent-executables": true,
|
||||
"code-model": "medium",
|
||||
"eh-frame-header": false,
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"panic-strategy": "abort",
|
||||
"dynamic-linking": true,
|
||||
"relocation-model": "pic",
|
||||
"position-independent-executables": true,
|
||||
|
||||
"has-thread-local": false,
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ chrono.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
device-api-macros.workspace = true
|
||||
|
||||
memtables.workspace = true
|
||||
vmalloc.workspace = true
|
||||
kernel-arch.workspace = true
|
||||
|
||||
@@ -35,6 +36,7 @@ ygg_driver_ahci = { path = "driver/block/ahci" }
|
||||
ygg_driver_input = { path = "driver/input" }
|
||||
ygg_driver_usb_xhci.path = "driver/usb/xhci"
|
||||
ygg_driver_net_rtl81xx.path = "driver/net/rtl81xx"
|
||||
ygg_driver_sound_core.path = "driver/sound/core"
|
||||
|
||||
memfs = { path = "driver/fs/memfs" }
|
||||
ext2 = { path = "driver/fs/ext2" }
|
||||
@@ -69,9 +71,14 @@ kernel-arch-x86.workspace = true
|
||||
|
||||
ygg_driver_acpi.path = "driver/acpi"
|
||||
ygg_driver_net_igbe.path = "driver/net/igbe"
|
||||
ygg_driver_intel_hda.path = "driver/sound/intel-hda"
|
||||
|
||||
acpi.workspace = true
|
||||
|
||||
[target.'cfg(target_arch = "x86")'.dependencies]
|
||||
kernel-arch-i686.workspace = true
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
abi-generator.workspace = true
|
||||
|
||||
@@ -82,6 +89,7 @@ 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
|
||||
@@ -93,5 +101,12 @@ ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
default = ["fb_console"]
|
||||
fb_console = []
|
||||
|
||||
# TODO replace this with a better configuration mechanism
|
||||
aarch64_board_virt = ["kernel-arch-aarch64/aarch64_board_virt"]
|
||||
aarch64_board_raspi4b = ["kernel-arch-aarch64/aarch64_board_raspi4b"]
|
||||
|
||||
riscv64_board_virt = ["kernel-arch-riscv64/riscv64_board_virt"]
|
||||
riscv64_board_jh7110 = ["kernel-arch-riscv64/riscv64_board_jh7110"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -9,6 +9,9 @@ kernel-arch-x86_64.path = "x86_64"
|
||||
[target.'cfg(all(target_os = "none", target_arch = "aarch64"))'.dependencies]
|
||||
kernel-arch-aarch64.path = "aarch64"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "x86"))'.dependencies]
|
||||
kernel-arch-i686.path = "i686"
|
||||
|
||||
[target.'cfg(all(target_os = "none", target_arch = "riscv64"))'.dependencies]
|
||||
kernel-arch-riscv64.path = "riscv64"
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -17,5 +18,10 @@ tock-registers.workspace = true
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
aarch64_board_virt = []
|
||||
aarch64_board_raspi4b = []
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(decl_macro)]
|
||||
#![feature(naked_functions, decl_macro)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -40,9 +40,11 @@ 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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,108 @@
|
||||
#![allow(clippy::missing_safety_doc)]
|
||||
use core::{
|
||||
alloc::Layout,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::atomic::{self, AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
use aarch64_cpu::{
|
||||
asm::barrier,
|
||||
registers::{MAIR_EL1, SCTLR_EL1, TCR_EL1},
|
||||
registers::{MAIR_EL1, PAR_EL1, SCTLR_EL1, TTBR0_EL1, TTBR1_EL1},
|
||||
};
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
sync::IrqSafeSpinlock,
|
||||
KERNEL_VIRT_OFFSET,
|
||||
split_spinlock, Architecture, 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::{ReadWriteable, Readable, Writeable};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub use intrinsics::*;
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
use crate::{mem::table::L1, ArchitectureImpl};
|
||||
use self::table::{PageAttributes, PageEntry, PageTable, L1, L2, L3};
|
||||
|
||||
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;
|
||||
|
||||
#[cfg(any(feature = "aarch64_board_virt", rust_analyzer))]
|
||||
const KERNEL_PHYS_BASE: usize = 0x40080000;
|
||||
#[cfg(any(feature = "aarch64_board_raspi4b", rust_analyzer))]
|
||||
const KERNEL_PHYS_BASE: usize = 0x80000;
|
||||
|
||||
// Precomputed mappings
|
||||
const KERNEL_L1_INDEX: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const KERNEL_START_L2_INDEX: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const KERNEL_END_L2_INDEX: usize = KERNEL_START_L2_INDEX + KERNEL_L3_COUNT;
|
||||
|
||||
// Must not be zero, should be at 4MiB
|
||||
const_assert_eq!(KERNEL_START_L2_INDEX, 0);
|
||||
// From static mapping
|
||||
#[cfg(any(feature = "aarch64_board_raspi4b", rust_analyzer))]
|
||||
const_assert_eq!(KERNEL_L1_INDEX, 0);
|
||||
#[cfg(any(feature = "aarch64_board_virt", rust_analyzer))]
|
||||
const_assert_eq!(KERNEL_L1_INDEX, 1);
|
||||
|
||||
// Runtime mappings
|
||||
// 2MiB max
|
||||
const EARLY_MAPPING_L2I: usize = KERNEL_END_L2_INDEX + 1;
|
||||
// 1GiB max
|
||||
const DEVICE_MAPPING_L1I: usize = KERNEL_L1_INDEX + 2;
|
||||
const DEVICE_MAPPING_L3_COUNT: usize = 4;
|
||||
// 16GiB max
|
||||
const RAM_MAPPING_START_L1I: usize = KERNEL_L1_INDEX + 3;
|
||||
pub const RAM_MAPPING_L1_COUNT: usize = 16;
|
||||
|
||||
// 2MiB for early mappings
|
||||
const EARLY_MAPPING_OFFSET: usize =
|
||||
MAPPING_OFFSET | (KERNEL_L1_INDEX * L1::SIZE) | (EARLY_MAPPING_L2I * L2::SIZE);
|
||||
static mut EARLY_MAPPING_L3: PageTable<L3> = PageTable::zeroed();
|
||||
// 1GiB for device MMIO mapping
|
||||
const DEVICE_MAPPING_OFFSET: usize = MAPPING_OFFSET | (DEVICE_MAPPING_L1I * L1::SIZE);
|
||||
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
|
||||
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
|
||||
[PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT];
|
||||
// 16GiB for RAM mapping
|
||||
pub const RAM_MAPPING_OFFSET: usize = MAPPING_OFFSET | (RAM_MAPPING_START_L1I * L1::SIZE);
|
||||
pub static MEMORY_LIMIT: AtomicUsize = AtomicUsize::new(0);
|
||||
|
||||
split_spinlock! {
|
||||
use crate::ArchitectureImpl;
|
||||
use crate::mem::FixedTables;
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> =
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
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 +110,442 @@ 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_OUTER
|
||||
| PageAttributes::PAGE_ATTR_NORMAL
|
||||
| PageAttributes::PRESENT
|
||||
}
|
||||
|
||||
// Early mappings
|
||||
unsafe fn map_early_pages(physical: PhysicalAddress, count: usize) -> Result<usize, Error> {
|
||||
for l3i in 0..512 {
|
||||
let mut taken = false;
|
||||
for i in 0..count {
|
||||
if EARLY_MAPPING_L3[i + l3i].is_present() {
|
||||
taken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if taken {
|
||||
continue;
|
||||
}
|
||||
|
||||
for i in 0..count {
|
||||
let page = physical.add(i * L3::SIZE);
|
||||
// TODO NX, NC
|
||||
EARLY_MAPPING_L3[i + l3i] = PageEntry::normal_page(page, PageAttributes::empty());
|
||||
tlb_flush_vaae1(EARLY_MAPPING_OFFSET + (l3i + i) * L3::SIZE);
|
||||
}
|
||||
|
||||
return Ok(EARLY_MAPPING_OFFSET + l3i * L3::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
unsafe fn unmap_early_page(address: usize) {
|
||||
if !(EARLY_MAPPING_OFFSET..EARLY_MAPPING_OFFSET + L2::SIZE).contains(&address) {
|
||||
panic!("Tried to unmap invalid early mapping: {:#x}", address);
|
||||
}
|
||||
|
||||
let l3i = (address - EARLY_MAPPING_OFFSET).page_index::<L3>();
|
||||
|
||||
assert!(EARLY_MAPPING_L3[l3i].is_present());
|
||||
EARLY_MAPPING_L3[l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be used by the architecture initialization functions.
|
||||
pub unsafe fn map_ram_l1(index: usize) {
|
||||
if index >= RAM_MAPPING_L1_COUNT {
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
let table_index = index + RAM_MAPPING_START_L1I;
|
||||
|
||||
if tables.l1.data[table_index] != 0 {
|
||||
ArchitectureImpl::halt();
|
||||
}
|
||||
|
||||
tables.l1.data[table_index] = ((index * L1::SIZE) as u64) | ram_block_flags().bits();
|
||||
tlb_flush_vaae1(RAM_MAPPING_OFFSET + index * L1::SIZE);
|
||||
}
|
||||
|
||||
// Device mappings
|
||||
unsafe fn map_device_memory_l3(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
// TODO don't map pages if already mapped
|
||||
|
||||
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
// TODO NX, NC
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::device_page(base.add(j * L3::SIZE));
|
||||
tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + l2i * L2::SIZE + l3i * L3::SIZE);
|
||||
}
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
unsafe fn map_device_memory_l2(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
|
||||
for j in 0..count {
|
||||
if DEVICE_MAPPING_L2[i + j].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
DEVICE_MAPPING_L2[i + j] = PageEntry::<L2>::device_block(base.add(j * L2::SIZE));
|
||||
tlb_flush_vaae1(DEVICE_MAPPING_OFFSET + (i + j) * L2::SIZE);
|
||||
}
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn map_device_memory(
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
|
||||
let l3_aligned = base.page_align_down::<L3>();
|
||||
let l3_offset = base.page_offset::<L3>();
|
||||
let page_count = (l3_offset + size).page_count::<L3>();
|
||||
|
||||
if page_count > 256 {
|
||||
// Large mapping, use L2 mapping instead
|
||||
let l2_aligned = base.page_align_down::<L2>();
|
||||
let l2_offset = base.page_offset::<L2>();
|
||||
let page_count = (l2_offset + size).page_count::<L2>();
|
||||
|
||||
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
|
||||
let address = base_address + l2_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l2_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L2::SIZE,
|
||||
))
|
||||
} else {
|
||||
// Just map the pages directly
|
||||
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
|
||||
let address = base_address + l3_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l3_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L3::SIZE,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
|
||||
match map.page_size {
|
||||
L3::SIZE => {
|
||||
for i in 0..map.page_count {
|
||||
let page = map.base_address + i * L3::SIZE;
|
||||
let l2i = page.page_index::<L2>();
|
||||
let l3i = page.page_index::<L3>();
|
||||
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
|
||||
|
||||
tlb_flush_vaae1(page);
|
||||
}
|
||||
}
|
||||
L2::SIZE => todo!(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[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_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 auto_upper_address<T>(ptr: *const T) -> usize {
|
||||
let address = ptr.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
address + KERNEL_VIRT_OFFSET
|
||||
pub fn tlb_flush_all() {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
unsafe {
|
||||
core::arch::asm!("tlbi vmalle1is");
|
||||
}
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_vaae1(page: usize) {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
let argument = page >> 12;
|
||||
unsafe {
|
||||
core::arch::asm!("tlbi vaae1, {argument}", argument = in(reg) argument);
|
||||
}
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
pub fn at_s1e0r(input: usize) -> Option<u64> {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
unsafe {
|
||||
core::arch::asm!("at s1e0r, {address}", address = in(reg) input);
|
||||
}
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
|
||||
Some(PAR_EL1.read(PAR_EL1::PA))
|
||||
} else {
|
||||
address
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_memory_attributes() {
|
||||
pub fn at_s1e1r(input: usize) -> Option<u64> {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
unsafe {
|
||||
core::arch::asm!("at s1e1r, {address}", address = in(reg) input);
|
||||
}
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
if PAR_EL1.matches_all(PAR_EL1::F::TranslationSuccessfull) {
|
||||
Some(PAR_EL1.read(PAR_EL1::PA))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ic_iallu() {
|
||||
atomic::compiler_fence(Ordering::SeqCst);
|
||||
barrier::dsb(barrier::ISH);
|
||||
barrier::isb(barrier::SY);
|
||||
unsafe {
|
||||
core::arch::asm!("ic iallu");
|
||||
}
|
||||
barrier::isb(barrier::SY);
|
||||
}
|
||||
|
||||
pub fn dc_cvac(input: usize) {
|
||||
barrier::dsb(barrier::ISHST);
|
||||
unsafe {
|
||||
core::arch::asm!("dc cvac, {address}", address = in(reg) input);
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_address<T>(value: *const T) -> usize {
|
||||
let addr = value.addr();
|
||||
if addr < KERNEL_VIRT_OFFSET {
|
||||
// Called from lower half
|
||||
addr
|
||||
} else {
|
||||
// Called from higher-half
|
||||
addr - KERNEL_VIRT_OFFSET
|
||||
}
|
||||
}
|
||||
|
||||
/// (BSP-early init) loads precomputed kernel mapping tables for the kernel to jump to "higher-half"
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, must only be called by BSP during its early init while still in "lower-half"
|
||||
pub unsafe fn load_fixed_tables() {
|
||||
let ttbr0 = auto_address(&raw const KERNEL_TABLES) as u64;
|
||||
|
||||
TTBR0_EL1.set(ttbr0);
|
||||
TTBR1_EL1.set(ttbr0);
|
||||
}
|
||||
|
||||
/// Sets up additional translation tables for kernel usage
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Unsafe, must only be called by BSP during its early init, must already be in "higher-half"
|
||||
pub unsafe fn init_fixed_tables() {
|
||||
// TODO this could be built in compile-time too?
|
||||
let mut tables = KERNEL_TABLES.grab();
|
||||
let early_mapping_l3_phys = auto_address(&raw const EARLY_MAPPING_L3);
|
||||
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
|
||||
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
let device_mapping_l3_phys = PhysicalAddress::from_usize(
|
||||
&DEVICE_MAPPING_L3S[i] as *const _ as usize - KERNEL_VIRT_OFFSET,
|
||||
);
|
||||
DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
|
||||
}
|
||||
|
||||
assert_eq!(tables.l2.data[EARLY_MAPPING_L2I], 0);
|
||||
tables.l2.data[EARLY_MAPPING_L2I] =
|
||||
(early_mapping_l3_phys as u64) | kernel_table_flags().bits();
|
||||
tlb_flush_vaae1(EARLY_MAPPING_OFFSET);
|
||||
|
||||
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
tables.l1.data[DEVICE_MAPPING_L1I] =
|
||||
(device_mapping_l2_phys as u64) | kernel_table_flags().bits();
|
||||
|
||||
tlb_flush_all();
|
||||
}
|
||||
|
||||
pub fn setup_memory_attributes() {
|
||||
// TODO: Figure out why WriteBack_NonTransient_ReadWriteAlloc doesn't work on Pi 4B
|
||||
|
||||
MAIR_EL1.write(
|
||||
//// Attribute 0 -- normal memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::WriteBack_NonTransient +
|
||||
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
|
||||
//// Attribute 1 -- normal non-cacheable memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
|
||||
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
|
||||
//// Attribute 2 -- device memory
|
||||
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
|
||||
MAIR_EL1::Attr0_Normal_Outer::WriteBack_NonTransient +
|
||||
//// Attribute 1 -- normal non-cacheable memory
|
||||
MAIR_EL1::Attr0_Normal_Inner::NonCacheable +
|
||||
MAIR_EL1::Attr0_Normal_Outer::NonCacheable +
|
||||
//// Attribute 2 -- device memory
|
||||
MAIR_EL1::Attr1_Device::nonGathering_nonReordering_EarlyWriteAck,
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
|
||||
/// 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::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,
|
||||
);
|
||||
SCTLR_EL1.modify(SCTLR_EL1::C::Cacheable);
|
||||
|
||||
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();
|
||||
/// 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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "kernel-arch-i686"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
kernel-arch-interface.workspace = true
|
||||
libk-mm-interface.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
kernel-arch-x86.workspace = true
|
||||
|
||||
bitflags.workspace = true
|
||||
static_assertions.workspace = true
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
@@ -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
|
||||
@@ -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));
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
#![feature(never_type, naked_functions, trace_macros)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::ptr::null_mut;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::interrupt::{LocalInterruptController, MessageInterruptController};
|
||||
use kernel_arch_interface::{
|
||||
cpu::{CpuData, CpuImpl, IpiQueue},
|
||||
task::Scheduler,
|
||||
Architecture,
|
||||
};
|
||||
|
||||
pub mod context;
|
||||
pub mod gdt;
|
||||
pub mod mem;
|
||||
|
||||
pub use context::TaskContextImpl;
|
||||
use kernel_arch_x86::cpuid::CpuFeatures;
|
||||
pub use mem::{KernelTableManagerImpl, ProcessAddressSpaceImpl};
|
||||
|
||||
pub struct ArchitectureImpl;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PerCpuData {
|
||||
pub available_features: CpuFeatures,
|
||||
pub enabled_features: CpuFeatures,
|
||||
}
|
||||
|
||||
impl CpuData for PerCpuData {}
|
||||
|
||||
static mut CPU: *mut () = null_mut();
|
||||
|
||||
#[naked]
|
||||
extern "C" fn idle_task(_: usize) -> ! {
|
||||
unsafe {
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
1:
|
||||
nop
|
||||
jmp 1b
|
||||
"#,
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Architecture for ArchitectureImpl {
|
||||
type PerCpuData = PerCpuData;
|
||||
type CpuFeatures = CpuFeatures;
|
||||
type BreakpointType = u8;
|
||||
|
||||
const BREAKPOINT_VALUE: Self::BreakpointType = 0xCC;
|
||||
|
||||
unsafe fn init_local_cpu<S: Scheduler + 'static>(id: Option<u32>, data: Self::PerCpuData) {
|
||||
use alloc::boxed::Box;
|
||||
|
||||
let cpu = Box::leak(Box::new(CpuImpl::<Self, S>::new(
|
||||
id.expect("x86_64 required manual CPU ID set"),
|
||||
data,
|
||||
)));
|
||||
|
||||
cpu.set_local();
|
||||
}
|
||||
|
||||
unsafe fn set_interrupt_mask(mask: bool) -> bool {
|
||||
let old = Self::interrupt_mask();
|
||||
if mask {
|
||||
core::arch::asm!("cli");
|
||||
} else {
|
||||
core::arch::asm!("sti");
|
||||
}
|
||||
old
|
||||
}
|
||||
|
||||
fn interrupt_mask() -> bool {
|
||||
let mut flags: u32;
|
||||
unsafe {
|
||||
core::arch::asm!("pushfl; pop {0:e}", out(reg) flags, options(att_syntax));
|
||||
}
|
||||
// If IF is zero, interrupts are disabled (masked)
|
||||
flags & (1 << 9) == 0
|
||||
}
|
||||
|
||||
fn wait_for_interrupt() {
|
||||
unsafe {
|
||||
core::arch::asm!("hlt");
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn init_ipi_queues(_queues: Vec<IpiQueue<Self>>) {}
|
||||
|
||||
fn local_cpu() -> *mut () {
|
||||
unsafe { CPU }
|
||||
}
|
||||
|
||||
fn cpu_index<S: Scheduler + 'static>() -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn set_local_cpu(cpu: *mut ()) {
|
||||
CPU = cpu;
|
||||
}
|
||||
|
||||
fn cpu_count() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn local_interrupt_controller() -> Option<&'static dyn LocalInterruptController> {
|
||||
None
|
||||
}
|
||||
|
||||
fn message_interrupt_controller() -> Option<&'static dyn MessageInterruptController> {
|
||||
None
|
||||
}
|
||||
|
||||
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn idle_task() -> extern "C" fn(usize) -> ! {
|
||||
idle_task
|
||||
}
|
||||
|
||||
fn halt() -> ! {
|
||||
loop {
|
||||
unsafe {
|
||||
core::arch::asm!("cli; hlt");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cpu_available_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
Some(&cpu.available_features)
|
||||
}
|
||||
|
||||
fn cpu_enabled_features<S: Scheduler>(cpu: &CpuImpl<Self, S>) -> Option<&Self::CpuFeatures> {
|
||||
Some(&cpu.enabled_features)
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
use fixed::FixedTables;
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock, KERNEL_VIRT_OFFSET,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
table::{page_count, EntryLevel},
|
||||
};
|
||||
use table::{PageAttributes, PageEntry, L0, L3};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
pub mod fixed;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
pub use process::ProcessAddressSpaceImpl;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KernelTableManagerImpl;
|
||||
|
||||
split_spinlock! {
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
use crate::mem::FixedTables;
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> = unsafe {
|
||||
KernelImageObject::new(FixedTables::zeroed())
|
||||
};
|
||||
}
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
unsafe fn map_device_pages(
|
||||
base: u64,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<Self>, Error> {
|
||||
// TODO page align up
|
||||
|
||||
let offset = (base & 0xFFF) as usize;
|
||||
let base = base & !0xFFF;
|
||||
let end = (base + count as u64 + 0xFFF) & !0xFFF;
|
||||
|
||||
// assert_eq!(base & 0xFFF, 0);
|
||||
if end < fixed::MAX_FIXED_PHYSICAL.into_u64() {
|
||||
// 1:1
|
||||
let address = Self::virtualize(base);
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
address, address, 0, 0,
|
||||
))
|
||||
} else {
|
||||
assert_eq!(base & 0xFFF, 0);
|
||||
log::info!("map_device_pages({:#x}, {})", base, count);
|
||||
let page_count = page_count::<L3>(count);
|
||||
let virt = KERNEL_TABLES.lock().map_dynamic_memory(base, page_count)?;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
virt + offset,
|
||||
virt,
|
||||
page_count,
|
||||
0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(_mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
// todo!()
|
||||
}
|
||||
|
||||
fn virtualize(phys: u64) -> usize {
|
||||
KERNEL_TABLES
|
||||
.lock()
|
||||
.virtualize(PhysicalAddress::from_u64(phys))
|
||||
}
|
||||
|
||||
fn physicalize(virt: usize) -> u64 {
|
||||
KERNEL_TABLES
|
||||
.lock()
|
||||
.physicalize(virt)
|
||||
.expect("Invalid virtual address")
|
||||
.into_u64()
|
||||
}
|
||||
|
||||
unsafe fn unmap_physical_address(virt: usize) {
|
||||
if virt < KERNEL_VIRT_OFFSET {
|
||||
panic!("Invalid 'virtualized' address: {:#x}", virt);
|
||||
}
|
||||
let virt = virt - KERNEL_VIRT_OFFSET;
|
||||
if virt >= fixed::FIXED_MAP_COUNT << L0::SHIFT {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets up fixed MMU translation tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Only meant to be called once during early OS init.
|
||||
pub unsafe fn init_fixed_tables() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
|
||||
// Unmap lower stuff
|
||||
for (i, entry) in tables.l0.lower.iter_mut().enumerate() {
|
||||
*entry = PageEntry::INVALID;
|
||||
flush_tlb_entry(i << 22);
|
||||
}
|
||||
|
||||
// Map the rest of fixed translation
|
||||
for (i, entry) in tables.l0.kernel.iter_mut().enumerate() {
|
||||
let virt = KERNEL_VIRT_OFFSET + (i << L0::SHIFT);
|
||||
let phys = (i << L0::SHIFT) as u32;
|
||||
*entry = PageEntry::block(PhysicalAddress::from_u32(phys), PageAttributes::WRITABLE);
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
let dynamic_len = tables.l0.dynamic.len();
|
||||
for i in 0..dynamic_len {
|
||||
let phys = tables.dynamic.lock().l3s[i].as_physical_address();
|
||||
tables.l0.dynamic[i] = PageEntry::table(phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `address` must be page-aligned.
|
||||
#[inline]
|
||||
pub unsafe fn flush_tlb_entry(address: usize) {
|
||||
core::arch::asm!("invlpg ({0})", in(reg) address, options(att_syntax));
|
||||
}
|
||||
@@ -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, u64) {
|
||||
(unsafe { self.l0.as_physical_address().into_u64() }, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> ProcessAddressSpaceImpl<TA> {
|
||||
// Write a single 4KiB entry
|
||||
fn write_l3_entry(
|
||||
&mut self,
|
||||
virt: usize,
|
||||
entry: PageEntry<L3>,
|
||||
overwrite: bool,
|
||||
) -> Result<(), Error> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let mut l3 = self.l0.get_mut_or_alloc::<TA>(l0i)?;
|
||||
|
||||
if l3[l3i].is_present() && !overwrite {
|
||||
todo!();
|
||||
}
|
||||
|
||||
l3[l3i] = entry;
|
||||
unsafe {
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pop_l3_entry(&mut self, virt: usize) -> Result<PhysicalAddress, Error> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let mut l3 = self.l0.get_mut(l0i).ok_or(Error::DoesNotExist)?;
|
||||
let page = l3[l3i].as_page().ok_or(Error::DoesNotExist)?;
|
||||
|
||||
l3[l3i] = PageEntry::INVALID;
|
||||
unsafe {
|
||||
flush_tlb_entry(virt);
|
||||
}
|
||||
|
||||
Ok(page)
|
||||
}
|
||||
|
||||
fn read_l3_entry(&self, virt: usize) -> Option<(PhysicalAddress, MapAttributes)> {
|
||||
let l0i = virt.page_index::<L0>();
|
||||
let l3i = virt.page_index::<L3>();
|
||||
|
||||
let l3 = self.l0.get(l0i)?;
|
||||
let page = l3[l3i].as_page()?;
|
||||
|
||||
Some((page.add(virt & 0xFFF), l3[l3i].attributes().into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<TA: TableAllocator> Drop for ProcessAddressSpaceImpl<TA> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: with safe usage of the ProcessAddressSpaceImpl, clearing and dropping
|
||||
// is safe, no one refers to the memory
|
||||
unsafe {
|
||||
self.clear();
|
||||
let l0_phys = self.l0.as_physical_address();
|
||||
TA::free_page_table(l0_phys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ pub mod sync;
|
||||
pub mod task;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(any(target_arch = "x86", rust_analyzer))]
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xC0000000;
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64", rust_analyzer))]
|
||||
pub const KERNEL_VIRT_OFFSET: usize = 0xFFFFFF8000000000;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ impl<A: Architecture, T> Spinlock<A, T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lock(&self) -> SpinlockGuard<'_, A, T> {
|
||||
pub fn lock(&self) -> SpinlockGuard<A, T> {
|
||||
// Loop until the lock can be acquired
|
||||
if LOCK_HACK.load(Ordering::Acquire) {
|
||||
return SpinlockGuard { lock: self };
|
||||
@@ -103,7 +103,7 @@ impl<A: Architecture, T> IrqSafeSpinlock<A, T> {
|
||||
}
|
||||
|
||||
/// Attempts to acquire a lock. IRQs will be disabled until the lock is released.
|
||||
pub fn lock(&self) -> IrqSafeSpinlockGuard<'_, A, T> {
|
||||
pub fn lock(&self) -> IrqSafeSpinlockGuard<A, T> {
|
||||
// Disable IRQs to avoid IRQ handler trying to acquire the same lock
|
||||
let irq_guard = IrqGuard::acquire();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ edition = "2024"
|
||||
yggdrasil-abi.workspace = true
|
||||
kernel-arch-interface.workspace = true
|
||||
libk-mm-interface.workspace = true
|
||||
memtables.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
|
||||
tock-registers.workspace = true
|
||||
@@ -15,5 +16,11 @@ static_assertions.workspace = true
|
||||
log.workspace = true
|
||||
cfg-if.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
riscv64_board_virt = []
|
||||
riscv64_board_jh7110 = []
|
||||
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -13,7 +13,7 @@ use tock_registers::{
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::{self},
|
||||
mem::{self, KERNEL_VIRT_OFFSET},
|
||||
registers::SATP,
|
||||
ArchitectureImpl, PerCpuData,
|
||||
};
|
||||
@@ -124,7 +124,8 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator<Address = PhysicalAddres
|
||||
|
||||
// TODO stack is leaked
|
||||
let satp = InMemoryRegister::new(0);
|
||||
let kernel_table_phys = mem::fixed::table_physical_address().into_u64();
|
||||
let kernel_table_phys =
|
||||
((&raw const mem::KERNEL_TABLES).addr() - KERNEL_VIRT_OFFSET) as u64;
|
||||
satp.write(SATP::MODE::Sv39 + SATP::ASID.val(0) + SATP::PPN.val(kernel_table_phys >> 12));
|
||||
|
||||
Ok(Self {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![feature(decl_macro)]
|
||||
#![feature(decl_macro, naked_functions)]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -60,9 +60,11 @@ impl CpuData for PerCpuData {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(naked)]
|
||||
#[naked]
|
||||
extern "C" fn idle_task(_: usize) -> ! {
|
||||
core::arch::naked_asm!("1: nop; j 1b");
|
||||
unsafe {
|
||||
core::arch::naked_asm!("1: nop; j 1b");
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchitectureImpl {
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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) };
|
||||
}
|
||||
@@ -1,26 +1,70 @@
|
||||
use kernel_arch_interface::mem::{
|
||||
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
|
||||
use cfg_if::cfg_if;
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock,
|
||||
};
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{page_index, EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use memtables::riscv64::PageAttributes;
|
||||
use static_assertions::{const_assert, const_assert_eq};
|
||||
use table::{PageEntry, PageTable, L1, L2, L3};
|
||||
use tock_registers::interfaces::Writeable;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
mem::table::{PageTable, L1, L3},
|
||||
registers::SATP,
|
||||
};
|
||||
pub use memtables::riscv64::FixedTables;
|
||||
|
||||
pub use intrinsics::*;
|
||||
use crate::registers::SATP;
|
||||
|
||||
pub mod fixed;
|
||||
pub mod intrinsics;
|
||||
pub mod process;
|
||||
pub mod table;
|
||||
|
||||
split_spinlock! {
|
||||
use crate::ArchitectureImpl;
|
||||
use crate::mem::FixedTables;
|
||||
use libk_mm_interface::KernelImageObject;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
#[used]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> =
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature = "riscv64_board_virt")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
} else if #[cfg(feature = "riscv64_board_jh7110")] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x40200000;
|
||||
} else if #[cfg(rust_analyzer)] {
|
||||
pub const KERNEL_PHYS_BASE: usize = 0x80200000;
|
||||
}
|
||||
}
|
||||
|
||||
pub const KERNEL_VIRT_OFFSET: usize = kernel_arch_interface::KERNEL_VIRT_OFFSET;
|
||||
pub const SIGN_EXTEND_MASK: usize = 0xFFFFFF80_00000000;
|
||||
|
||||
pub const KERNEL_START_L1I: usize = page_index::<L1>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
pub const KERNEL_L2I: usize = page_index::<L2>(KERNEL_VIRT_OFFSET + KERNEL_PHYS_BASE);
|
||||
const_assert_eq!(KERNEL_L2I, 1);
|
||||
|
||||
// Runtime mappings
|
||||
// 1GiB of device memory space
|
||||
const DEVICE_MAPPING_L1I: usize = KERNEL_START_L1I + 1;
|
||||
const DEVICE_MAPPING_L3_COUNT: usize = 4;
|
||||
// 32GiB of RAM space
|
||||
const RAM_MAPPING_START_L1I: usize = KERNEL_START_L1I + 2;
|
||||
const RAM_MAPPING_L1_COUNT: usize = 32;
|
||||
const_assert!(RAM_MAPPING_START_L1I + RAM_MAPPING_L1_COUNT <= 512);
|
||||
const_assert!(DEVICE_MAPPING_L1I < 512);
|
||||
|
||||
const DEVICE_MAPPING_OFFSET: usize = (DEVICE_MAPPING_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
|
||||
const RAM_MAPPING_OFFSET: usize = (RAM_MAPPING_START_L1I << L1::SHIFT) | SIGN_EXTEND_MASK;
|
||||
|
||||
// Runtime tables
|
||||
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
|
||||
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
|
||||
[const { PageTable::zeroed() }; DEVICE_MAPPING_L3_COUNT];
|
||||
|
||||
/// Any VAs above this one are sign-extended
|
||||
pub const USER_BOUNDARY: usize = 0x40_00000000;
|
||||
@@ -31,20 +75,17 @@ 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}")
|
||||
if address >= RAM_MAPPING_OFFSET {
|
||||
panic!("Invalid physical address: {address:#x}");
|
||||
}
|
||||
address + RAM_MAPPING_OFFSET
|
||||
}
|
||||
|
||||
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}");
|
||||
if address < RAM_MAPPING_OFFSET {
|
||||
panic!("Invalid \"physicalized\" virtual address {address:#x}");
|
||||
}
|
||||
(address - KERNEL_VIRT_OFFSET) as u64
|
||||
(address - RAM_MAPPING_OFFSET) as u64
|
||||
}
|
||||
|
||||
unsafe fn map_device_pages(
|
||||
@@ -52,32 +93,146 @@ impl KernelTableManager for KernelTableManagerImpl {
|
||||
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 { map_device_memory(PhysicalAddress::from_u64(base), count, attrs) }
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_pages(mapping: &RawDeviceMemoryMapping<Self>) {
|
||||
let _ = mapping;
|
||||
unsafe { unmap_device_memory(mapping) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn auto_lower_address<T>(x: *const T) -> usize {
|
||||
// Device mappings
|
||||
unsafe fn map_device_memory_l3(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
// TODO don't map pages if already mapped
|
||||
|
||||
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
unsafe {
|
||||
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
unsafe {
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] =
|
||||
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::W);
|
||||
}
|
||||
}
|
||||
|
||||
let start = DEVICE_MAPPING_OFFSET + i * L3::SIZE;
|
||||
tlb_flush_range_va(start, count * L3::SIZE);
|
||||
return Ok(start);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
unsafe fn map_device_memory_l2(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
|
||||
for j in 0..count {
|
||||
unsafe {
|
||||
if DEVICE_MAPPING_L2[i + j].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
for j in 0..count {
|
||||
DEVICE_MAPPING_L2[i + j] =
|
||||
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::W);
|
||||
}
|
||||
}
|
||||
|
||||
let start = DEVICE_MAPPING_OFFSET + i * L2::SIZE;
|
||||
tlb_flush_range_va(start, count * L2::SIZE);
|
||||
return Ok(start);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn map_device_memory(
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
|
||||
let l3_aligned = base.page_align_down::<L3>();
|
||||
let l3_offset = base.page_offset::<L3>();
|
||||
let page_count = (l3_offset + size).page_count::<L3>();
|
||||
|
||||
if page_count > 256 {
|
||||
// Large mapping, use L2 mapping instead
|
||||
let l2_aligned = base.page_align_down::<L2>();
|
||||
let l2_offset = base.page_offset::<L2>();
|
||||
let page_count = (l2_offset + size).page_count::<L2>();
|
||||
|
||||
unsafe {
|
||||
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
|
||||
let address = base_address + l2_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l2_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L2::SIZE,
|
||||
))
|
||||
}
|
||||
} else {
|
||||
// Just map the pages directly
|
||||
unsafe {
|
||||
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
|
||||
let address = base_address + l3_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l3_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L3::SIZE,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
|
||||
match map.page_size {
|
||||
L3::SIZE => {
|
||||
for i in 0..map.page_count {
|
||||
let page = map.base_address + i * L3::SIZE;
|
||||
let l2i = page.page_index::<L2>();
|
||||
let l3i = page.page_index::<L3>();
|
||||
unsafe {
|
||||
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
|
||||
}
|
||||
}
|
||||
tlb_flush_range_va(map.base_address, map.page_count * L3::SIZE);
|
||||
}
|
||||
L2::SIZE => todo!(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn auto_address<T>(x: *const T) -> usize {
|
||||
let x = x.addr();
|
||||
if x >= KERNEL_VIRT_OFFSET {
|
||||
x - KERNEL_VIRT_OFFSET
|
||||
@@ -92,14 +247,113 @@ pub fn auto_lower_address<T>(x: *const T) -> usize {
|
||||
///
|
||||
/// Only meant to be called once per each HART during their early init.
|
||||
pub unsafe fn enable_mmu() {
|
||||
let l1_phys = auto_lower_address(&raw const fixed::KERNEL_L1) as u64;
|
||||
let l1_phys = auto_address(&raw const KERNEL_TABLES) 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] };
|
||||
/// Removes the lower half translation mappings.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// Needs to be called once after secondary HARTs are initialized.
|
||||
pub unsafe fn unmap_lower_half() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
let kernel_l1i_lower = page_index::<L1>(KERNEL_PHYS_BASE);
|
||||
tables.l1.data[kernel_l1i_lower] = 0;
|
||||
tlb_flush_range_va(0x0, L1::SIZE);
|
||||
}
|
||||
|
||||
/// Sets up run-time kernel translation tables.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure MMU is already enabled.
|
||||
pub unsafe fn setup_fixed_tables() {
|
||||
let mut tables = KERNEL_TABLES.lock();
|
||||
|
||||
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
|
||||
|
||||
// Set up static runtime mappings
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
unsafe {
|
||||
let device_mapping_l3_phys = PhysicalAddress::from_usize(
|
||||
(&raw const DEVICE_MAPPING_L3S[i]).addr() - KERNEL_VIRT_OFFSET,
|
||||
);
|
||||
DEVICE_MAPPING_L2[i] =
|
||||
PageEntry::table(device_mapping_l3_phys, PageAttributes::empty());
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(tables.l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
tables.l1.data[DEVICE_MAPPING_L1I] =
|
||||
((device_mapping_l2_phys as u64) >> 2) | PageAttributes::V.bits();
|
||||
|
||||
for l1i in 0..RAM_MAPPING_L1_COUNT {
|
||||
let physical = (l1i as u64) << L1::SHIFT;
|
||||
tables.l1.data[l1i + RAM_MAPPING_START_L1I] = (physical >> 2)
|
||||
| (PageAttributes::R
|
||||
| PageAttributes::W
|
||||
| PageAttributes::A
|
||||
| PageAttributes::D
|
||||
| PageAttributes::V)
|
||||
.bits();
|
||||
}
|
||||
|
||||
tlb_flush_full();
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_full() {
|
||||
tlb_flush_full();
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_global_va(va: usize) {
|
||||
tlb_flush_va(va);
|
||||
// TODO send TLB shootdown IPI to other harts
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va(start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va(page);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tlb_flush_range_va_asid(asid: usize, start: usize, size: usize) {
|
||||
let end = (start + size).page_align_up::<L3>();
|
||||
let start = start.page_align_down::<L3>();
|
||||
|
||||
for page in (start..end).step_by(L3::SIZE) {
|
||||
tlb_flush_va_asid(page, asid);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_full() {
|
||||
unsafe { core::arch::asm!("sfence.vma") };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va(va: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, zero", in(reg) va) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_asid(asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma zero, {0}", in(reg) asid) };
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn tlb_flush_va_asid(va: usize, asid: usize) {
|
||||
unsafe { core::arch::asm!("sfence.vma {0}, {1}", in(reg) va, in(reg) asid) };
|
||||
}
|
||||
|
||||
pub fn clone_kernel_tables(dst: &mut PageTable<L1>) {
|
||||
let tables = KERNEL_TABLES.lock();
|
||||
for l1i in page_index::<L1>(USER_BOUNDARY)..512 {
|
||||
dst[l1i] = unsafe { PageEntry::from_raw(tables.l1.data[l1i]) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,12 +11,10 @@ use libk_mm_interface::{
|
||||
EntryLevel, EntryLevelDrop, EntryLevelExt, MapAttributes, NextPageTable, TableAllocator,
|
||||
},
|
||||
};
|
||||
use memtables::riscv64::PageAttributes;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::mem::{
|
||||
clone_kernel_tables,
|
||||
table::{PageAttributes, PageEntry},
|
||||
};
|
||||
use crate::mem::{clone_kernel_tables, table::PageEntry};
|
||||
|
||||
use super::{
|
||||
table::{DroppableRange, PageTable, L1, L2, L3},
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use core::{
|
||||
fmt,
|
||||
marker::PhantomData,
|
||||
ops::{Index, IndexMut, Range},
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use libk_mm_interface::{
|
||||
address::{AsPhysicalAddress, PhysicalAddress},
|
||||
pointer::{PhysicalRef, PhysicalRefMut},
|
||||
@@ -18,40 +16,7 @@ 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
|
||||
}
|
||||
pub use memtables::riscv64::PageAttributes;
|
||||
|
||||
/// L3 - entry is 4KiB
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -77,7 +42,7 @@ impl EntryLevel for L1 {
|
||||
|
||||
#[repr(C, align(0x1000))]
|
||||
pub struct PageTable<L: EntryLevel> {
|
||||
pub(crate) entries: [PageEntry<L>; 512],
|
||||
entries: [PageEntry<L>; 512],
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
@@ -239,19 +204,6 @@ impl<L: NonTerminalEntryLevel + 'static> NextPageTable for PageTable<L> {
|
||||
}
|
||||
|
||||
impl<L: NonTerminalEntryLevel> PageEntry<L> {
|
||||
pub const fn identity_block(address: PhysicalAddress) -> Self {
|
||||
Self(
|
||||
(address.into_u64() >> 2)
|
||||
| PageAttributes::R.bits()
|
||||
| PageAttributes::W.bits()
|
||||
| PageAttributes::X.bits()
|
||||
| PageAttributes::V.bits()
|
||||
| PageAttributes::D.bits()
|
||||
| PageAttributes::A.bits(),
|
||||
PhantomData,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn block(address: PhysicalAddress, attrs: PageAttributes) -> Self {
|
||||
// TODO validate address alignment
|
||||
Self(
|
||||
@@ -318,26 +270,3 @@ impl<L: EntryLevel> IndexMut<usize> for PageTable<L> {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PageAttributes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use fmt::Write;
|
||||
|
||||
macro_rules! bit {
|
||||
($self:ident, $field:expr, $letter:literal) => {
|
||||
if $self.contains($field) {
|
||||
f.write_char($letter)
|
||||
} else {
|
||||
f.write_char('-')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bit!(self, Self::R, 'r')?;
|
||||
bit!(self, Self::W, 'w')?;
|
||||
bit!(self, Self::X, 'x')?;
|
||||
bit!(self, Self::U, 'u')?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +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 = "x86")] {
|
||||
extern crate kernel_arch_i686 as imp;
|
||||
} else if #[cfg(target_arch = "riscv64")] {
|
||||
extern crate kernel_arch_riscv64 as imp;
|
||||
} else {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#![no_std]
|
||||
#![allow(clippy::new_without_default)]
|
||||
#![feature(naked_functions)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@@ -68,16 +69,18 @@ impl 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!(
|
||||
r#"
|
||||
unsafe {
|
||||
core::arch::naked_asm!(
|
||||
r#"
|
||||
1:
|
||||
nop
|
||||
jmp 1b
|
||||
"#,
|
||||
options(att_syntax)
|
||||
);
|
||||
options(att_syntax)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl ArchitectureImpl {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
@@ -1,37 +1,98 @@
|
||||
use kernel_arch_interface::mem::{
|
||||
DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping,
|
||||
use core::{
|
||||
alloc::Layout,
|
||||
ops::{Deref, DerefMut},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
};
|
||||
use libk_mm_interface::{address::PhysicalAddress, table::EntryLevel};
|
||||
|
||||
use kernel_arch_interface::{
|
||||
mem::{DeviceMemoryAttributes, KernelTableManager, RawDeviceMemoryMapping},
|
||||
split_spinlock,
|
||||
};
|
||||
use kernel_arch_x86::registers::CR3;
|
||||
use libk_mm_interface::{
|
||||
address::PhysicalAddress,
|
||||
table::{page_index, EntryLevel, EntryLevelExt},
|
||||
};
|
||||
use static_assertions::{const_assert_eq, const_assert_ne};
|
||||
use 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 libk_mm_interface::KernelImageObject;
|
||||
use memtables::x86_64::FixedTables;
|
||||
use crate::ArchitectureImpl;
|
||||
|
||||
#[link_section = ".data.tables"]
|
||||
static KERNEL_TABLES: KernelImageObject<FixedTables> =
|
||||
unsafe { KernelImageObject::new(FixedTables::zeroed()) };
|
||||
}
|
||||
|
||||
// 2MiB for early mappings
|
||||
const EARLY_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK
|
||||
| (KERNEL_L0_INDEX * L0::SIZE)
|
||||
| (KERNEL_L1_INDEX * L1::SIZE)
|
||||
| (EARLY_MAPPING_L2I * L2::SIZE);
|
||||
static mut EARLY_MAPPING_L3: PageTable<L3> = PageTable::zeroed();
|
||||
// 1GiB for device MMIO mapping
|
||||
const DEVICE_MAPPING_OFFSET: usize =
|
||||
CANONICAL_ADDRESS_MASK | (KERNEL_L0_INDEX * L0::SIZE) | (DEVICE_MAPPING_L1I * L1::SIZE);
|
||||
static mut DEVICE_MAPPING_L2: PageTable<L2> = PageTable::zeroed();
|
||||
static mut DEVICE_MAPPING_L3S: [PageTable<L3>; DEVICE_MAPPING_L3_COUNT] =
|
||||
[PageTable::zeroed(); DEVICE_MAPPING_L3_COUNT];
|
||||
// 512GiB for whole RAM mapping
|
||||
pub const RAM_MAPPING_OFFSET: usize = CANONICAL_ADDRESS_MASK | (RAM_MAPPING_L0I * L0::SIZE);
|
||||
pub static MEMORY_LIMIT: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static mut RAM_MAPPING_L1: PageTable<L1> = PageTable::zeroed();
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
fn virtualize(address: u64) -> usize {
|
||||
let address = address as usize;
|
||||
if address < 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,25 +100,245 @@ 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);
|
||||
flush_tlb_entry(EARLY_MAPPING_OFFSET + (i + l3i) * L3::SIZE);
|
||||
}
|
||||
|
||||
return Ok(EARLY_MAPPING_OFFSET + l3i * L3::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
unsafe fn unmap_early_page(address: usize) {
|
||||
if !(EARLY_MAPPING_OFFSET..EARLY_MAPPING_OFFSET + L2::SIZE).contains(&address) {
|
||||
panic!("Tried to unmap invalid early mapping: {:#x}", address);
|
||||
}
|
||||
|
||||
let l3i = (address - EARLY_MAPPING_OFFSET).page_index::<L3>();
|
||||
|
||||
assert!(EARLY_MAPPING_L3[l3i].is_present());
|
||||
EARLY_MAPPING_L3[l3i] = PageEntry::INVALID;
|
||||
}
|
||||
|
||||
// Device mappings
|
||||
unsafe fn map_device_memory_l3(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
// TODO don't map pages if already mapped
|
||||
|
||||
'l0: for i in 0..DEVICE_MAPPING_L3_COUNT * 512 {
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
if DEVICE_MAPPING_L3S[l2i][l3i].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
let l2i = (i + j) / 512;
|
||||
let l3i = (i + j) % 512;
|
||||
|
||||
// TODO NX, NC
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] =
|
||||
PageEntry::page(base.add(j * L3::SIZE), PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L3::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
unsafe fn map_device_memory_l2(
|
||||
base: PhysicalAddress,
|
||||
count: usize,
|
||||
_attrs: DeviceMemoryAttributes,
|
||||
) -> Result<usize, Error> {
|
||||
'l0: for i in DEVICE_MAPPING_L3_COUNT..512 {
|
||||
for j in 0..count {
|
||||
if DEVICE_MAPPING_L2[i + j].is_present() {
|
||||
continue 'l0;
|
||||
}
|
||||
}
|
||||
|
||||
for j in 0..count {
|
||||
DEVICE_MAPPING_L2[i + j] =
|
||||
PageEntry::<L2>::block(base.add(j * L2::SIZE), PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
return Ok(DEVICE_MAPPING_OFFSET + i * L2::SIZE);
|
||||
}
|
||||
|
||||
Err(Error::OutOfMemory)
|
||||
}
|
||||
|
||||
unsafe fn map_device_memory(
|
||||
base: PhysicalAddress,
|
||||
size: usize,
|
||||
attrs: DeviceMemoryAttributes,
|
||||
) -> Result<RawDeviceMemoryMapping<KernelTableManagerImpl>, Error> {
|
||||
let l3_aligned = base.page_align_down::<L3>();
|
||||
let l3_offset = base.page_offset::<L3>();
|
||||
let page_count = (l3_offset + size).page_count::<L3>();
|
||||
|
||||
if page_count > 256 {
|
||||
// Large mapping, use L2 mapping instead
|
||||
let l2_aligned = base.page_align_down::<L2>();
|
||||
let l2_offset = base.page_offset::<L2>();
|
||||
let page_count = (l2_offset + size).page_count::<L2>();
|
||||
|
||||
let base_address = map_device_memory_l2(l2_aligned, page_count, attrs)?;
|
||||
let address = base_address + l2_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l2_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L2::SIZE,
|
||||
))
|
||||
} else {
|
||||
// Just map the pages directly
|
||||
let base_address = map_device_memory_l3(l3_aligned, page_count, attrs)?;
|
||||
let address = base_address + l3_offset;
|
||||
|
||||
Ok(RawDeviceMemoryMapping::from_raw_parts(
|
||||
l3_aligned.into_u64(),
|
||||
address,
|
||||
base_address,
|
||||
page_count,
|
||||
L3::SIZE,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn unmap_device_memory(map: &RawDeviceMemoryMapping<KernelTableManagerImpl>) {
|
||||
match map.page_size {
|
||||
L3::SIZE => {
|
||||
for i in 0..map.page_count {
|
||||
let page = map.base_address + i * L3::SIZE;
|
||||
let l2i = page.page_index::<L2>();
|
||||
let l3i = page.page_index::<L3>();
|
||||
assert!(DEVICE_MAPPING_L3S[l2i][l3i].is_present());
|
||||
DEVICE_MAPPING_L3S[l2i][l3i] = PageEntry::INVALID;
|
||||
flush_tlb_entry(page);
|
||||
}
|
||||
}
|
||||
L2::SIZE => todo!(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory mapping which may be used for performing early kernel initialization
|
||||
pub struct EarlyMapping<'a, T: ?Sized> {
|
||||
value: &'a mut T,
|
||||
page_count: usize,
|
||||
}
|
||||
|
||||
impl<'a, T: Sized> EarlyMapping<'a, T> {
|
||||
/// # Safety
|
||||
///
|
||||
/// `physical` address provided must be a valid non-NULL address actually containing `T`.
|
||||
pub unsafe fn map(physical: PhysicalAddress) -> Result<EarlyMapping<'a, T>, Error> {
|
||||
let layout = Layout::new::<T>();
|
||||
let aligned = physical.page_align_down::<L3>();
|
||||
let offset = physical.page_offset::<L3>();
|
||||
let page_count = (offset + layout.size()).div_ceil(L3::SIZE);
|
||||
|
||||
let virt = map_early_pages(aligned, page_count)?;
|
||||
let value = &mut *((virt + offset) as *mut T);
|
||||
|
||||
Ok(EarlyMapping { value, page_count })
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// `physical` address provided must be a valid non-NULL address actually containing a `T`
|
||||
/// slice of given `len`.
|
||||
pub unsafe fn map_slice(
|
||||
physical: PhysicalAddress,
|
||||
len: usize,
|
||||
) -> Result<EarlyMapping<'a, [T]>, Error> {
|
||||
let layout = Layout::array::<T>(len).unwrap();
|
||||
let aligned = physical.page_align_down::<L3>();
|
||||
let offset = physical.page_offset::<L3>();
|
||||
let page_count = (offset + layout.size()).div_ceil(L3::SIZE);
|
||||
|
||||
let virt = map_early_pages(aligned, page_count)?;
|
||||
let value = core::slice::from_raw_parts_mut((virt + offset) as *mut T, len);
|
||||
|
||||
Ok(EarlyMapping { value, page_count })
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Deref for EarlyMapping<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> DerefMut for EarlyMapping<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Drop for EarlyMapping<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
let address = (self.value as *mut T).addr() & !(L3::SIZE - 1);
|
||||
|
||||
for i in 0..self.page_count {
|
||||
let page = address + i * L3::SIZE;
|
||||
|
||||
unsafe {
|
||||
unmap_early_page(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn auto_lower_address<T>(pointer: *const T) -> usize {
|
||||
pub fn auto_address<T>(pointer: *const T) -> usize {
|
||||
let address = pointer.addr();
|
||||
if address < KERNEL_VIRT_OFFSET {
|
||||
address
|
||||
@@ -82,12 +363,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 = auto_address(&raw const EARLY_MAPPING_L3);
|
||||
let device_mapping_l2_phys = auto_address(&raw const DEVICE_MAPPING_L2);
|
||||
let ram_mapping_l1_phys = auto_address(&raw const RAM_MAPPING_L1);
|
||||
|
||||
for i in 0..DEVICE_MAPPING_L3_COUNT {
|
||||
let device_mapping_l3_phys =
|
||||
PhysicalAddress::from_usize(auto_address(&raw const DEVICE_MAPPING_L3S[i]));
|
||||
DEVICE_MAPPING_L2[i] = PageEntry::table(device_mapping_l3_phys, PageAttributes::WRITABLE);
|
||||
}
|
||||
|
||||
assert_eq!(tables.kernel_l2.data[EARLY_MAPPING_L2I], 0);
|
||||
tables.kernel_l2.data[EARLY_MAPPING_L2I] = (early_mapping_l3_phys as u64)
|
||||
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
|
||||
assert_eq!(tables.kernel_l1.data[DEVICE_MAPPING_L1I], 0);
|
||||
tables.kernel_l1.data[DEVICE_MAPPING_L1I] = (device_mapping_l2_phys as u64)
|
||||
| (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
|
||||
assert_eq!(tables.l0.data[RAM_MAPPING_L0I], 0);
|
||||
tables.l0.data[RAM_MAPPING_L0I] =
|
||||
(ram_mapping_l1_phys as u64) | (PageAttributes::WRITABLE | PageAttributes::PRESENT).bits();
|
||||
|
||||
// TODO ENABLE EFER.NXE
|
||||
let cr3 = auto_address(&raw const tables.l0);
|
||||
CR3.set_address(cr3);
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
|
||||
+3
-3
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,6 @@ fn main() {
|
||||
"x86_64" => build_x86_64(),
|
||||
"aarch64" => (),
|
||||
"riscv64" => (),
|
||||
_ => panic!("Unknown target arch: {arch:?}"),
|
||||
_ => panic!("Unknown target arch: {:?}", arch),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,6 @@ unsafe impl Allocator for AcpiAllocator {
|
||||
}
|
||||
|
||||
// TODO don't map memory as device if not necessary
|
||||
/// # Safety
|
||||
///
|
||||
/// Allows direct reads from physical memory, unsafe
|
||||
pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
|
||||
let io =
|
||||
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
|
||||
@@ -52,9 +49,6 @@ pub unsafe fn read_memory<T>(address: PhysicalAddress) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Allows direct writes to physical memory, unsafe
|
||||
pub unsafe fn write_memory<T>(address: PhysicalAddress, value: T) {
|
||||
let io =
|
||||
unsafe { DeviceMemoryMapping::map(address, size_of::<T>(), Default::default()).unwrap() };
|
||||
|
||||
@@ -235,7 +235,7 @@ impl AhciPort {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn submit<C: AtaCommand>(&self, command: &C) -> Result<SubmittedCommand<'_>, AhciError> {
|
||||
async fn submit<C: AtaCommand>(&self, command: &C) -> Result<SubmittedCommand, AhciError> {
|
||||
if command.prd_count() > 2 {
|
||||
log::warn!("TODO: AHCI doesn't like 3+ PRD transfers");
|
||||
return Err(AhciError::RegionTooLarge);
|
||||
|
||||
@@ -129,7 +129,7 @@ impl PrpList {
|
||||
list: None,
|
||||
}),
|
||||
_ => {
|
||||
let count = size.div_ceil(0x1000);
|
||||
let count = (size + 0xFFF) / 0x1000;
|
||||
let list =
|
||||
DmaBuffer::new_slice_with(dma, |i| base.add((i + 1) * 0x1000), count - 1)
|
||||
.map_err(NvmeError::MemoryError)?;
|
||||
|
||||
@@ -71,10 +71,10 @@ impl ScsiEnclosure {
|
||||
|
||||
// Probe LUNs
|
||||
for i in 0..lun_count {
|
||||
if this.probe_lun(i as u8).await
|
||||
&& let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await
|
||||
{
|
||||
*this.units[i].write() = Some(unit);
|
||||
if this.probe_lun(i as u8).await {
|
||||
if let Ok(unit) = ScsiUnit::setup(this.clone(), i as u8).await {
|
||||
*this.units[i].write() = Some(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,11 @@ impl ScsiEnclosure {
|
||||
attempts -= 1;
|
||||
}
|
||||
|
||||
attempts != 0
|
||||
if attempts == 0 {
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
async fn poll(self: &Arc<Self>) {
|
||||
|
||||
@@ -44,7 +44,7 @@ impl ScsiTransportWrapper {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let lba_bytes = (lba as u32).to_be_bytes();
|
||||
let lba_count = lba_count.to_be_bytes();
|
||||
let lba_count = (lba_count as u16).to_be_bytes();
|
||||
// Issue a READ (10) command
|
||||
let request_buffer = [
|
||||
0x28,
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
use core::time::Duration;
|
||||
|
||||
use libk::{error::Error, task::runtime::psleep};
|
||||
use tock_registers::{fields::FieldValue, register_bitfields, LocalRegisterCopy};
|
||||
|
||||
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
// bitflags! {
|
||||
// pub struct PcieLinkControl: u16 {
|
||||
// const ASPM_DISABLE = 0 << 0;
|
||||
// // Active state power management control
|
||||
// const ASPM_MASK = 0x3 << 0;
|
||||
// // Enable clock power management
|
||||
// const ECPM = 1 << 8;
|
||||
// }
|
||||
// }
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub DeviceCapabilities [
|
||||
MAX_PAYLOAD_SIZE OFFSET(0) NUMBITS(3) [],
|
||||
PHANTOM_FUNCTIONS OFFSET(3) NUMBITS(2) [],
|
||||
L0S_ACCEPTABLE_LATENCY OFFSET(6) NUMBITS(3) [],
|
||||
L1_ACCEPTABLE_LATENCY OFFSET(9) NUMBITS(3) [],
|
||||
ROLE_ERROR_REPORTING OFFSET(15) NUMBITS(1) [],
|
||||
CAPTURED_SLOT_POWER_LIMIT OFFSET(18) NUMBITS(8) [],
|
||||
CAPTURED_SLOT_POWER_SCALE OFFSET(26) NUMBITS(3) [],
|
||||
FUNCTION_LEVEL_RESET OFFSET(28) NUMBITS(1) [],
|
||||
],
|
||||
pub LinkCapabilities [
|
||||
MAX_LINK_SPEED OFFSET(0) NUMBITS(4) [],
|
||||
MAX_LINK_WIDTH OFFSET(4) NUMBITS(6) [],
|
||||
ASPM OFFSET(10) NUMBITS(2) [],
|
||||
L0S_EXIT_LATENCY OFFSET(12) NUMBITS(3) [],
|
||||
L1_EXIT_LATENCY OFFSET(15) NUMBITS(3) [],
|
||||
CLOCK_PM OFFSET(18) NUMBITS(1) [],
|
||||
SURPRISE_DOWN_ERROR OFFSET(19) NUMBITS(1) [],
|
||||
DATA_LINK_ACTIVE_REPORTING OFFSET(20) NUMBITS(1) [],
|
||||
LINK_BANDWIDTH_NOTIFICATION OFFSET(21) NUMBITS(1) [],
|
||||
ASPM_COMPLIANCE OFFSET(22) NUMBITS(1) [],
|
||||
PORT_NUMBER OFFSET(24) NUMBITS(8) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u16,
|
||||
pub DeviceControl [
|
||||
CORRECTABLE_ERROR_REPORTING OFFSET(0) NUMBITS(1) [],
|
||||
NONFATAL_ERROR_REPORTING OFFSET(1) NUMBITS(1) [],
|
||||
FATAL_ERROR_REPORTING OFFSET(2) NUMBITS(1) [],
|
||||
UNSUPPORTED_REQ_REPORTING OFFSET(3) NUMBITS(1) [],
|
||||
RELAXED_ORDERING OFFSET(4) NUMBITS(1) [],
|
||||
MAX_PAYLOAD_SIZE OFFSET(5) NUMBITS(3) [],
|
||||
EXTENDED_TAG_FIELD OFFSET(8) NUMBITS(1) [],
|
||||
PHANTOM_FUNCTIONS OFFSET(9) NUMBITS(1) [],
|
||||
AUX_POWER_PM_ENABLE OFFSET(10) NUMBITS(1) [],
|
||||
NO_SNOOP OFFSET(11) NUMBITS(1) [],
|
||||
MAX_READ_REQ_SIZE OFFSET(12) NUMBITS(3) [],
|
||||
FUNCTION_LEVEL_RESET OFFSET(15) NUMBITS(1) [],
|
||||
],
|
||||
pub LinkControl [
|
||||
ASPM OFFSET(0) NUMBITS(2) [
|
||||
Disabled = 0,
|
||||
L0sEntryEnabled = 1,
|
||||
L1EntryEnabled = 2,
|
||||
L0sL1EntryEnabled = 3,
|
||||
],
|
||||
READ_COMPLETION_BOUNDARY OFFSET(3) NUMBITS(1) [],
|
||||
LINK_DISABLE OFFSET(4) NUMBITS(1) [],
|
||||
RETRAIN_LINK OFFSET(5) NUMBITS(1) [],
|
||||
COMMON_CLOCK_CONFIG OFFSET(6) NUMBITS(1) [],
|
||||
EXTENDED_SYNCH OFFSET(7) NUMBITS(1) [],
|
||||
CLOCK_PM OFFSET(8) NUMBITS(1) [],
|
||||
HARDWARE_ABW_DISABLE OFFSET(9) NUMBITS(1) [],
|
||||
LINK_BW_MANAGEMENT_IRQ OFFSET(10) NUMBITS(1) [],
|
||||
LINK_ABW_IRQ OFFSET(11) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
/// PCIe capability
|
||||
pub struct PciExpressCapability;
|
||||
|
||||
/// PCI Express capability data structure
|
||||
pub struct PciExpressData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl PciCapability for PciExpressCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PciExpressData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PciExpressData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reg_read {
|
||||
($self:expr, $offset:literal, u32) => {
|
||||
$self.space.read_u32($self.offset + $offset)
|
||||
};
|
||||
($self:expr, $offset:literal, u16) => {
|
||||
$self.space.read_u16($self.offset + $offset)
|
||||
};
|
||||
}
|
||||
macro_rules! reg_write {
|
||||
($self:expr, $offset:literal, u32, $value:expr) => {
|
||||
$self.space.write_u32($self.offset + $offset, $value)
|
||||
};
|
||||
($self:expr, $offset:literal, u16, $value:expr) => {
|
||||
$self.space.write_u16($self.offset + $offset, $value)
|
||||
};
|
||||
}
|
||||
macro_rules! make_register {
|
||||
(
|
||||
$reg:ident : $ty:ident @ $offset:literal {
|
||||
$get:ident
|
||||
$(, $set:ident, $modify:ident)?
|
||||
$(,)?
|
||||
}
|
||||
) => {
|
||||
pub fn $get(&self) -> LocalRegisterCopy<$ty, $reg::Register> {
|
||||
LocalRegisterCopy::new(reg_read!(self, $offset, $ty))
|
||||
}
|
||||
|
||||
$(
|
||||
pub fn $set(&mut self, value: $ty) {
|
||||
reg_write!(self, $offset, $ty, value)
|
||||
}
|
||||
|
||||
pub fn $modify(&mut self, field: FieldValue<$ty, $reg::Register>) {
|
||||
let mut value = self.$get();
|
||||
value.modify(field);
|
||||
self.$set(value.get());
|
||||
}
|
||||
)?
|
||||
};
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PciExpressData<'s, S> {
|
||||
make_register!(DeviceCapabilities : u32 @ 0x04 { device_capabilities });
|
||||
make_register!(DeviceControl : u16 @ 0x08 {
|
||||
device_control,
|
||||
set_device_control,
|
||||
modify_device_control,
|
||||
});
|
||||
make_register!(LinkCapabilities : u32 @ 0x0C { link_capabilities });
|
||||
make_register!(LinkControl : u16 @ 0x10 {
|
||||
link_control,
|
||||
set_link_control,
|
||||
modify_link_control,
|
||||
});
|
||||
|
||||
pub fn function_level_reset(&mut self) -> Result<(), Error> {
|
||||
if self
|
||||
.device_capabilities()
|
||||
.matches_all(DeviceCapabilities::FUNCTION_LEVEL_RESET::SET)
|
||||
{
|
||||
self.modify_device_control(DeviceControl::FUNCTION_LEVEL_RESET::SET);
|
||||
psleep(Duration::from_millis(10));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hot_link_reset(&mut self) {
|
||||
self.modify_link_control(LinkControl::LINK_DISABLE::SET);
|
||||
psleep(Duration::from_millis(10));
|
||||
self.modify_link_control(LinkControl::LINK_DISABLE::CLEAR);
|
||||
psleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//! PCI capability structures and queries
|
||||
|
||||
pub mod express;
|
||||
pub mod msi;
|
||||
pub mod power;
|
||||
pub mod virtio;
|
||||
|
||||
pub use express::PciExpressCapability;
|
||||
pub use msi::{MsiCapability, MsiXCapability};
|
||||
pub use power::PowerManagementCapability;
|
||||
pub use virtio::{
|
||||
VirtioCapability, VirtioCommonConfigCapability, VirtioDeviceConfigCapability,
|
||||
VirtioInterruptStatusCapability, VirtioNotifyConfigCapability,
|
||||
};
|
||||
+7
-306
@@ -1,91 +1,21 @@
|
||||
//! PCI capability structures and queries
|
||||
|
||||
use alloc::{sync::Arc, vec, vec::Vec};
|
||||
use bitflags::bitflags;
|
||||
|
||||
use device_api::interrupt::{
|
||||
InterruptAffinity, InterruptHandler, MessageInterruptController, MsiInfo,
|
||||
};
|
||||
use libk::error::Error;
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
registers::{ReadWrite, WriteOnly},
|
||||
};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::PciBaseAddress;
|
||||
|
||||
use super::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
bitflags! {
|
||||
pub struct PcieLinkControl: u16 {
|
||||
const ASPM_DISABLE = 0 << 0;
|
||||
// Active state power management control
|
||||
const ASPM_MASK = 0x3 << 0;
|
||||
// Enable clock power management
|
||||
const ECPM = 1 << 8;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
use core::mem::offset_of;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64", rust_analyzer))]
|
||||
use kernel_arch_x86::intrinsics;
|
||||
|
||||
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self;
|
||||
|
||||
fn space(&self) -> &'s S;
|
||||
fn offset(&self) -> usize;
|
||||
|
||||
fn bar_index(&self) -> Option<usize> {
|
||||
let value = self.space().read_u8(self.offset() + 4);
|
||||
(value <= 0x5).then_some(value as _)
|
||||
}
|
||||
|
||||
fn bar_offset(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 8);
|
||||
value as _
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 12);
|
||||
value as _
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VirtioCapability {
|
||||
const CFG_TYPE: u8;
|
||||
const MIN_LEN: usize = 0;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
|
||||
}
|
||||
|
||||
/// Power management capability entry
|
||||
pub struct PowerManagementCapability;
|
||||
/// MSI-X capability query
|
||||
pub struct MsiXCapability;
|
||||
/// MSI capability query
|
||||
pub struct MsiCapability;
|
||||
/// PCIe capability
|
||||
pub struct PciExpressCapability;
|
||||
|
||||
// VirtIO-over-PCI capabilities
|
||||
/// VirtIO PCI configuration access
|
||||
pub struct VirtioDeviceConfigCapability;
|
||||
/// VirtIO common configuration
|
||||
pub struct VirtioCommonConfigCapability;
|
||||
/// VirtIO notify configuration
|
||||
pub struct VirtioNotifyConfigCapability;
|
||||
/// VirtIO interrupt status
|
||||
pub struct VirtioInterruptStatusCapability;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DevicePowerState {
|
||||
D0,
|
||||
D1,
|
||||
D2,
|
||||
D3Cold,
|
||||
D3Hot,
|
||||
}
|
||||
use crate::{PciBaseAddress, PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
/// Represents an entry in MSI-X vector table
|
||||
#[repr(C)]
|
||||
@@ -109,11 +39,10 @@ pub struct MsiXVectorTable<'a> {
|
||||
len: usize,
|
||||
}
|
||||
|
||||
/// PCI Power Management capability data structure
|
||||
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
/// MSI-X capability query
|
||||
pub struct MsiXCapability;
|
||||
/// MSI capability query
|
||||
pub struct MsiCapability;
|
||||
|
||||
/// MSI-X capability data structure
|
||||
pub struct MsiXData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
@@ -127,63 +56,6 @@ pub struct MsiData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
/// PCI Express capability data structure
|
||||
pub struct PcieData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<T: VirtioCapability> PciCapability for T {
|
||||
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
|
||||
|
||||
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
|
||||
let cfg_type = space.read_u8(offset + 3);
|
||||
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
|
||||
}
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
T::Output::from_space_offset(space, offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for PowerManagementCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PowerManagementData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for MsiXCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::MsiX;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = MsiXData<'a, S>;
|
||||
@@ -210,167 +82,6 @@ impl PciCapability for MsiCapability {
|
||||
}
|
||||
}
|
||||
|
||||
impl PciCapability for PciExpressCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PciExpress;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PcieData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PcieData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioDeviceConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x04;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioDeviceConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioCommonConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x01;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioCommonConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioNotifyConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x02;
|
||||
const MIN_LEN: usize = 0x14;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
|
||||
pub fn offset_multiplier(&self) -> usize {
|
||||
self.space.read_u32(self.offset + 16) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioNotifyConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioInterruptStatusCapability {
|
||||
const CFG_TYPE: u8 = 0x03;
|
||||
const MIN_LEN: usize = 1;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
|
||||
pub fn read_status(&self) -> (bool, bool) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioInterruptStatusData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
|
||||
pub fn set_device_power_state(&self, state: DevicePowerState) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
|
||||
let current = self.get_device_power_state();
|
||||
|
||||
if state == current {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set device power state: {state:?}");
|
||||
|
||||
match state {
|
||||
DevicePowerState::D0 => {
|
||||
// power = 0b00 | PME_EN
|
||||
self.space.write_u16(self.offset + 4, pmcsr);
|
||||
}
|
||||
_ => {
|
||||
log::warn!("TODO: {state:?} power state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pme_en(&self, state: bool) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
let new = if state {
|
||||
pmcsr | (1 << 8)
|
||||
} else {
|
||||
pmcsr & !(1 << 8)
|
||||
};
|
||||
if pmcsr == new {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set PMCSR.PME_En = {state}");
|
||||
|
||||
self.space.write_u16(self.offset + 4, new);
|
||||
}
|
||||
|
||||
pub fn get_device_power_state(&self) -> DevicePowerState {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
match pmcsr & 0x3 {
|
||||
0b00 => DevicePowerState::D0,
|
||||
0b01 => DevicePowerState::D1,
|
||||
0b10 => DevicePowerState::D2,
|
||||
0b11 => DevicePowerState::D3Hot,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiXData<'s, S> {
|
||||
// TODO use pending bits as well
|
||||
/// Maps and returns the vector table associated with the device's MSI-X capability
|
||||
@@ -601,13 +312,3 @@ impl<'s, S: PciConfigurationSpace + ?Sized + 's> MsiData<'s, S> {
|
||||
Ok(info)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PcieData<'s, S> {
|
||||
pub fn link_control(&self) -> PcieLinkControl {
|
||||
PcieLinkControl::from_bits_retain(self.space.read_u16(self.offset + 0x10))
|
||||
}
|
||||
|
||||
pub fn set_link_control(&mut self, value: PcieLinkControl) {
|
||||
self.space.write_u16(self.offset + 0x10, value.bits());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum DevicePowerState {
|
||||
D0,
|
||||
D1,
|
||||
D2,
|
||||
D3Cold,
|
||||
D3Hot,
|
||||
}
|
||||
|
||||
/// Power management capability entry
|
||||
pub struct PowerManagementCapability;
|
||||
|
||||
/// PCI Power Management capability data structure
|
||||
pub struct PowerManagementData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl PciCapability for PowerManagementCapability {
|
||||
const ID: PciCapabilityId = PciCapabilityId::PowerManagement;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = PowerManagementData<'a, S>;
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
PowerManagementData { space, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> PowerManagementData<'s, S> {
|
||||
pub fn set_device_power_state(&self, state: DevicePowerState) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4) & !0x3;
|
||||
let current = self.get_device_power_state();
|
||||
|
||||
if state == current {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set device power state: {state:?}");
|
||||
|
||||
match state {
|
||||
DevicePowerState::D0 => {
|
||||
// power = 0b00 | PME_EN
|
||||
self.space.write_u16(self.offset + 4, pmcsr);
|
||||
}
|
||||
_ => {
|
||||
log::warn!("TODO: {state:?} power state");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_pme_en(&self, state: bool) {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
let new = if state {
|
||||
pmcsr | (1 << 8)
|
||||
} else {
|
||||
pmcsr & !(1 << 8)
|
||||
};
|
||||
if pmcsr == new {
|
||||
return;
|
||||
}
|
||||
|
||||
log::info!("Set PMCSR.PME_En = {state}");
|
||||
|
||||
self.space.write_u16(self.offset + 4, new);
|
||||
}
|
||||
|
||||
pub fn get_device_power_state(&self) -> DevicePowerState {
|
||||
let pmcsr = self.space.read_u16(self.offset + 4);
|
||||
match pmcsr & 0x3 {
|
||||
0b00 => DevicePowerState::D0,
|
||||
0b01 => DevicePowerState::D1,
|
||||
0b10 => DevicePowerState::D2,
|
||||
0b11 => DevicePowerState::D3Hot,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
use crate::{PciCapability, PciCapabilityId, PciConfigurationSpace};
|
||||
|
||||
pub trait VirtioCapabilityData<'s, S: PciConfigurationSpace + ?Sized + 's>: Sized {
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self;
|
||||
|
||||
fn space(&self) -> &'s S;
|
||||
fn offset(&self) -> usize;
|
||||
|
||||
fn bar_index(&self) -> Option<usize> {
|
||||
let value = self.space().read_u8(self.offset() + 4);
|
||||
(value <= 0x5).then_some(value as _)
|
||||
}
|
||||
|
||||
fn bar_offset(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 8);
|
||||
value as _
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
let value = self.space().read_u32(self.offset() + 12);
|
||||
value as _
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VirtioCapability {
|
||||
const CFG_TYPE: u8;
|
||||
const MIN_LEN: usize = 0;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a>: VirtioCapabilityData<'a, S>;
|
||||
}
|
||||
|
||||
// VirtIO-over-PCI capabilities
|
||||
/// VirtIO PCI configuration access
|
||||
pub struct VirtioDeviceConfigCapability;
|
||||
/// VirtIO common configuration
|
||||
pub struct VirtioCommonConfigCapability;
|
||||
/// VirtIO notify configuration
|
||||
pub struct VirtioNotifyConfigCapability;
|
||||
/// VirtIO interrupt status
|
||||
pub struct VirtioInterruptStatusCapability;
|
||||
|
||||
pub struct VirtioDeviceConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioCommonConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioNotifyConfigData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
pub struct VirtioInterruptStatusData<'s, S: PciConfigurationSpace + ?Sized + 's> {
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<T: VirtioCapability> PciCapability for T {
|
||||
const ID: PciCapabilityId = PciCapabilityId::VendorSpecific;
|
||||
type CapabilityData<'a, S: PciConfigurationSpace + ?Sized + 'a> = T::Output<'a, S>;
|
||||
|
||||
fn check<S: PciConfigurationSpace + ?Sized>(space: &S, offset: usize, len: usize) -> bool {
|
||||
let cfg_type = space.read_u8(offset + 3);
|
||||
cfg_type == T::CFG_TYPE && len >= T::MIN_LEN
|
||||
}
|
||||
|
||||
fn data<'s, S: PciConfigurationSpace + ?Sized + 's>(
|
||||
space: &'s S,
|
||||
offset: usize,
|
||||
_len: usize,
|
||||
) -> Self::CapabilityData<'s, S> {
|
||||
T::Output::from_space_offset(space, offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioDeviceConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x04;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioDeviceConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioDeviceConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioCommonConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x01;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioCommonConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioCommonConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioNotifyConfigCapability {
|
||||
const CFG_TYPE: u8 = 0x02;
|
||||
const MIN_LEN: usize = 0x14;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioNotifyConfigData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioNotifyConfigData<'s, S> {
|
||||
pub fn offset_multiplier(&self) -> usize {
|
||||
self.space.read_u32(self.offset + 16) as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioNotifyConfigData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtioCapability for VirtioInterruptStatusCapability {
|
||||
const CFG_TYPE: u8 = 0x03;
|
||||
const MIN_LEN: usize = 1;
|
||||
type Output<'a, S: PciConfigurationSpace + ?Sized + 'a> = VirtioInterruptStatusData<'a, S>;
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioInterruptStatusData<'s, S> {
|
||||
pub fn read_status(&self) -> (bool, bool) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: PciConfigurationSpace + ?Sized + 's> VirtioCapabilityData<'s, S>
|
||||
for VirtioInterruptStatusData<'s, S>
|
||||
{
|
||||
fn from_space_offset(space: &'s S, offset: usize) -> Self {
|
||||
Self { space, offset }
|
||||
}
|
||||
|
||||
fn space(&self) -> &'s S {
|
||||
self.space
|
||||
}
|
||||
|
||||
fn offset(&self) -> usize {
|
||||
self.offset
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ use libk_util::{sync::spin_rwlock::IrqSafeRwLock, OneTimeInit};
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
capability::{MsiCapability, MsiXCapability, MsiXVectorTable},
|
||||
capability::{msi::MsiXVectorTable, MsiCapability, MsiXCapability, PciExpressCapability},
|
||||
driver::PciDriver,
|
||||
PciAddress, PciCommandRegister, PciConfigSpace, PciConfigurationSpace, PciSegmentInfo,
|
||||
};
|
||||
@@ -142,6 +142,23 @@ impl PciDeviceInfo {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn function_level_reset(&self) -> Result<(), Error> {
|
||||
if let Some(mut pcie) = self.config_space.capability::<PciExpressCapability>() {
|
||||
pcie.function_level_reset()
|
||||
} else {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hot_link_reset(&self) -> Result<(), Error> {
|
||||
if let Some(mut pcie) = self.config_space.capability::<PciExpressCapability>() {
|
||||
pcie.hot_link_reset();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_interrupts(&self, preferred_mode: PreferredInterruptMode) -> Result<(), Error> {
|
||||
self.interrupt_config
|
||||
.try_init_with(|| {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! PCI/PCIe bus interfaces
|
||||
#![no_std]
|
||||
#![feature(let_chains, decl_macro)]
|
||||
#![allow(clippy::missing_transmute_annotations, clippy::identity_op)]
|
||||
#![allow(clippy::missing_transmute_annotations)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
@@ -466,7 +466,7 @@ impl PciBusManager {
|
||||
for segment in this.segments.iter_mut() {
|
||||
for device in segment.devices.iter_mut() {
|
||||
let mut device = device.lock();
|
||||
if !f(&mut device)? {
|
||||
if !f(&mut *device)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -349,7 +349,7 @@ pub trait PciConfigurationSpace {
|
||||
}
|
||||
|
||||
/// Returns an iterator over the PCI capabilities
|
||||
fn capability_iter(&self) -> CapabilityIterator<'_, Self> {
|
||||
fn capability_iter(&self) -> CapabilityIterator<Self> {
|
||||
let status = PciStatusRegister::from_bits_retain(self.status());
|
||||
|
||||
let current = if status.contains(PciStatusRegister::CAPABILITIES_LIST) {
|
||||
@@ -374,7 +374,7 @@ pub trait PciConfigurationSpace {
|
||||
/// Locates a capability within this configuration space
|
||||
fn capability<C: PciCapability>(&self) -> Option<C::CapabilityData<'_, Self>> {
|
||||
self.capability_iter().find_map(|(id, offset, len)| {
|
||||
if id == Some(C::ID) && C::check(self, offset, len) {
|
||||
if id.map_or(false, |id| id == C::ID) && C::check(self, offset, len) {
|
||||
Some(C::data(self, offset, len))
|
||||
} else {
|
||||
None
|
||||
|
||||
@@ -131,7 +131,7 @@ impl Bbb {
|
||||
&mut self,
|
||||
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
) -> Result<usize, Error> {
|
||||
if buffer.is_empty() {
|
||||
if buffer.len() == 0 {
|
||||
return Ok(0);
|
||||
}
|
||||
let len = self
|
||||
|
||||
@@ -36,7 +36,7 @@ async fn extract_class_info(device: &UsbDeviceAccess) -> Result<Option<UsbClassI
|
||||
let device_info = &device.info;
|
||||
let config_info = device.query_configuration_info(0).await?;
|
||||
|
||||
if !config_info.interfaces.is_empty() {
|
||||
if config_info.interfaces.len() >= 1 {
|
||||
let if_info = &config_info.interfaces[0];
|
||||
|
||||
let class = if device_info.device_class == UsbDeviceClass::FromInterface {
|
||||
|
||||
@@ -166,7 +166,7 @@ impl UsbDeviceAccess {
|
||||
|
||||
pub fn read_current_configuration(
|
||||
&self,
|
||||
) -> IrqSafeRwLockReadGuard<'_, Option<UsbConfigurationInfo>> {
|
||||
) -> IrqSafeRwLockReadGuard<Option<UsbConfigurationInfo>> {
|
||||
self.current_configuration.read()
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ impl<'a> DirentIter<'a> {
|
||||
self.offset
|
||||
}
|
||||
|
||||
pub fn next_record(&mut self) -> Option<Record<'_, &[u8]>> {
|
||||
pub fn next_record(&mut self) -> Option<Record<&[u8]>> {
|
||||
if self.offset + size_of::<Dirent>() > self.block.len() {
|
||||
return None;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ impl<'a> DirentIterMut<'a> {
|
||||
Self { fs, block, offset }
|
||||
}
|
||||
|
||||
pub fn next_record(&mut self) -> Option<Record<'_, &mut [u8]>> {
|
||||
pub fn next_record(&mut self) -> Option<Record<&mut [u8]>> {
|
||||
if self.offset + size_of::<Dirent>() > self.block.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![feature(if_let_guard, impl_trait_in_assoc_type)]
|
||||
#![feature(if_let_guard, async_drop, impl_trait_in_assoc_type)]
|
||||
#![cfg_attr(not(test), no_std)]
|
||||
#![allow(clippy::new_ret_no_self)]
|
||||
|
||||
|
||||
@@ -160,7 +160,8 @@ impl Fat32FsInfo {
|
||||
let signature0 = &self.bytes[0..4];
|
||||
let signature1 = &self.bytes[484..488];
|
||||
|
||||
signature0 == Self::SIGNATURE0.to_le_bytes() && signature1 == Self::SIGNATURE1.to_le_bytes()
|
||||
signature0 == &Self::SIGNATURE0.to_le_bytes()
|
||||
&& signature1 == &Self::SIGNATURE1.to_le_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -303,7 +303,7 @@ impl CommonImpl for DirectoryNode {
|
||||
}
|
||||
|
||||
fn metadata(&self, _node: &NodeRef) -> Result<Metadata, Error> {
|
||||
Ok(self.metadata)
|
||||
Ok(self.metadata.clone())
|
||||
}
|
||||
|
||||
fn set_metadata(&self, _node: &NodeRef, _metadata: &Metadata) -> Result<(), Error> {
|
||||
@@ -357,7 +357,7 @@ impl DirectoryImpl for DirectoryNode {
|
||||
}
|
||||
|
||||
fn len(&self, _node: &NodeRef) -> Result<usize, Error> {
|
||||
Ok((self.size_bytes.unwrap_or(0) / DIRENT_SIZE as u32) as usize)
|
||||
Ok((self.size_bytes.unwrap_or(0) as u32 / DIRENT_SIZE as u32) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ impl Fat32Fs {
|
||||
) -> Result<NodeRef, Error> {
|
||||
let mut cached = true;
|
||||
for option in options {
|
||||
#[allow(clippy::single_match)]
|
||||
match option {
|
||||
FilesystemMountOption::Sync => cached = false,
|
||||
_ => (),
|
||||
|
||||
@@ -16,7 +16,7 @@ pub(crate) struct TarIterator<'a> {
|
||||
zero_blocks: usize,
|
||||
}
|
||||
|
||||
#[repr(C, packed)]
|
||||
#[repr(packed)]
|
||||
pub(crate) struct TarEntry {
|
||||
pub name: TarString<100>,
|
||||
pub mode: OctalField<8>,
|
||||
|
||||
@@ -125,31 +125,24 @@ impl<'a, M: MdioBus> PhyAccess<'a, M> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn setup_link(&self, have_pause: bool, force_gbesr: Option<GBESR>) -> Result<(), Error> {
|
||||
pub fn setup_link(&self, have_pause: bool, force_gbesr: GBESR) -> Result<(), Error> {
|
||||
let bmsr = BMSR::from(self.read_reg(REG_BMSR)?);
|
||||
let gbesr = if let Some(force_gbesr) = force_gbesr {
|
||||
let mut gbesr = if bmsr.contains(BMSR::EXT_STATUS_1000BASET) {
|
||||
GBESR::from(self.read_reg(REG_GBESR)?)
|
||||
} else {
|
||||
GBESR::empty()
|
||||
};
|
||||
gbesr |= force_gbesr;
|
||||
Some(gbesr)
|
||||
let mut gbesr = if bmsr.contains(BMSR::EXT_STATUS_1000BASET) {
|
||||
GBESR::from(self.read_reg(REG_GBESR)?)
|
||||
} else {
|
||||
None
|
||||
GBESR::empty()
|
||||
};
|
||||
gbesr |= force_gbesr;
|
||||
let mut anar = ANAR::from_capabilities(bmsr);
|
||||
if have_pause {
|
||||
anar |= ANAR::HAVE_PAUSE | ANAR::ASM_DIR;
|
||||
}
|
||||
let mut gbcr = GBCR::empty();
|
||||
if let Some(gbesr) = gbesr {
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_HALF) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_HALF;
|
||||
}
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_FULL) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_FULL;
|
||||
}
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_HALF) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_HALF;
|
||||
}
|
||||
if gbesr.contains(GBESR::HAVE_1000BASET_FULL) {
|
||||
gbcr |= GBCR::HAVE_1000BASET_FULL;
|
||||
}
|
||||
|
||||
self.write_reg(REG_ANAR, anar.bits())?;
|
||||
|
||||
@@ -127,7 +127,7 @@ pub fn register_interface(
|
||||
NetworkInterfaceType::Ethernet => {
|
||||
static LAST_ETHERNET_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
let eth_id = LAST_ETHERNET_ID.fetch_add(1, Ordering::SeqCst);
|
||||
format!("eth{eth_id}").into_boxed_str()
|
||||
format!("eth{}", eth_id).into_boxed_str()
|
||||
}
|
||||
NetworkInterfaceType::Loopback => "lo".into(),
|
||||
};
|
||||
|
||||
@@ -133,7 +133,7 @@ impl Route {
|
||||
|
||||
pub fn insert(route: Self) -> Result<(), Error> {
|
||||
// TODO check for conflicts
|
||||
log::debug!("Add route: {route}");
|
||||
log::debug!("Add route: {}", route);
|
||||
ROUTES.write().push(route);
|
||||
Ok(())
|
||||
}
|
||||
@@ -143,7 +143,7 @@ impl fmt::Display for Route {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{} ", self.subnet)?;
|
||||
if let Some(gw) = self.gateway {
|
||||
write!(f, " via {gw}")?;
|
||||
write!(f, " via {}", gw)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![feature(map_try_insert, let_chains)]
|
||||
#![feature(map_try_insert, let_chains, result_flattening)]
|
||||
#![allow(clippy::type_complexity, clippy::new_without_default)]
|
||||
#![no_std]
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ fn describe_route(route: &Route) -> RouteInfo {
|
||||
interface_name: interface.name.clone(),
|
||||
interface_id: route.interface,
|
||||
subnet: route.subnet,
|
||||
gateway: route.gateway,
|
||||
gateway: route.gateway.map(Into::into),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ enum OwnedAddress {
|
||||
Anonymous(u64),
|
||||
}
|
||||
|
||||
pub fn load_address(bytes: &[u8]) -> Result<LocalSocketAddress<'_>, Error> {
|
||||
pub fn load_address(bytes: &[u8]) -> Result<LocalSocketAddress, Error> {
|
||||
Ok(wire::from_slice(bytes)?)
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ pub fn write_ancillary(
|
||||
}
|
||||
|
||||
impl OwnedAddress {
|
||||
fn to_borrowed(&self) -> LocalSocketAddress<'_> {
|
||||
fn to_borrowed(&self) -> LocalSocketAddress {
|
||||
match self {
|
||||
Self::Path(path) => LocalSocketAddress::Path(path.as_ref()),
|
||||
Self::Anonymous(anon) => LocalSocketAddress::Anonymous(*anon),
|
||||
|
||||
@@ -110,9 +110,9 @@ impl LocalPacketSocket {
|
||||
///
|
||||
/// 1. If this socket is bound to a name, the name will be used
|
||||
/// 2. Otherwise:
|
||||
/// 2.1. If `remote` is "known" by this socket, the address in the table will be used
|
||||
/// 2.2. Otherwise, this socket is "paired" with the remote by generating a new anonymous
|
||||
/// address unique to that socket
|
||||
/// 2.1. If `remote` is "known" by this socket, the address in the table will be used
|
||||
/// 2.2. Otherwise, this socket is "paired" with the remote by generating a new anonymous
|
||||
/// address unique to that socket
|
||||
fn get_send_info(
|
||||
self: &Arc<Self>,
|
||||
remote: &OwnedAddress,
|
||||
|
||||
@@ -81,7 +81,7 @@ impl RawSocket {
|
||||
};
|
||||
|
||||
for id in ids {
|
||||
if except == Some(*id) {
|
||||
if except.map_or(false, |i| i == *id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -210,7 +210,7 @@ impl Socket for RawSocket {
|
||||
if message.payload.len() > 1024 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let mut builder = TxPacketBuilder::new(&interface, message.payload.len())?;
|
||||
let mut builder = TxPacketBuilder::new(&*interface, message.payload.len())?;
|
||||
builder.push_bytes(message.payload)?;
|
||||
// false to prevent loopback
|
||||
builder.transmit(Some(self.id))?;
|
||||
|
||||
@@ -70,7 +70,7 @@ impl TcpListener {
|
||||
pub(super) fn poll_accept(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<IrqSafeSpinlockGuard<'_, Vec<Arc<TcpStream>>>> {
|
||||
) -> Poll<IrqSafeSpinlockGuard<Vec<Arc<TcpStream>>>> {
|
||||
let lock = self.pending_accept.lock();
|
||||
self.accept_notify.register(cx.waker());
|
||||
if !lock.is_empty() {
|
||||
|
||||
@@ -214,7 +214,7 @@ impl TcpStream {
|
||||
pub(super) fn poll_receive(
|
||||
&self,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<IrqSafeRwLockWriteGuard<'_, TcpConnection>, Error>> {
|
||||
) -> Poll<Result<IrqSafeRwLockWriteGuard<TcpConnection>, Error>> {
|
||||
let lock = self.connection.write();
|
||||
match lock.poll_receive(cx) {
|
||||
Poll::Ready(Ok(())) => Poll::Ready(Ok(lock)),
|
||||
|
||||
@@ -26,15 +26,12 @@ use ygg_driver_pci::{
|
||||
};
|
||||
use yggdrasil_abi::net::{link::LinkState, MacAddress};
|
||||
|
||||
use crate::regs::Revision;
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
mod regs;
|
||||
mod ring;
|
||||
|
||||
struct Igbe {
|
||||
chip: Revision,
|
||||
regs: IrqSafeSpinlock<Regs>,
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
pci: PciDeviceInfo,
|
||||
@@ -46,9 +43,8 @@ struct Igbe {
|
||||
}
|
||||
|
||||
impl Igbe {
|
||||
pub fn new(dma: Arc<dyn DmaAllocator>, regs: Regs, chip: Revision, pci: PciDeviceInfo) -> Self {
|
||||
pub fn new(dma: Arc<dyn DmaAllocator>, regs: Regs, pci: PciDeviceInfo) -> Self {
|
||||
Self {
|
||||
chip,
|
||||
dma,
|
||||
pci,
|
||||
mac: OneTimeInit::new(),
|
||||
@@ -78,7 +74,7 @@ impl Device for Igbe {
|
||||
regs.reset(Duration::from_millis(200))?;
|
||||
// Intel 8257x manuals say an additional interrupt disable is needed after a global reset
|
||||
regs.disable_interrupts();
|
||||
regs.set_link_up(self.chip)?;
|
||||
regs.set_link_up()?;
|
||||
|
||||
// Initialize Rx
|
||||
regs.initialize_receiver(&rx_ring);
|
||||
@@ -179,10 +175,6 @@ impl NetworkDevice for Igbe {
|
||||
|
||||
pci_driver! {
|
||||
matches: [
|
||||
device (0x8086:0x100E), // 82540EM (E1000)
|
||||
device (0x8086:0x100C), // 82544GC (E1000)
|
||||
device (0x8086:0x100F), // 82545EM (E1000)
|
||||
device (0x8086:0x10D3), // 82574L (E1000E) [[BROKEN]]
|
||||
device (0x8086:0x10C9), // 82576 GbE
|
||||
device (0x8086:0x1502), // 82579LM GbE (Lewisville)
|
||||
],
|
||||
@@ -205,22 +197,11 @@ pci_driver! {
|
||||
}
|
||||
};
|
||||
|
||||
let chip = match info.device_id {
|
||||
0x100E | 0x100C | 0x100F => Revision::I8254x,
|
||||
0x10D3 => Revision::I82574L,
|
||||
0x10C9 => Revision::I82576,
|
||||
0x1502 => Revision::I82579LM,
|
||||
id => {
|
||||
log::error!("Invalid igbe chip variant: {id:#04x}");
|
||||
return Err(Error::InvalidOperation)
|
||||
},
|
||||
};
|
||||
|
||||
info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
info.set_command(true, use_mmio, !use_mmio, true);
|
||||
|
||||
let regs = unsafe { Regs::map(base) }?;
|
||||
let device = Igbe::new(dma.clone(), regs, chip, info.clone());
|
||||
let device = Igbe::new(dma.clone(), regs, info.clone());
|
||||
Ok(Arc::new(device))
|
||||
}
|
||||
|
||||
|
||||
@@ -42,14 +42,6 @@ pub trait Reg {
|
||||
const OFFSET: u16;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Revision {
|
||||
I8254x,
|
||||
I82574L,
|
||||
I82576,
|
||||
I82579LM,
|
||||
}
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub CTRL [
|
||||
@@ -337,7 +329,6 @@ impl MdioBus for Regs {
|
||||
|
||||
let mdic = self.inner.extract();
|
||||
if mdic.matches_all(MDIC::E::SET) {
|
||||
log::warn!("MDIO read error: phyaddr={phyaddr:#x}, regaddr={regaddr:#x}");
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
@@ -359,9 +350,6 @@ impl MdioBus for Regs {
|
||||
)?;
|
||||
|
||||
if self.inner.matches_all(MDIC::E::SET) {
|
||||
log::warn!(
|
||||
"MDIO write error: phyaddr={phyaddr:#x}, regaddr={regaddr:#x}, value={value:#x}"
|
||||
);
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
@@ -415,7 +403,7 @@ impl Regs {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_link_up(&mut self, chip: Revision) -> Result<(), Error> {
|
||||
pub fn set_link_up(&mut self) -> Result<(), Error> {
|
||||
self.inner
|
||||
.modify(CTRL::SLU::SET + CTRL::RFCE::SET + CTRL::TFCE::SET);
|
||||
|
||||
@@ -424,14 +412,8 @@ impl Regs {
|
||||
let (id0, id1) = phy.id()?;
|
||||
log::info!("PHY {:04x}:{:04x}", id0, id1);
|
||||
|
||||
phy.reset(Duration::from_millis(200))
|
||||
.inspect_err(|e| log::error!("PHY reset error {e:?}"))?;
|
||||
let force_gbesr = match chip {
|
||||
Revision::I82576 | Revision::I82579LM => Some(GBESR::empty()),
|
||||
_ => None,
|
||||
};
|
||||
phy.setup_link(true, force_gbesr)
|
||||
.inspect_err(|e| log::error!("PHY setup error: {e:?}"))?;
|
||||
phy.reset(Duration::from_millis(200))?;
|
||||
phy.setup_link(true, GBESR::empty())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user