Files
RedBear-OS/src/startup/memory.rs
T
vasilito 0f3840a5b5 absorb: 7 orphaned kernel patches re-applied (Phase 1.0A)
Per local/docs/PATCH-PRESERVATION-AUDIT-2026-07-12.md the kernel
fork was carrying only 21 of 45 patches in local/patches/kernel/.
The other 24 patches' content was silently missing from the fork
working tree, even though their .patch files were preserved.

This commit re-applies 7 patches that genuinely still apply
cleanly. The other 17 patches in the orphan list had hunks that
were already partially present in the fork (conservative audit
flagged them as orphan but the changes were material and only
partially diverged) or no longer apply (file was restructured
upstream). After this commit, the kernel fork reflects the
intended Red Bear work for:

- P1-memory-map-overflow: stack-guard on startup memory map
- P3-eventfd-kernel: scheme support for eventfd fd-table ops
- P5-context-mod-sched: context-switch optimization (mod.rs)
- P8-msi-foundation: MSI/MSI-X driver foundation (src/arch/x86_shared/device/msi.rs)
- P8-msi: device-level MSI plumbing (vector.rs)
- P9-proc-lock-ordering: scheme/proc lock ordering fix
- redox: Makefile patch

Untracked files msi.rs and vector.rs created by patch application.
mtn/ tree and proc.rs.orig cleaned up (leftovers from absolute-path
patch context lines).
2026-07-12 01:28:23 +03:00

453 lines
15 KiB
Rust

use crate::{
arch::CurrentRmmArch,
memory::PAGE_SIZE,
startup::{memory::BootloaderMemoryKind::Null, KernelArgs},
};
use core::{
cell::SyncUnsafeCell,
cmp::{max, min},
slice::{self, Iter},
};
use rmm::{
Arch, BumpAllocator, MemoryArea, PageFlags, PageMapper, PhysicalAddress, TableKind,
VirtualAddress, KILOBYTE, MEGABYTE,
};
// Keep synced with OsMemoryKind in bootloader
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(u64)]
#[allow(dead_code)]
pub enum BootloaderMemoryKind {
Null = 0,
Free = 1,
Reclaim = 2,
Reserved = 3,
// These are local to kernel
Kernel = 0x100,
Device = 0x101,
IdentityMap = 0x102,
}
// Keep synced with OsMemoryEntry in bootloader
#[derive(Clone, Copy, Debug)]
#[repr(C, packed(8))]
struct BootloaderMemoryEntry {
pub base: u64,
pub size: u64,
pub kind: BootloaderMemoryKind,
}
#[derive(Clone, Copy, Debug)]
struct MemoryEntry {
pub start: usize,
pub end: usize,
pub kind: BootloaderMemoryKind,
}
impl MemoryEntry {
fn intersect(&self, other: &Self) -> Option<Self> {
let start = max(self.start, other.start);
let end = min(self.end, other.end);
if start < end {
Some(Self {
start,
end,
kind: self.kind,
})
} else {
None
}
}
fn combine(&self, other: &Self) -> Option<Self> {
if self.start <= other.end && self.end >= other.start {
Some(Self {
start: min(self.start, other.start),
end: max(self.end, other.end),
kind: self.kind,
})
} else {
None
}
}
}
struct MemoryMap {
entries: [MemoryEntry; 1024],
size: usize,
}
impl MemoryMap {
fn register(&mut self, base: usize, size: usize, kind: BootloaderMemoryKind) {
if self.size >= self.entries.len() {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
unsafe { core::arch::asm!("out dx, al", in("dx") 0x3F8u16, in("al") b'!', options(nostack, preserves_flags)); }
panic!("Early memory map overflow at entry {} (max {})", self.size, self.entries.len());
}
let start = if kind == BootloaderMemoryKind::Free {
align_up(base)
} else {
align_down(base)
};
let end = base.saturating_add(size);
let end = if kind == BootloaderMemoryKind::Free {
align_down(end)
} else {
align_up(end)
};
if start < end
&& let Some(entry) = self.entries.get_mut(self.size)
{
*entry = MemoryEntry { start, end, kind };
self.size += 1;
}
}
fn iter(&self) -> Iter<'_, MemoryEntry> {
self.entries[0..self.size].iter()
}
pub fn free(&self) -> impl Iterator<Item = &MemoryEntry> {
self.iter().filter(|x| x.kind == BootloaderMemoryKind::Free)
}
pub fn non_free(&self) -> impl Iterator<Item = &MemoryEntry> {
self.iter().filter(|x| x.kind != BootloaderMemoryKind::Free)
}
pub fn kernel(&self) -> Option<&MemoryEntry> {
self.iter().find(|x| x.kind == BootloaderMemoryKind::Kernel)
}
pub fn devices(&self) -> impl Iterator<Item = &MemoryEntry> {
self.iter()
.filter(|x| x.kind == BootloaderMemoryKind::Device)
}
pub fn identity_mapped(&self) -> impl Iterator<Item = &MemoryEntry> {
self.iter()
.filter(|x| x.kind == BootloaderMemoryKind::IdentityMap)
}
}
static MEMORY_MAP: SyncUnsafeCell<MemoryMap> = SyncUnsafeCell::new(MemoryMap {
entries: [MemoryEntry {
start: 0,
end: 0,
kind: BootloaderMemoryKind::Null,
}; 1024],
size: 0,
});
fn align_up(x: usize) -> usize {
(x.saturating_add(PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE
}
fn align_down(x: usize) -> usize {
x / PAGE_SIZE * PAGE_SIZE
}
fn register_memory_from_kernel_args(args: &KernelArgs) {
register_bootloader_areas(args.areas_base as usize, args.areas_size as usize);
if let Some(dt) = args.dtb() {
crate::dtb::register_dev_memory_ranges(&dt);
}
register_memory_region(
args.kernel_base as usize,
args.kernel_size as usize,
BootloaderMemoryKind::Kernel,
);
register_memory_region(
args.env_base as usize,
args.env_size as usize,
BootloaderMemoryKind::IdentityMap,
);
register_memory_region(
args.hwdesc_base as usize,
args.hwdesc_size as usize,
BootloaderMemoryKind::IdentityMap,
);
register_memory_region(
args.bootstrap_base as usize,
args.bootstrap_size as usize,
BootloaderMemoryKind::IdentityMap,
);
}
pub fn register_memory_region(base: usize, size: usize, kind: BootloaderMemoryKind) {
if kind != Null && size != 0 {
debug!("Registering {:?} memory {:X} size {:X}", kind, base, size);
unsafe { (*MEMORY_MAP.get()).register(base, size, kind) }
}
}
fn register_bootloader_areas(areas_base: usize, areas_size: usize) {
let bootloader_areas = unsafe {
slice::from_raw_parts(
areas_base as *const BootloaderMemoryEntry,
areas_size / size_of::<BootloaderMemoryEntry>(),
)
};
for bootloader_area in bootloader_areas.iter() {
register_memory_region(
bootloader_area.base as usize,
bootloader_area.size as usize,
bootloader_area.kind,
)
}
}
unsafe fn add_memory(areas: &mut [MemoryArea], area_i: &mut usize, mut area: MemoryEntry) {
unsafe {
for reservation in (*MEMORY_MAP.get()).non_free() {
if area.end > reservation.start && area.end <= reservation.end {
info!(
"Memory {:X}:{:X} overlaps with reservation {:X}:{:X}",
area.start, area.end, reservation.start, reservation.end
);
area.end = reservation.start;
}
if area.start >= area.end {
return;
}
if area.start >= reservation.start && area.start < reservation.end {
info!(
"Memory {:X}:{:X} overlaps with reservation {:X}:{:X}",
area.start, area.end, reservation.start, reservation.end
);
area.start = reservation.end;
}
if area.start >= area.end {
return;
}
if area.start <= reservation.start && area.end > reservation.start {
info!(
"Memory {:X}:{:X} contains reservation {:X}:{:X}",
area.start, area.end, reservation.start, reservation.end
);
debug_assert!(area.start < reservation.start && reservation.end < area.end,
"Should've contained reservation entirely: memory block {:X}:{:X} reservation {:X}:{:X}",
area.start, area.end,
reservation.start, reservation.end
);
// recurse on first part of split memory block
add_memory(
areas,
area_i,
MemoryEntry {
end: reservation.start,
..area
},
);
// and continue with the second part
area.start = reservation.end;
}
debug_assert!(
area.intersect(reservation).is_none(),
"Intersects with reservation! memory block {:X}:{:X} reservation {:X}:{:X}",
area.start,
area.end,
reservation.start,
reservation.end
);
debug_assert!(
area.start < area.end,
"Empty memory block {:X}:{:X}",
area.start,
area.end
);
}
// Combine overlapping memory areas
let mut other_i = 0;
while other_i < *area_i {
let other = &areas[other_i];
let other = MemoryEntry {
start: other.base.data(),
end: other.base.data().saturating_add(other.size),
kind: BootloaderMemoryKind::Free,
};
if let Some(union) = area.combine(&other) {
debug!(
"{:X}:{:X} overlaps with area {:X}:{:X}, combining into {:X}:{:X}",
area.start, area.end, other.start, other.end, union.start, union.end
);
area = union;
*area_i -= 1; // delete the original memory chunk
areas[other_i] = areas[*area_i];
} else {
other_i = other_i.saturating_add(1);
}
}
areas[*area_i].base = PhysicalAddress::new(area.start);
areas[*area_i].size = area.end.saturating_sub(area.start);
*area_i += 1;
}
}
fn kernel_page_flags<A: Arch>(virt: VirtualAddress) -> PageFlags<A> {
use crate::kernel_executable_offsets::*;
let virt_addr = virt.data();
(if virt_addr >= __text_start() && virt_addr < __text_end() {
// Remap text read-only, execute
PageFlags::new().execute(true)
} else if virt_addr >= __rodata_start() && virt_addr < __rodata_end() {
// Remap rodata read-only, no execute
PageFlags::new()
} else {
// Remap everything else read-write, no execute
PageFlags::new().write(true)
})
.global(cfg!(all(target_arch = "x86_64", not(feature = "pti"))))
}
unsafe fn map_memory<A: Arch>(areas: &[MemoryArea], mut bump_allocator: &mut BumpAllocator<A>) {
unsafe {
let mut mapper = PageMapper::<A, _>::create(TableKind::Kernel, &mut bump_allocator)
.expect("failed to create Mapper");
// Map all physical areas at PHYS_OFFSET
for area in areas.iter() {
for i in 0..area.size / PAGE_SIZE {
let phys = area.base.add(i * PAGE_SIZE);
let virt = A::phys_to_virt(phys);
let flags = kernel_page_flags::<A>(virt);
let flush = mapper
.map_phys(virt, phys, flags)
.expect("failed to map frame");
flush.ignore(); // Not the active table
}
}
let kernel_area = (*MEMORY_MAP.get()).kernel().unwrap();
let kernel_base = kernel_area.start;
let kernel_size = kernel_area.end.saturating_sub(kernel_area.start);
// Map kernel at KERNEL_OFFSET
for i in 0..kernel_size / A::PAGE_SIZE {
let phys = PhysicalAddress::new(kernel_base + i * PAGE_SIZE);
let virt = VirtualAddress::new(
crate::kernel_executable_offsets::KERNEL_OFFSET() + i * PAGE_SIZE,
);
let flags = kernel_page_flags::<A>(virt);
let flush = mapper
.map_phys(virt, phys, flags)
.expect("failed to map frame");
flush.ignore(); // Not the active table
}
for area in (*MEMORY_MAP.get()).identity_mapped() {
let base = area.start;
let size = area.end.saturating_sub(area.start);
for i in 0..size / PAGE_SIZE {
let phys = PhysicalAddress::new(base + i * PAGE_SIZE);
let virt = A::phys_to_virt(phys);
let flags = kernel_page_flags::<A>(virt);
let flush = mapper
.map_phys(virt, phys, flags)
.expect("failed to map frame");
flush.ignore(); // Not the active table
}
}
//map dev mem
for area in (*MEMORY_MAP.get()).devices() {
let base = area.start;
let size = area.end.saturating_sub(area.start);
for i in 0..size / PAGE_SIZE {
let phys = PhysicalAddress::new(base + i * PAGE_SIZE);
let virt = A::phys_to_virt(phys);
let flags = kernel_page_flags::<A>(virt).device_memory(true);
let flush = mapper
.map_phys(virt, phys, flags)
.expect("failed to map frame");
flush.ignore(); // Not the active table
}
}
// Ensure graphical debug region remains paged
{
use crate::devices::graphical_debug::FRAMEBUFFER;
let (phys, virt, size) = *FRAMEBUFFER.lock();
let pages = size.div_ceil(PAGE_SIZE);
for i in 0..pages {
let phys = PhysicalAddress::new(phys + i * PAGE_SIZE);
let virt = VirtualAddress::new(virt + i * PAGE_SIZE);
let flags = PageFlags::new().write(true).write_combining(true);
let flush = mapper
.map_phys(virt, phys, flags)
.expect("failed to map frame");
flush.ignore(); // Not the active table
}
}
debug!("Table: {:X}", mapper.table().phys().data());
mapper.table().debug_entries(|args| debug!("{args}"));
// Use the new table
mapper.make_current();
}
}
pub unsafe fn init(
args: &KernelArgs,
low_limit: Option<usize>,
high_limit: Option<usize>,
) -> BumpAllocator<CurrentRmmArch> {
register_memory_from_kernel_args(args);
unsafe {
let physmem_limit = MemoryEntry {
start: align_up(low_limit.unwrap_or(0)),
end: align_down(high_limit.unwrap_or(usize::MAX)),
kind: BootloaderMemoryKind::Free,
};
let areas = &mut *crate::memory::AREAS.get();
let mut area_i = 0;
// Copy initial memory map, and page align it
for area in (*MEMORY_MAP.get()).free() {
debug!("{:X}:{:X}", area.start, area.end);
if let Some(area) = area.intersect(&physmem_limit) {
add_memory(areas, &mut area_i, area);
}
}
areas[..area_i].sort_unstable_by_key(|area| area.base);
crate::memory::AREA_COUNT.get().write(area_i as u16);
// free memory map in now ready
let areas = crate::memory::areas();
// First, calculate how much memory we have
let mut size = 0_usize;
for area in areas.iter() {
if area.size > 0 {
debug!("{:X?}", area);
size = size.saturating_add(area.size);
}
}
info!("Memory: {} MB", size.div_ceil(MEGABYTE));
// Create a basic allocator for the first pages
let mut bump_allocator = BumpAllocator::<CurrentRmmArch>::new(areas, 0);
map_memory(areas, &mut bump_allocator);
// Create the physical memory map
let offset = bump_allocator.offset();
info!("Permanently used: {} KB", offset.div_ceil(KILOBYTE));
bump_allocator
}
}