Populates Maxmem (#1319)

This commit is contained in:
Putta Khunchalee 2025-03-27 22:17:42 +09:00 committed by GitHub
parent 9401c58c4d
commit 3703853c03
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 89 additions and 43 deletions

View file

@ -1,5 +1,6 @@
pub use self::arch::*;
use alloc::sync::Arc;
use core::num::NonZero;
use macros::elf_note;
@ -10,5 +11,26 @@ mod arch;
pub const PAGE_SIZE: NonZero<usize> = NonZero::new(1 << PAGE_SHIFT).unwrap();
pub const PAGE_MASK: NonZero<usize> = NonZero::new(PAGE_SIZE.get() - 1).unwrap();
/// Runtime configurations for the kernel populated from [`config::Config`].
pub struct Config {
max_cpu: NonZero<usize>,
}
impl Config {
pub fn new(src: &'static ::config::Config) -> Arc<Self> {
Arc::new(Self {
max_cpu: src.max_cpu,
})
}
pub fn max_cpu(&self) -> NonZero<usize> {
self.max_cpu
}
pub fn env(&self, _: &str) -> Option<&'static str> {
todo!()
}
}
#[elf_note(section = ".note.obkrnl.page-size", name = "obkrnl", ty = 0)]
static NOTE_PAGE_SIZE: [u8; size_of::<usize>()] = PAGE_SIZE.get().to_ne_bytes();

View file

@ -1,7 +1,6 @@
use super::{PinnedContext, pin_cpu};
use super::{PinnedContext, current_config, pin_cpu};
use alloc::vec::Vec;
use core::ops::Deref;
use krt::config;
/// Encapsulates per-CPU value.
///
@ -15,7 +14,7 @@ pub struct CpuLocal<T>(Vec<T>);
impl<T> CpuLocal<T> {
pub fn new(mut f: impl FnMut(usize) -> T) -> Self {
let len = config().max_cpu.get();
let len = current_config().max_cpu().get();
let mut vec = Vec::with_capacity(len);
for i in 0..len {

View file

@ -3,6 +3,7 @@ pub use self::arch::*;
pub use self::local::*;
use crate::arch::ArchConfig;
use crate::config::Config;
use crate::proc::{ProcMgr, Thread};
use crate::uma::Uma;
use alloc::rc::Rc;
@ -23,6 +24,7 @@ mod local;
///
/// # Safety
/// - This function can be called only once per CPU.
/// - `config` must be the same object for all context.
/// - `arch` must be the same object for all context.
/// - `cpu` must be unique and valid.
/// - `setup` must return the same objects for all context.
@ -32,6 +34,7 @@ mod local;
/// |---------|--------|
/// |PS4 11.00|0x08DA70|
pub unsafe fn run_with_context(
config: Arc<Config>,
arch: Arc<ArchConfig>,
cpu: usize,
td: Arc<Thread>,
@ -42,6 +45,7 @@ pub unsafe fn run_with_context(
// on each CPU stack instead.
let mut cx = pin!(Context::new(
Base {
config: Arc::into_raw(config),
arch: Arc::into_raw(arch.clone()),
cpu,
thread: Arc::into_raw(td),
@ -66,6 +70,16 @@ pub unsafe fn run_with_context(
main();
}
/// # Interrupt safety
/// This function can be called from interrupt handler.
pub fn current_config() -> BorrowedArc<Config> {
// It does not matter if we are on a different CPU after we load the Context::arch because it is
// always the same for all CPU.
unsafe {
BorrowedArc::from_non_null(Context::load_static_ptr::<{ offset_of!(Base, config) }, _>())
}
}
/// # Interrupt safety
/// This function can be called from interrupt handler.
pub fn current_arch() -> BorrowedArc<ArchConfig> {
@ -156,6 +170,7 @@ pub struct ContextSetup {
/// panic handler, both of them does not require a CPU context.
#[repr(C)]
struct Base {
config: *const Config,
arch: *const ArchConfig,
cpu: usize, // pc_cpuid
thread: *const Thread, // pc_curthread

View file

@ -1,6 +1,7 @@
#![no_std]
#![cfg_attr(not(test), no_main)]
use self::config::Config;
use self::context::{ContextSetup, current_procmgr};
use self::imgact::Ps4Abi;
use self::malloc::KernelHeap;
@ -36,12 +37,13 @@ extern crate alloc;
///
/// See Orbis kernel entry point for a reference.
#[cfg_attr(target_os = "none", unsafe(no_mangle))]
fn main() -> ! {
fn main(config: &'static ::config::Config) -> ! {
// SAFETY: This function has a lot of restrictions. See Context documentation for more details.
info!("Starting Obliteration Kernel.");
// Setup the CPU after the first print to let the bootloader developer know (some of) their code
// are working.
let config = Config::new(config);
let arch = unsafe { self::arch::setup_main_cpu() };
// Setup proc0 to represent the kernel.
@ -54,7 +56,7 @@ fn main() -> ! {
// Activate CPU context.
let thread0 = Arc::new(thread0);
unsafe { self::context::run_with_context(arch, 0, thread0, setup, run) };
unsafe { self::context::run_with_context(config, arch, 0, thread0, setup, run) };
}
fn setup() -> ContextSetup {

View file

@ -1,8 +1,8 @@
pub use self::object::*;
use self::stats::VmStats;
use crate::config::{PAGE_MASK, PAGE_SIZE};
use crate::context::{current_arch, current_thread};
use crate::config::{PAGE_MASK, PAGE_SHIFT, PAGE_SIZE};
use crate::context::{current_arch, current_config, current_thread};
use crate::lock::GutexGroup;
use crate::proc::Proc;
use alloc::sync::{Arc, Weak};
@ -16,10 +16,11 @@ mod stats;
/// Implementation of Virtual Memory system.
pub struct Vm {
boot_area: u64, // basemem
boot_addr: u64, // boot_address
boot_tables: u64, // mptramp_pagetables
initial_memory_size: u64,
boot_area: u64, // basemem
boot_addr: u64, // boot_address
boot_tables: u64, // mptramp_pagetables
initial_memory_size: u64, // initial_memory_size
end_page: u64, // Maxmem
stats: [VmStats; 3],
pagers: [Weak<Proc>; 2], // pageproc
}
@ -62,6 +63,7 @@ impl Vm {
boot_addr: 0,
boot_tables: 0,
initial_memory_size: 0,
end_page: 0,
stats,
pagers: Default::default(),
};
@ -122,9 +124,9 @@ impl Vm {
/// |---------|--------|
/// |PS4 11.00|0x25CF00|
fn load_memory_map(&mut self) -> Result<(), VmError> {
// TODO: Some of the logic around physmap does not make sense.
// TODO: Some of the logic around here are very hard to understand.
let mut physmap = [0u64; 60];
let mut i = 0usize;
let mut last = 0usize;
let map = match boot_env() {
BootEnv::Vm(v) => v.memory_map.as_slice(),
};
@ -142,12 +144,14 @@ impl Vm {
break;
}
let mut insert_idx = i + 2;
// Check if we need to insert before the previous entries.
let mut insert_idx = last + 2;
let mut j = 0usize;
while j <= i {
while j <= last {
if m.base < physmap[j + 1] {
if physmap[j] < m.base + m.len {
// Check if end address overlapped.
if m.base + m.len > physmap[j] {
warn!("Overlapping memory regions, ignoring second region.");
continue 'top;
}
@ -159,19 +163,23 @@ impl Vm {
j += 2;
}
if insert_idx <= i && m.base + m.len == physmap[insert_idx] {
// Check if end address is the start address of the next entry. If yes we just change
// base address of it to increase its size.
if insert_idx <= last && m.base + m.len == physmap[insert_idx] {
physmap[insert_idx] = m.base;
continue;
}
// Check if start address is the end address of the previous entry. If yes we just
// increase the size of previous entry.
if insert_idx > 0 && m.base == physmap[insert_idx - 1] {
physmap[insert_idx - 1] = m.base + m.len;
continue;
}
i += 2;
last += 2;
if i == physmap.len() {
if last == physmap.len() {
warn!("Too many segments in the physical address map, giving up.");
break;
}
@ -179,7 +187,7 @@ impl Vm {
// This loop does not make sense on the Orbis. It seems like if this loop once
// entered it will never exit.
#[allow(clippy::while_immutable_condition)]
while insert_idx < i {
while insert_idx < last {
todo!()
}
@ -187,10 +195,10 @@ impl Vm {
physmap[insert_idx + 1] = m.base + m.len;
}
// Check if bootloader provide us a memory map.
let physmap = &mut physmap[..i];
if physmap.is_empty() {
// Check if bootloader provide us a memory map. The Orbis will check if
// preload_search_info() return null but we can't do that since we use a static size array
// to pass this information.
if physmap[1] == 0 {
return Err(VmError::NoMemoryMap);
}
@ -198,16 +206,16 @@ impl Vm {
let page_size = PAGE_SIZE.get().try_into().unwrap();
let page_mask = !u64::try_from(PAGE_MASK.get()).unwrap();
for e in physmap.chunks(2) {
for i in (0..=last).step_by(2) {
// Check if BIOS boot area.
if e[0] == 0 {
if physmap[i] == 0 {
// TODO: Why 1024?
self.boot_area = e[1] / 1024;
self.boot_area = physmap[i + 1] / 1024;
}
// Add to initial memory size.
let start = e[0].next_multiple_of(page_size);
let end = e[1] & page_mask;
let start = physmap[i].next_multiple_of(page_size);
let end = physmap[i + 1] & page_mask;
self.initial_memory_size += end.saturating_sub(start);
}
@ -220,6 +228,13 @@ impl Vm {
// what is the point of the logic on the above to find boot_area?
physmap[1] = self.adjust_boot_area(physmap[1] / 1024);
// Get end page.
self.end_page = physmap[last + 1] >> PAGE_SHIFT;
if let Some(v) = current_config().env("hw.physmem") {
self.end_page = v.parse::<u64>().unwrap() >> PAGE_SHIFT;
}
Ok(())
}

View file

@ -1,4 +1,4 @@
use config::{BootEnv, Config};
use config::BootEnv;
use core::ptr::null;
pub fn boot_env() -> &'static BootEnv {
@ -6,19 +6,12 @@ pub fn boot_env() -> &'static BootEnv {
unsafe { &*BOOT_ENV }
}
pub fn config() -> &'static Config {
// SAFETY: This is safe because the setup() requirements.
unsafe { &*CONFIG }
}
/// # Safety
/// This function must be called immediately in the [_start](super::_start) function. After that it
/// must never be called again.
#[allow(dead_code)]
pub(super) unsafe fn setup(env: &'static BootEnv, conf: &'static Config) {
pub(super) unsafe fn setup(env: &'static BootEnv) {
unsafe { BOOT_ENV = env };
unsafe { CONFIG = conf };
}
static mut BOOT_ENV: *const BootEnv = null();
static mut CONFIG: *const Config = null();

View file

@ -1,4 +1,4 @@
//! Minical Rust runtime for the kernel.
//! Minimal Rust runtime for the kernel.
//!
//! This crate provides foundations for the kernel to run. Its contains panic handler, console I/O
//! and other stuff. All of the provided functionalities here can be used immediately when
@ -26,10 +26,10 @@ mod panic;
/// 3. Only main CPU can execute this function.
#[cfg(target_os = "none")]
#[unsafe(no_mangle)]
extern "C" fn _start(env: &'static ::config::BootEnv, conf: &'static ::config::Config) -> ! {
extern "C" fn _start(env: &'static ::config::BootEnv, config: &'static ::config::Config) -> ! {
// SAFETY: We call it as the first thing here.
unsafe { self::config::setup(env, conf) };
main();
unsafe { self::config::setup(env) };
main(config);
}
#[allow(dead_code)]
@ -47,5 +47,5 @@ fn panic(i: &PanicInfo) -> ! {
#[cfg(target_os = "none")]
unsafe extern "Rust" {
safe fn main() -> !;
safe fn main(config: &'static ::config::Config) -> !;
}