Merge upstream/master into submodule/kernel
Sync the kernel fork with 10 upstream commits (context timer-separation with unblock_context/wakeup_context, dtb MMIO/bus-address translation + tests, event read_with_timeout support, proc wakeup paths, dup2 rewrite with rollback, futex Weak context_lock + unblock_context, memory/init ordering, and more). Satisfies the verify-fork-functions gate (11 previously-missing upstream functions now present). Conflicts resolved (5 files; 9 auto-merged): - src/syscall/futex.rs: took upstream's version wholesale (Weak context_lock, upgrade+unblock_context, TimeSpec::to_nanos, get_futex_stat, addr_space strong_count cleanup); re-applied the RB FUTEX_WAIT_MULTIPLE/FUTEX_REQUEUE implementations adapted to the Weak API, with wake-index bookkeeping preserved (Context::futex_wake_index survives in context.rs). - src/event.rs: merged EventQueue with both RB's fd-refcount refs and upstream's timeout_opt (init in new()); kept RB's EventCounter/ eventfd block; added upstream's read_with_timeout + EVENT_TIMEOUT_ID write handling; deduplicated EAGAIN/EINTR imports. - src/scheme/proc.rs: upstream imports minus UnmapVec (RB deleted that machinery from context/memory.rs — import-only dependency); ContextHandle::Start keeps RB's require_zero_offset + upstream's wakeup_context; FORCEKILL region takes upstream's is_dead()/being_sigkilled form. - src/syscall/fs.rs: took upstream's dup2 structure (natural self-dup-with-buf handling — preserves the relibc dup2(ft,ft, "refresh") semantic — plus rollback on insert failure), adapted to the fork's 4-arg duplicate_file and read/token_split conventions. - src/arch/aarch64/start.rs: kept upstream's new PageMapper/ acpi::init_before_mem/memory::init_mm init block. Verified: repo cook kernel --force-rebuild successful; verify-fork-functions.sh --no-fetch kernel passes.
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
target
|
target
|
||||||
|
/build
|
||||||
/config.toml
|
/config.toml
|
||||||
|
/sysroot
|
||||||
.gitlab-ci-local/
|
.gitlab-ci-local/
|
||||||
|
|||||||
+13
-34
@@ -1,8 +1,3 @@
|
|||||||
image: "redoxos/redoxer:latest"
|
|
||||||
|
|
||||||
variables:
|
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
|
||||||
|
|
||||||
workflow:
|
workflow:
|
||||||
rules:
|
rules:
|
||||||
- if: '$CI_PROJECT_NAMESPACE == "redox-os"'
|
- if: '$CI_PROJECT_NAMESPACE == "redox-os"'
|
||||||
@@ -12,14 +7,12 @@ stages:
|
|||||||
- build
|
- build
|
||||||
- cross-build
|
- cross-build
|
||||||
- test
|
- test
|
||||||
- other-features
|
|
||||||
# TODO: benchmarks and profiling (maybe manually enabled for relevant MRs)?
|
# TODO: benchmarks and profiling (maybe manually enabled for relevant MRs)?
|
||||||
|
|
||||||
x86_64:
|
x86_64:
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- mkdir -p target/${ARCH}
|
- redoxer env make
|
||||||
- redoxer env make BUILD=target/${ARCH}
|
|
||||||
variables:
|
variables:
|
||||||
ARCH: "x86_64"
|
ARCH: "x86_64"
|
||||||
|
|
||||||
@@ -27,64 +20,50 @@ aarch64:
|
|||||||
stage: cross-build
|
stage: cross-build
|
||||||
image: "redoxos/redoxer:aarch64"
|
image: "redoxos/redoxer:aarch64"
|
||||||
script:
|
script:
|
||||||
- mkdir -p target/${ARCH}
|
- redoxer env make
|
||||||
- redoxer env make BUILD=target/${ARCH}
|
|
||||||
variables:
|
variables:
|
||||||
ARCH: "aarch64"
|
ARCH: "aarch64"
|
||||||
|
|
||||||
i586:
|
i586:
|
||||||
stage: cross-build
|
stage: cross-build
|
||||||
script:
|
script:
|
||||||
- mkdir -p target/${ARCH}
|
- TARGET=${ARCH}-unknown-redox redoxer env make
|
||||||
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
|
|
||||||
variables:
|
variables:
|
||||||
ARCH: "i586"
|
ARCH: "i586"
|
||||||
|
|
||||||
riscv64gc:
|
riscv64gc:
|
||||||
stage: cross-build
|
stage: cross-build
|
||||||
script:
|
script:
|
||||||
- mkdir -p target/${ARCH}
|
- TARGET=${ARCH}-unknown-redox redoxer env make
|
||||||
- TARGET=${ARCH}-unknown-redox redoxer env make BUILD=target/${ARCH}
|
|
||||||
variables:
|
variables:
|
||||||
ARCH: "riscv64gc"
|
ARCH: "riscv64gc"
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
stage: build
|
stage: build
|
||||||
script:
|
script:
|
||||||
- rustup component add rustfmt
|
- redoxer env cargo fmt --check
|
||||||
- rustfmt --check
|
|
||||||
|
|
||||||
x86_64:boot:
|
x86_64:boot:
|
||||||
stage: test
|
stage: test
|
||||||
needs: [x86_64]
|
needs: [x86_64]
|
||||||
script:
|
script:
|
||||||
- mkdir -p target/${ARCH}
|
|
||||||
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
|
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
|
||||||
- redoxer env make BUILD=target/${ARCH}
|
- redoxer env make test
|
||||||
- timeout -s KILL 9m redoxer exec --folder target/${ARCH}/:/usr/lib/boot uname -a
|
|
||||||
variables:
|
|
||||||
ARCH: "x86_64"
|
|
||||||
|
|
||||||
x86_64:relibc:
|
x86_64:relibc:
|
||||||
stage: test
|
stage: test
|
||||||
needs: [x86_64]
|
needs: [x86_64]
|
||||||
|
# TODO: remove
|
||||||
|
allow_failure: true
|
||||||
script:
|
script:
|
||||||
- redoxer pkg relibc-tests-bins
|
|
||||||
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
|
- export COOKBOOK_SOURCE_IDENT=$CI_COMMIT_SHA
|
||||||
- mkdir -p target/${TARGET}/sysroot/{usr/lib/boot,root} target/${TARGET}/root
|
- timeout -s KILL 9m redoxer env make test-relibc
|
||||||
- redoxer env make BUILD=target/${TARGET}/sysroot/usr/lib/boot
|
|
||||||
- (cd target/${TARGET}/sysroot && mv home/user/relibc-tests/* root/)
|
|
||||||
- timeout -s KILL 9m redoxer exec --folder target/${TARGET}/sysroot/:/ make run
|
|
||||||
# It is fine if failing sometimes
|
|
||||||
allow_failure: true
|
|
||||||
variables:
|
|
||||||
TARGET: "x86_64-unknown-redox"
|
|
||||||
|
|
||||||
profiling-compile:
|
x86_64:profiling:
|
||||||
stage: other-features
|
stage: build
|
||||||
allow_failure: true
|
needs: [x86_64]
|
||||||
script:
|
script:
|
||||||
make check
|
- make check
|
||||||
variables:
|
variables:
|
||||||
ARCH: "x86_64"
|
ARCH: "x86_64"
|
||||||
KERNEL_CHECK_FEATURES: profiling
|
KERNEL_CHECK_FEATURES: profiling
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
.PHONY: all check
|
.PHONY: all check
|
||||||
|
|
||||||
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
BUILD?=$(CURDIR)
|
|
||||||
export RUST_TARGET_PATH=$(SOURCE)/targets
|
export RUST_TARGET_PATH=$(SOURCE)/targets
|
||||||
|
|
||||||
ifeq ($(TARGET),)
|
ifeq ($(TARGET),)
|
||||||
@@ -11,6 +10,9 @@ else
|
|||||||
ARCH?=$(shell echo "$(TARGET)" | cut -d - -f1)
|
ARCH?=$(shell echo "$(TARGET)" | cut -d - -f1)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
BUILD?=$(CURDIR)/build/$(ARCH)
|
||||||
|
DESTDIR?=./sysroot
|
||||||
|
|
||||||
ifeq ($(ARCH),riscv64gc)
|
ifeq ($(ARCH),riscv64gc)
|
||||||
override ARCH:=riscv64
|
override ARCH:=riscv64
|
||||||
GNU_TARGET=riscv64-unknown-redox
|
GNU_TARGET=riscv64-unknown-redox
|
||||||
@@ -21,6 +23,7 @@ else
|
|||||||
GNU_TARGET=$(ARCH)-unknown-redox
|
GNU_TARGET=$(ARCH)-unknown-redox
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
OBJCOPY?=$(GNU_TARGET)-objcopy
|
||||||
|
|
||||||
all: $(BUILD)/kernel $(BUILD)/kernel.sym
|
all: $(BUILD)/kernel $(BUILD)/kernel.sym
|
||||||
|
|
||||||
@@ -31,7 +34,10 @@ TARGET_SPEC=$(RUST_TARGET_PATH)/$(ARCH)-unknown-kernel.json
|
|||||||
|
|
||||||
KERNEL_CARGO_FEATURES?=
|
KERNEL_CARGO_FEATURES?=
|
||||||
|
|
||||||
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f)
|
$(BUILD):
|
||||||
|
mkdir -p "$@"
|
||||||
|
|
||||||
|
$(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell find $(SOURCE) -name "*.rs" -type f) | $(BUILD)
|
||||||
cargo rustc \
|
cargo rustc \
|
||||||
--bin kernel \
|
--bin kernel \
|
||||||
--manifest-path "$(MANIFEST)" \
|
--manifest-path "$(MANIFEST)" \
|
||||||
@@ -45,13 +51,13 @@ $(BUILD)/kernel.all: $(LD_SCRIPT) $(LOCKFILE) $(MANIFEST) $(TARGET_SPEC) $(shell
|
|||||||
--emit link="$(BUILD)/kernel.all"
|
--emit link="$(BUILD)/kernel.all"
|
||||||
|
|
||||||
$(BUILD)/kernel.sym: $(BUILD)/kernel.all
|
$(BUILD)/kernel.sym: $(BUILD)/kernel.all
|
||||||
$(GNU_TARGET)-objcopy \
|
$(OBJCOPY) \
|
||||||
--only-keep-debug \
|
--only-keep-debug \
|
||||||
"$(BUILD)/kernel.all" \
|
"$(BUILD)/kernel.all" \
|
||||||
"$(BUILD)/kernel.sym"
|
"$(BUILD)/kernel.sym"
|
||||||
|
|
||||||
$(BUILD)/kernel: $(BUILD)/kernel.all
|
$(BUILD)/kernel: $(BUILD)/kernel.all
|
||||||
$(GNU_TARGET)-objcopy \
|
$(OBJCOPY) \
|
||||||
--strip-debug \
|
--strip-debug \
|
||||||
"$(BUILD)/kernel.all" \
|
"$(BUILD)/kernel.all" \
|
||||||
"$(BUILD)/kernel"
|
"$(BUILD)/kernel"
|
||||||
@@ -65,3 +71,29 @@ check:
|
|||||||
--target "$(TARGET_SPEC)" \
|
--target "$(TARGET_SPEC)" \
|
||||||
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -Z json-target-spec \
|
-Z build-std=core,alloc -Zbuild-std-features=compiler-builtins-mem -Z json-target-spec \
|
||||||
--features=$(KERNEL_CHECK_FEATURES)
|
--features=$(KERNEL_CHECK_FEATURES)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build sysroot target config.toml
|
||||||
|
|
||||||
|
install: all
|
||||||
|
@mkdir -pv "$(DESTDIR)/usr/lib/boot/"
|
||||||
|
cp -v $(BUILD)/kernel.all "$(DESTDIR)/usr/lib/boot/"
|
||||||
|
cp -v $(BUILD)/kernel.sym "$(DESTDIR)/usr/lib/boot/"
|
||||||
|
cp -v $(BUILD)/kernel "$(DESTDIR)/usr/lib/boot/"
|
||||||
|
|
||||||
|
# test if booting
|
||||||
|
# to ensure it's using this kernel, set COOKBOOK_SOURCE_IDENT env before build
|
||||||
|
test: all
|
||||||
|
$(MAKE) install
|
||||||
|
REDOXER_SYSROOT=$(DESTDIR) redoxer exec uname -a
|
||||||
|
|
||||||
|
# test with interactive gui
|
||||||
|
test-gui: all
|
||||||
|
$(MAKE) install
|
||||||
|
REDOXER_SYSROOT=$(DESTDIR) redoxer exec --gui ion
|
||||||
|
|
||||||
|
# test with relibc tests
|
||||||
|
test-relibc: all
|
||||||
|
$(MAKE) install
|
||||||
|
REDOXER_SYSROOT=$(DESTDIR) redoxer pkg relibc-tests-bins
|
||||||
|
REDOXER_SYSROOT=$(DESTDIR) redoxer exec relibc-tests-runner
|
||||||
|
|||||||
+19
-7
@@ -6,6 +6,7 @@ use core::ptr::NonNull;
|
|||||||
use alloc::{boxed::Box, string::String, vec::Vec};
|
use alloc::{boxed::Box, string::String, vec::Vec};
|
||||||
|
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use rmm::{BumpAllocator, FrameAllocator, PageMapper};
|
||||||
use spin::{Once, RwLock};
|
use spin::{Once, RwLock};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -33,7 +34,11 @@ mod spcr;
|
|||||||
pub mod srat;
|
pub mod srat;
|
||||||
mod xsdt;
|
mod xsdt;
|
||||||
|
|
||||||
unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::memory::PageMapper) {
|
unsafe fn map_linearly(
|
||||||
|
addr: PhysicalAddress,
|
||||||
|
len: usize,
|
||||||
|
mapper: &mut PageMapper<RmmA, impl FrameAllocator>,
|
||||||
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data()));
|
let base = PhysicalAddress::new(crate::memory::round_down_pages(addr.data()));
|
||||||
let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data()));
|
let aligned_len = crate::memory::round_up_pages(len + (addr.data() - base.data()));
|
||||||
@@ -50,7 +55,10 @@ unsafe fn map_linearly(addr: PhysicalAddress, len: usize, mapper: &mut crate::me
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_sdt(sdt_address: PhysicalAddress, mapper: &mut KernelMapper<true>) -> &'static Sdt {
|
pub fn get_sdt(
|
||||||
|
sdt_address: PhysicalAddress,
|
||||||
|
mapper: &mut PageMapper<RmmA, impl FrameAllocator>,
|
||||||
|
) -> &'static Sdt {
|
||||||
let sdt;
|
let sdt;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -93,16 +101,20 @@ impl Rxsdt for RxsdtEnum {
|
|||||||
|
|
||||||
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
|
pub static RXSDT_ENUM: Once<RxsdtEnum> = Once::new();
|
||||||
|
|
||||||
/// Initialses the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages.
|
/// Initialises the global `RXSDT_ENUM` if RSDT or XSDT was found and maps the SDT pages.
|
||||||
/// It does not perform any allocations
|
///
|
||||||
pub unsafe fn init_before_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
|
/// It does not use `TheFrameAllocator` nor does it heap-allocate.
|
||||||
|
pub unsafe fn init_before_mem(
|
||||||
|
already_supplied_rsdp: Option<NonNull<u8>>,
|
||||||
|
mapper: &mut PageMapper<RmmA, BumpAllocator<RmmA>>,
|
||||||
|
) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Search for RSDP
|
// Search for RSDP
|
||||||
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
|
let rsdp_opt = Rsdp::get_rsdp(already_supplied_rsdp);
|
||||||
|
|
||||||
if let Some(rsdp) = rsdp_opt {
|
if let Some(rsdp) = rsdp_opt {
|
||||||
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
|
debug!("SDT address: {:#x}", rsdp.sdt_address().data());
|
||||||
let rxsdt = get_sdt(rsdp.sdt_address(), &mut KernelMapper::lock_rw());
|
let rxsdt = get_sdt(rsdp.sdt_address(), mapper);
|
||||||
|
|
||||||
let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) {
|
let rxsdt = if let Some(rsdt) = Rsdt::new(rxsdt) {
|
||||||
let mut initialized = false;
|
let mut initialized = false;
|
||||||
@@ -139,7 +151,7 @@ pub unsafe fn init_before_mem(already_supplied_rsdp: Option<NonNull<u8>>) {
|
|||||||
// TODO: Don't touch ACPI tables in kernel?
|
// TODO: Don't touch ACPI tables in kernel?
|
||||||
|
|
||||||
for sdt in rxsdt.iter() {
|
for sdt in rxsdt.iter() {
|
||||||
get_sdt(sdt, &mut KernelMapper::lock_rw());
|
get_sdt(sdt, mapper);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!("NO RSDP FOUND");
|
error!("NO RSDP FOUND");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use super::InterruptController;
|
use super::InterruptController;
|
||||||
use crate::{
|
use crate::{
|
||||||
dtb::{
|
dtb::{
|
||||||
get_mmio_address,
|
|
||||||
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
||||||
|
translate_mmio_address,
|
||||||
},
|
},
|
||||||
sync::CleanLockToken,
|
sync::CleanLockToken,
|
||||||
};
|
};
|
||||||
@@ -60,7 +60,7 @@ impl GenericInterruptController {
|
|||||||
if chunk.size.is_none() {
|
if chunk.size.is_none() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let addr = get_mmio_address(fdt, node, &chunk).unwrap();
|
let addr = translate_mmio_address(fdt, node, &chunk).unwrap();
|
||||||
match idx {
|
match idx {
|
||||||
0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()),
|
0 => (regs.0, regs.1) = (addr, chunk.size.unwrap()),
|
||||||
2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()),
|
2 => (regs.2, regs.3) = (addr, chunk.size.unwrap()),
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ use fdt::{node::NodeProperty, Fdt};
|
|||||||
use super::{gic::GicDistIf, InterruptController};
|
use super::{gic::GicDistIf, InterruptController};
|
||||||
use crate::{
|
use crate::{
|
||||||
dtb::{
|
dtb::{
|
||||||
get_mmio_address,
|
|
||||||
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
irqchip::{InterruptHandler, IrqCell, IrqDesc},
|
||||||
|
translate_mmio_address,
|
||||||
},
|
},
|
||||||
sync::CleanLockToken,
|
sync::CleanLockToken,
|
||||||
};
|
};
|
||||||
@@ -53,7 +53,7 @@ impl GicV3 {
|
|||||||
// Read registers
|
// Read registers
|
||||||
let mut chunks = node.reg().unwrap();
|
let mut chunks = node.reg().unwrap();
|
||||||
if let Some(gicd) = chunks.next()
|
if let Some(gicd) = chunks.next()
|
||||||
&& let Some(addr) = get_mmio_address(fdt, &node, &gicd)
|
&& let Some(addr) = translate_mmio_address(fdt, &node, &gicd)
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
self.gic_dist_if.init(crate::PHYS_OFFSET + addr);
|
self.gic_dist_if.init(crate::PHYS_OFFSET + addr);
|
||||||
@@ -62,7 +62,7 @@ impl GicV3 {
|
|||||||
for _ in 0..gicrs {
|
for _ in 0..gicrs {
|
||||||
if let Some(gicr) = chunks.next() {
|
if let Some(gicr) = chunks.next() {
|
||||||
self.gicrs.push((
|
self.gicrs.push((
|
||||||
get_mmio_address(fdt, &node, &gicr).unwrap(),
|
translate_mmio_address(fdt, &node, &gicr).unwrap(),
|
||||||
gicr.size.unwrap(),
|
gicr.size.unwrap(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,15 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
|
|||||||
// Initialize paging
|
// Initialize paging
|
||||||
paging::init();
|
paging::init();
|
||||||
|
|
||||||
|
let mut mapper = rmm::PageMapper::current(rmm::TableKind::Kernel, bump_allocator);
|
||||||
|
#[cfg(feature = "acpi")]
|
||||||
|
{
|
||||||
|
use crate::acpi;
|
||||||
|
acpi::init_before_mem(args.acpi_rsdp(), &mut mapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::memory::init_mm(mapper.allocator_mut());
|
||||||
|
|
||||||
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
|
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
|
||||||
|
|
||||||
// Setup kernel heap
|
// Setup kernel heap
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Initialize RMM
|
// Initialize RMM
|
||||||
crate::startup::memory::init(&args, None, None);
|
let mut bump_allocator = crate::startup::memory::init(&args, None, None);
|
||||||
|
|
||||||
let boot_hart_id =
|
let boot_hart_id =
|
||||||
get_boot_hart_id(args.env()).expect("Didn't get boot HART id from bootloader");
|
get_boot_hart_id(args.env()).expect("Didn't get boot HART id from bootloader");
|
||||||
@@ -114,6 +114,8 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs) -> ! {
|
|||||||
|
|
||||||
paging::init();
|
paging::init();
|
||||||
|
|
||||||
|
crate::memory::init_mm(&mut bump_allocator);
|
||||||
|
|
||||||
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
|
crate::arch::misc::init(crate::cpu_set::LogicalCpuId::new(0));
|
||||||
|
|
||||||
// Setup kernel heap
|
// Setup kernel heap
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
//! defined in other files inside of the `arch` module
|
//! defined in other files inside of the `arch` module
|
||||||
use core::{arch::naked_asm, cell::SyncUnsafeCell, mem::offset_of};
|
use core::{arch::naked_asm, cell::SyncUnsafeCell, mem::offset_of};
|
||||||
|
|
||||||
|
use rmm::PageMapper;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
allocator,
|
allocator,
|
||||||
arch::{device, gdt, idt, interrupt, paging},
|
arch::{device, gdt, idt, interrupt, paging},
|
||||||
@@ -112,13 +114,14 @@ unsafe extern "C" fn start(args_ptr: *const KernelArgs, stack_end: usize) -> ! {
|
|||||||
// Initialize paging
|
// Initialize paging
|
||||||
paging::init();
|
paging::init();
|
||||||
|
|
||||||
|
let mut mapper = PageMapper::current(rmm::TableKind::Kernel, bump_allocator);
|
||||||
if cfg!(feature = "acpi") {
|
if cfg!(feature = "acpi") {
|
||||||
crate::acpi::init_before_mem(args.acpi_rsdp());
|
crate::acpi::init_before_mem(args.acpi_rsdp(), &mut mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
numa::init(&mut bump_allocator);
|
numa::init(mapper.allocator_mut());
|
||||||
|
|
||||||
crate::memory::init_mm(bump_allocator);
|
crate::memory::init_mm(mapper.allocator_mut());
|
||||||
|
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
crate::arch::alternative::early_init(true);
|
crate::arch::alternative::early_init(true);
|
||||||
|
|||||||
@@ -333,7 +333,6 @@ impl Context {
|
|||||||
pub fn unblock_no_ipi(&mut self) -> bool {
|
pub fn unblock_no_ipi(&mut self) -> bool {
|
||||||
if self.status.is_soft_blocked() {
|
if self.status.is_soft_blocked() {
|
||||||
self.status = Status::Runnable;
|
self.status = Status::Runnable;
|
||||||
self.wake = None;
|
|
||||||
self.status_reason = "";
|
self.status_reason = "";
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
+37
-4
@@ -9,13 +9,17 @@ use alloc::{
|
|||||||
use core::{cmp::Reverse, num::NonZeroUsize, ops::Deref};
|
use core::{cmp::Reverse, num::NonZeroUsize, ops::Deref};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::memory::AddrSpaceWrapper,
|
context::{
|
||||||
|
memory::AddrSpaceWrapper,
|
||||||
|
switch::{BASE_SLICE_TICKS, NANOS_PER_TICK, SCALE, SCHED_PRIO_TO_WEIGHT, TICK_INTERVAL},
|
||||||
|
},
|
||||||
cpu_set::LogicalCpuSet,
|
cpu_set::LogicalCpuSet,
|
||||||
|
ipi::{ipi, IpiKind, IpiTarget},
|
||||||
memory::{RmmA, RmmArch, TableKind},
|
memory::{RmmA, RmmArch, TableKind},
|
||||||
percpu::PercpuBlock,
|
percpu::PercpuBlock,
|
||||||
sync::{
|
sync::{
|
||||||
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
|
ArcRwLockWriteGuard, CleanLockToken, LockToken, Mutex, MutexGuard, RwLock, RwLockReadGuard,
|
||||||
RwLockWriteGuard, L0, L1, L2, L4,
|
RwLockWriteGuard, L0, L1, L2, L3, L4,
|
||||||
},
|
},
|
||||||
syscall::error::Result,
|
syscall::error::Result,
|
||||||
};
|
};
|
||||||
@@ -83,7 +87,7 @@ static IDLE_CONTEXTS: Mutex<L2, VecDeque<WeakContextRef>> = Mutex::new(VecDeque:
|
|||||||
pub struct RunContextData {
|
pub struct RunContextData {
|
||||||
// queue: VecDeque<WeakContextRef>,
|
// queue: VecDeque<WeakContextRef>,
|
||||||
queue: BTreeMap<(u64, Reverse<u64>, u32), (u64, u64, WeakContextRef)>, // ((vd, rem_slice, ctxt_id), (vtime, weight, context))
|
queue: BTreeMap<(u64, Reverse<u64>, u32), (u64, u64, WeakContextRef)>, // ((vd, rem_slice, ctxt_id), (vtime, weight, context))
|
||||||
timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context)
|
timers: BTreeSet<(u128, WeakContextRef)>, // (wake, context)
|
||||||
count: usize,
|
count: usize,
|
||||||
v: u64,
|
v: u64,
|
||||||
total_weight: u64,
|
total_weight: u64,
|
||||||
@@ -135,6 +139,36 @@ pub fn run_contexts_try(token: LockToken<'_, L0>) -> Option<MutexGuard<'_, L1, R
|
|||||||
RUN_CONTEXTS.try_lock(token)
|
RUN_CONTEXTS.try_lock(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unblock_context(context_lock: &Arc<ContextLock>, token: &mut LockToken<'_, L3>) -> bool {
|
||||||
|
let cpu_id = {
|
||||||
|
let mut guard = context_lock.write(token.token());
|
||||||
|
if !guard.unblock_no_ipi() {
|
||||||
|
if guard.status.is_runnable() {
|
||||||
|
// already set to runnable externally
|
||||||
|
wakeup_context(context_lock);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
guard.cpu_id
|
||||||
|
};
|
||||||
|
|
||||||
|
wakeup_context(context_lock);
|
||||||
|
|
||||||
|
if let Some(cpu_id) = cpu_id
|
||||||
|
&& cpu_id != crate::cpu_id()
|
||||||
|
{
|
||||||
|
ipi(IpiKind::Wakeup, IpiTarget::Other);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wakeup_context(context_lock: &Arc<ContextLock>) {
|
||||||
|
let percpu: &PercpuBlock = PercpuBlock::current();
|
||||||
|
let weak = WeakContextRef(Arc::downgrade(context_lock));
|
||||||
|
percpu.switch_internals.wakeup_list.borrow_mut().push(weak);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init(token: &mut CleanLockToken) {
|
pub fn init(token: &mut CleanLockToken) {
|
||||||
let owner = None; // kmain not owned by any fd
|
let owner = None; // kmain not owned by any fd
|
||||||
let mut context = Context::new(owner).expect("failed to create kmain context");
|
let mut context = Context::new(owner).expect("failed to create kmain context");
|
||||||
@@ -261,7 +295,6 @@ pub fn spawn(
|
|||||||
let context_lock = Arc::new(ContextLock::new(context));
|
let context_lock = Arc::new(ContextLock::new(context));
|
||||||
let context_ref = ContextRef(Arc::clone(&context_lock));
|
let context_ref = ContextRef(Arc::clone(&context_lock));
|
||||||
let run_ref = WeakContextRef(Arc::downgrade(&context_ref.0));
|
let run_ref = WeakContextRef(Arc::downgrade(&context_ref.0));
|
||||||
idle_contexts(token.downgrade()).push_back(run_ref);
|
|
||||||
contexts_mut(token.downgrade()).insert(context_ref);
|
contexts_mut(token.downgrade()).insert(context_ref);
|
||||||
|
|
||||||
Ok(context_lock)
|
Ok(context_lock)
|
||||||
|
|||||||
+34
-11
@@ -13,7 +13,7 @@ use crate::{
|
|||||||
percpu::PercpuBlock,
|
percpu::PercpuBlock,
|
||||||
sync::{ArcRwLockWriteGuard, CleanLockToken, L4},
|
sync::{ArcRwLockWriteGuard, CleanLockToken, L4},
|
||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::{sync::Arc, vec::Vec};
|
||||||
use core::{
|
use core::{
|
||||||
cell::{Cell, RefCell},
|
cell::{Cell, RefCell},
|
||||||
cmp::Reverse,
|
cmp::Reverse,
|
||||||
@@ -32,16 +32,16 @@ enum UpdateResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A simple geometric series where value[i] ~= value[i + 1] * 1.25
|
// A simple geometric series where value[i] ~= value[i + 1] * 1.25
|
||||||
const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
pub const SCHED_PRIO_TO_WEIGHT: [usize; 40] = [
|
||||||
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
|
88761, 71755, 56483, 46273, 36291, 29154, 23254, 18705, 14949, 11916, 9548, 7620, 6100, 4904,
|
||||||
3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87,
|
3906, 3121, 2501, 1991, 1586, 1277, 1024, 820, 655, 526, 423, 335, 272, 215, 172, 137, 110, 87,
|
||||||
70, 56, 45, 36, 29, 23, 18, 15,
|
70, 56, 45, 36, 29, 23, 18, 15,
|
||||||
];
|
];
|
||||||
|
|
||||||
const SCALE: u128 = 1 << 40;
|
pub const SCALE: u128 = 1 << 40;
|
||||||
const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms
|
pub const TICK_INTERVAL: u64 = 3; // Approx 6.75 ms
|
||||||
const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms
|
pub const BASE_SLICE_TICKS: u64 = TICK_INTERVAL * 3; // Approx 20.25 ms
|
||||||
const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms
|
pub const NANOS_PER_TICK: u128 = 2_250_000; // 2.25 ms
|
||||||
|
|
||||||
/// Determines if a given context is eligible to be scheduled on a given CPU (in
|
/// Determines if a given context is eligible to be scheduled on a given CPU (in
|
||||||
/// principle, the current CPU).
|
/// principle, the current CPU).
|
||||||
@@ -190,6 +190,8 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
let mut wakeups = wakeup_contexts(token);
|
let mut wakeups = wakeup_contexts(token);
|
||||||
let mut push_idle: SmallVec<[WeakContextRef; 16]> = SmallVec::new();
|
let mut push_idle: SmallVec<[WeakContextRef; 16]> = SmallVec::new();
|
||||||
|
|
||||||
|
// These timers coukd have expired
|
||||||
|
let mut timers: SmallVec<[(u128, WeakContextRef); 16]> = SmallVec::new();
|
||||||
if let Some(mut run_contexts) = run_contexts_try(token.token()) {
|
if let Some(mut run_contexts) = run_contexts_try(token.token()) {
|
||||||
// Pop Timers
|
// Pop Timers
|
||||||
while let Some((wake, _)) = run_contexts.timers.first() {
|
while let Some((wake, _)) = run_contexts.timers.first() {
|
||||||
@@ -197,12 +199,31 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((_, context_ref)) = run_contexts.timers.pop_first() {
|
if let Some(entry) = run_contexts.timers.pop_first() {
|
||||||
wakeups.push(context_ref);
|
timers.push(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (wake, context_ref) in timers {
|
||||||
|
let Some(context_lock) = context_ref.upgrade() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let guard = context_lock.read(token.token());
|
||||||
|
if guard.status.is_soft_blocked() && guard.wake == Some(wake) {
|
||||||
|
wakeups.push(context_ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drain from percpu
|
||||||
|
{
|
||||||
|
let mut percpu_wake = percpu.switch_internals.wakeup_list.borrow_mut();
|
||||||
|
for context_ref in percpu_wake.drain(..) {
|
||||||
|
wakeups.push(context_ref.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if wakeups.len() > 0 {
|
if wakeups.len() > 0 {
|
||||||
let mut run_contexts = run_contexts(token.token());
|
let mut run_contexts = run_contexts(token.token());
|
||||||
for context_ref in wakeups {
|
for context_ref in wakeups {
|
||||||
@@ -388,9 +409,7 @@ pub fn switch(token: &mut CleanLockToken) -> SwitchResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wakeup_contexts(
|
fn wakeup_contexts(token: &mut CleanLockToken) -> SmallVec<[WeakContextRef; 16]> {
|
||||||
token: &mut CleanLockToken,
|
|
||||||
) -> SmallVec<[WeakContextRef; 16]> {
|
|
||||||
// TODO: Optimise this somehow
|
// TODO: Optimise this somehow
|
||||||
let mut wakeups = SmallVec::new();
|
let mut wakeups = SmallVec::new();
|
||||||
let current_context = context::current();
|
let current_context = context::current();
|
||||||
@@ -687,6 +706,9 @@ pub struct ContextSwitchPercpu {
|
|||||||
/// The idle process.
|
/// The idle process.
|
||||||
idle_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
idle_ctxt: RefCell<Option<Arc<ContextLock>>>,
|
||||||
pub(crate) being_sigkilled: Cell<bool>,
|
pub(crate) being_sigkilled: Cell<bool>,
|
||||||
|
|
||||||
|
// wakeups
|
||||||
|
pub(crate) wakeup_list: RefCell<Vec<WeakContextRef>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextSwitchPercpu {
|
impl ContextSwitchPercpu {
|
||||||
@@ -698,6 +720,7 @@ impl ContextSwitchPercpu {
|
|||||||
current_ctxt: RefCell::new(None),
|
current_ctxt: RefCell::new(None),
|
||||||
idle_ctxt: RefCell::new(None),
|
idle_ctxt: RefCell::new(None),
|
||||||
being_sigkilled: Cell::new(false),
|
being_sigkilled: Cell::new(false),
|
||||||
|
wakeup_list: RefCell::new(Vec::new()),
|
||||||
|
|
||||||
#[cfg(feature = "profiling")]
|
#[cfg(feature = "profiling")]
|
||||||
current_dbg_id: core::sync::atomic::AtomicU32::new(!0),
|
current_dbg_id: core::sync::atomic::AtomicU32::new(!0),
|
||||||
|
|||||||
+187
-43
@@ -102,53 +102,145 @@ pub fn register_dev_memory_ranges(dt: &Fdt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let Some(soc_node) = dt.find_node("/soc") else {
|
if let Some(soc_node) = dt.find_node("/soc") {
|
||||||
warn!("failed to find /soc in devicetree");
|
if let Some(reg) = soc_node.ranges() {
|
||||||
return;
|
for chunk in reg {
|
||||||
};
|
debug!(
|
||||||
let Some(reg) = soc_node.ranges() else {
|
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
|
||||||
warn!("devicetree /soc has no ranges");
|
chunk.child_bus_address_hi,
|
||||||
return;
|
chunk.child_bus_address,
|
||||||
};
|
chunk.parent_bus_address,
|
||||||
for chunk in reg {
|
chunk.size
|
||||||
debug!(
|
);
|
||||||
"dev mem 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x}",
|
|
||||||
chunk.child_bus_address_hi,
|
|
||||||
chunk.child_bus_address,
|
|
||||||
chunk.parent_bus_address,
|
|
||||||
chunk.size
|
|
||||||
);
|
|
||||||
|
|
||||||
/*TODO: soc memory may contain all free memory!
|
/*TODO: soc memory may contain all free memory!
|
||||||
register_memory_region(
|
register_memory_region(
|
||||||
chunk.parent_bus_address,
|
chunk.parent_bus_address,
|
||||||
chunk.size,
|
chunk.size,
|
||||||
BootloaderMemoryKind::Device,
|
BootloaderMemoryKind::Device,
|
||||||
);*/
|
);*/
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!("devicetree /soc has no ranges");
|
||||||
|
}
|
||||||
|
|
||||||
// also add all soc-internal devices because they might not be shown in ranges
|
// also add direct /soc children because they might not be shown in
|
||||||
// (identity-mapped soc bus may have empty ranges)
|
// ranges (an identity-mapped bus may have empty ranges)
|
||||||
for device in soc_node.children() {
|
for device in soc_node.children() {
|
||||||
if let Some(reg) = device.reg() {
|
if let Some(reg) = device.reg() {
|
||||||
for entry in reg {
|
for entry in reg {
|
||||||
if let Some(size) = entry.size {
|
if let Some(size) = entry.size {
|
||||||
let addr = entry.starting_address as usize;
|
let addr = entry.starting_address as usize;
|
||||||
if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) {
|
if let Some(mapped_addr) = get_mmio_address(dt, &device, &entry) {
|
||||||
debug!(
|
debug!(
|
||||||
"soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}",
|
"soc device {} 0x{:08x} -> 0x{:08x} size 0x{:08x}",
|
||||||
device.name, addr, mapped_addr, size
|
device.name, addr, mapped_addr, size
|
||||||
);
|
);
|
||||||
register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device);
|
register_memory_region(mapped_addr, size, BootloaderMemoryKind::Device);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
warn!("failed to find /soc in devicetree");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The selected console may be below a nested bus whose own `reg` range
|
||||||
|
// does not cover all children. Register the exact translated range so it
|
||||||
|
// remains mapped after the boot-time identity map is replaced.
|
||||||
|
if let Some((address, size, _, _, _)) = diag_uart_range(dt) {
|
||||||
|
debug!(
|
||||||
|
"diagnostic UART 0x{:08x} size 0x{:08x}",
|
||||||
|
address.data(),
|
||||||
|
size
|
||||||
|
);
|
||||||
|
register_memory_region(address.data(), size, BootloaderMemoryKind::Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt controllers are not required to live below /soc. Some
|
||||||
|
// devicetrees place the primary GIC directly below the root node, so its
|
||||||
|
// register ranges would otherwise be absent from the kernel physmap.
|
||||||
|
if let Some(root) = dt.find_node("/") {
|
||||||
|
for controller in root
|
||||||
|
.children()
|
||||||
|
.filter(|node| node.property("interrupt-controller").is_some())
|
||||||
|
{
|
||||||
|
let Some(regions) = controller.reg() else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for region in regions {
|
||||||
|
let Some(size) = region.size else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let Some(address) = translate_mmio_address(dt, &controller, ®ion) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
debug!(
|
||||||
|
"root interrupt controller {} 0x{:08x} size 0x{:08x}",
|
||||||
|
controller.name, address, size
|
||||||
|
);
|
||||||
|
register_memory_region(address, size, BootloaderMemoryKind::Device);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME return PhysicalAddress
|
fn same_node(left: FdtNode<'_, '_>, right: FdtNode<'_, '_>) -> bool {
|
||||||
pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
|
if left.name != right.name {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// FdtNode is reconstructed while walking the tree and has no identity
|
||||||
|
// operation. A property's value is a slice into the original DTB, so equal
|
||||||
|
// `reg` slice pointers identify the same node occurrence without relying
|
||||||
|
// on names that may repeat below different buses.
|
||||||
|
match (left.property("reg"), right.property("reg")) {
|
||||||
|
(Some(left_reg), Some(right_reg)) => core::ptr::eq(left_reg.value, right_reg.value),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_bus_address(bus: FdtNode<'_, '_>, address: usize, size: usize) -> Option<usize> {
|
||||||
|
let ranges_property = bus.property("ranges")?;
|
||||||
|
if ranges_property.value.is_empty() {
|
||||||
|
return Some(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
let last_offset = size.saturating_sub(1);
|
||||||
|
let ranges = bus.ranges()?;
|
||||||
|
for range in ranges {
|
||||||
|
let Some(offset) = address.checked_sub(range.child_bus_address) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
if offset < range.size && last_offset < range.size - offset {
|
||||||
|
return range.parent_bus_address.checked_add(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate_from_subtree(
|
||||||
|
bus_or_device: FdtNode<'_, '_>,
|
||||||
|
target: FdtNode<'_, '_>,
|
||||||
|
address: usize,
|
||||||
|
size: usize,
|
||||||
|
) -> Option<usize> {
|
||||||
|
if same_node(bus_or_device, target) {
|
||||||
|
return Some(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
for child in bus_or_device.children() {
|
||||||
|
if let Some(child_address) = translate_from_subtree(child, target, address, size) {
|
||||||
|
// `bus_or_device` is the bus parent of the subtree that matched.
|
||||||
|
return translate_bus_address(bus_or_device, child_address, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Translate a device's bus-relative `reg` address through every ancestor's
|
||||||
|
/// `ranges` property until it reaches the CPU physical address space.
|
||||||
|
pub fn translate_mmio_address(fdt: &Fdt, device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
|
||||||
/* DT spec 2.3.8 "ranges":
|
/* DT spec 2.3.8 "ranges":
|
||||||
* The ranges property provides a means of defining a mapping or translation between
|
* The ranges property provides a means of defining a mapping or translation between
|
||||||
* the address space of the bus (the child address space) and the address space of the bus
|
* the address space of the bus (the child address space) and the address space of the bus
|
||||||
@@ -159,15 +251,32 @@ pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) ->
|
|||||||
* children of the node and the parent address space.
|
* children of the node and the parent address space.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// FIXME assumes all the devices are connected to CPUs via the /soc bus
|
let address = region.starting_address as usize;
|
||||||
|
let size = region.size.unwrap_or(1);
|
||||||
|
let root = fdt.find_node("/")?;
|
||||||
|
|
||||||
|
// The root is already the CPU address space, so only its children perform
|
||||||
|
// translations. This also supports devices that are not under `/soc`.
|
||||||
|
for child in root.children() {
|
||||||
|
if let Some(translated) = translate_from_subtree(child, *device, address, size) {
|
||||||
|
translated.checked_add(size.saturating_sub(1))?;
|
||||||
|
return Some(translated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME return PhysicalAddress
|
||||||
|
pub fn get_mmio_address(fdt: &Fdt, _device: &FdtNode, region: &MemoryRegion) -> Option<usize> {
|
||||||
let mut mapped_addr = region.starting_address as usize;
|
let mut mapped_addr = region.starting_address as usize;
|
||||||
let size = region.size.unwrap_or(0).saturating_sub(1);
|
let size = region.size.unwrap_or(0).saturating_sub(1);
|
||||||
let last_address = mapped_addr.saturating_add(size);
|
let last_address = mapped_addr.saturating_add(size);
|
||||||
if let Some(parent) = fdt.find_node("/soc") {
|
if let Some(parent) = fdt.find_node("/soc") {
|
||||||
let mut ranges = parent.ranges().map(|f| f.peekable())?;
|
let mut ranges = parent.ranges().map(|ranges| ranges.peekable())?;
|
||||||
if ranges.peek().is_some() {
|
if ranges.peek().is_some() {
|
||||||
let parent_range = ranges.find(|x| {
|
let parent_range = ranges.find(|range| {
|
||||||
x.child_bus_address <= mapped_addr && last_address - x.child_bus_address <= x.size
|
range.child_bus_address <= mapped_addr
|
||||||
|
&& last_address - range.child_bus_address <= range.size
|
||||||
})?;
|
})?;
|
||||||
mapped_addr = parent_range
|
mapped_addr = parent_range
|
||||||
.parent_bus_address
|
.parent_bus_address
|
||||||
@@ -219,7 +328,7 @@ pub fn diag_uart_range<'a>(dtb: &'a Fdt) -> Option<(PhysicalAddress, usize, bool
|
|||||||
|
|
||||||
let mut reg = uart_node.reg()?;
|
let mut reg = uart_node.reg()?;
|
||||||
let memory = reg.next()?;
|
let memory = reg.next()?;
|
||||||
let address = get_mmio_address(dtb, &uart_node, &memory)?;
|
let address = translate_mmio_address(dtb, &uart_node, &memory)?;
|
||||||
|
|
||||||
Some((
|
Some((
|
||||||
PhysicalAddress::new(address),
|
PhysicalAddress::new(address),
|
||||||
@@ -244,3 +353,38 @@ pub fn fill_env_data(dt: &Fdt, env_base: usize) -> usize {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::translate_mmio_address;
|
||||||
|
use fdt::Fdt;
|
||||||
|
|
||||||
|
static MMIO_DTB: &[u8] = include_bytes!("testdata/mmio.dtb");
|
||||||
|
|
||||||
|
fn translated(path: &str) -> Option<usize> {
|
||||||
|
let fdt = Fdt::new(MMIO_DTB).unwrap();
|
||||||
|
let node = fdt.find_node(path).unwrap();
|
||||||
|
let region = node.reg().unwrap().next().unwrap();
|
||||||
|
translate_mmio_address(&fdt, &node, ®ion)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn translates_uart_through_nested_and_empty_ranges() {
|
||||||
|
assert_eq!(translated("/soc/bus@1000/serial@200"), Some(0x1200));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn accepts_region_ending_exactly_at_range_boundary() {
|
||||||
|
assert_eq!(translated("/soc/bus@1000/device@ff0"), Some(0x1ff0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rejects_region_crossing_range_boundary() {
|
||||||
|
assert_eq!(translated("/soc/bus@1000/device@ff1"), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_ranges_is_identity_mapping() {
|
||||||
|
assert_eq!(translated("/identity-bus/device@3000"), Some(0x3000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Vendored
BIN
Binary file not shown.
Vendored
+48
@@ -0,0 +1,48 @@
|
|||||||
|
/dts-v1/;
|
||||||
|
|
||||||
|
/ {
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
|
||||||
|
chosen {
|
||||||
|
stdout-path = "/soc/bus@1000/serial@200:115200n8";
|
||||||
|
};
|
||||||
|
|
||||||
|
soc {
|
||||||
|
compatible = "simple-bus";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
bus@1000 {
|
||||||
|
compatible = "simple-bus";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <1>;
|
||||||
|
reg = <0x0 0x1000 0x0 0x1000>;
|
||||||
|
ranges = <0x0 0x0 0x1000 0x1000>;
|
||||||
|
|
||||||
|
serial@200 {
|
||||||
|
reg = <0x200 0x18>;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@ff0 {
|
||||||
|
reg = <0xff0 0x10>;
|
||||||
|
};
|
||||||
|
|
||||||
|
device@ff1 {
|
||||||
|
reg = <0xff1 0x10>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
identity-bus {
|
||||||
|
compatible = "simple-bus";
|
||||||
|
#address-cells = <2>;
|
||||||
|
#size-cells = <2>;
|
||||||
|
ranges;
|
||||||
|
|
||||||
|
device@3000 {
|
||||||
|
reg = <0x0 0x3000 0x0 0x20>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
+58
-1
@@ -2,7 +2,7 @@ use alloc::sync::Arc;
|
|||||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
|
use hashbrown::{hash_map::DefaultHashBuilder, HashMap};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use syscall::data::GlobalSchemes;
|
use syscall::{data::GlobalSchemes, EAGAIN, EINTR};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context,
|
context,
|
||||||
@@ -29,6 +29,9 @@ pub struct EventQueue {
|
|||||||
/// handle to the SAME queue; the queue must survive until the last fd
|
/// handle to the SAME queue; the queue must survive until the last fd
|
||||||
/// closes. Starts at 1 for the fd created by `open`.
|
/// closes. Starts at 1 for the fd created by `open`.
|
||||||
refs: AtomicUsize,
|
refs: AtomicUsize,
|
||||||
|
/// Pending EVENT_TIMEOUT_ID timeout recorded by `write()` and consumed
|
||||||
|
/// by `read_with_timeout()` (upstream event-timeout support).
|
||||||
|
timeout_opt: Mutex<L1, Option<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const EVENTFD_COUNTER_MAX: u64 = u64::MAX - 1;
|
const EVENTFD_COUNTER_MAX: u64 = u64::MAX - 1;
|
||||||
@@ -48,6 +51,7 @@ impl EventQueue {
|
|||||||
id,
|
id,
|
||||||
queue: WaitQueue::new(),
|
queue: WaitQueue::new(),
|
||||||
refs: AtomicUsize::new(1),
|
refs: AtomicUsize::new(1),
|
||||||
|
timeout_opt: Mutex::new(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,8 +75,61 @@ impl EventQueue {
|
|||||||
.receive_into_user(buf, block, "EventQueue::read", token)
|
.receive_into_user(buf, block, "EventQueue::read", token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_with_timeout(
|
||||||
|
&self,
|
||||||
|
buf: UserSliceWo,
|
||||||
|
timeout: usize,
|
||||||
|
token: &mut CleanLockToken,
|
||||||
|
) -> Result<usize> {
|
||||||
|
// if zero, instant timeout
|
||||||
|
let block = timeout > 0;
|
||||||
|
if block {
|
||||||
|
let mut time = crate::time::monotonic(token);
|
||||||
|
time += timeout as u128 * 1_000_000;
|
||||||
|
context::current().write(token.token()).wake = Some(time);
|
||||||
|
}
|
||||||
|
let r = self
|
||||||
|
.queue
|
||||||
|
.receive_into_user(buf, block, "EventQueue::read_with_timeout", token);
|
||||||
|
match (r, block) {
|
||||||
|
(Ok(r), _) => return Ok(r),
|
||||||
|
(err @ Err(Error { errno: EINTR }), true) => {
|
||||||
|
let old_wake = context::current().write(token.token()).wake.take();
|
||||||
|
// The scheduler clears `wake` on timeout
|
||||||
|
if !old_wake.is_none() {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
// proceed writing timeout
|
||||||
|
}
|
||||||
|
(Err(Error { errno: EAGAIN }), false) => {
|
||||||
|
// proceed writing timeout
|
||||||
|
}
|
||||||
|
(err, _) => return err,
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: let the scheduler write this for us?
|
||||||
|
let event = Event {
|
||||||
|
id: syscall::EVENT_TIMEOUT_ID,
|
||||||
|
// TODO: it's undefined when flags is written to EVENT_TIMEOUT_ID
|
||||||
|
flags: EventFlags::EVENT_READ,
|
||||||
|
data: timeout,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes_copied = buf.copy_common_bytes_from_slice(&event)?;
|
||||||
|
return Ok(bytes_copied);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write(&self, events: &[Event], token: &mut CleanLockToken) -> Result<usize> {
|
pub fn write(&self, events: &[Event], token: &mut CleanLockToken) -> Result<usize> {
|
||||||
for event in events {
|
for event in events {
|
||||||
|
if event.id == syscall::EVENT_TIMEOUT_ID {
|
||||||
|
if event.flags.is_empty() {
|
||||||
|
self.timeout_opt.lock(token.token()).take();
|
||||||
|
} else {
|
||||||
|
*self.timeout_opt.lock(token.token()) = Some(event.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let file = {
|
let file = {
|
||||||
let context_ref = context::current();
|
let context_ref = context::current();
|
||||||
let mut context = context_ref.read(token.token());
|
let mut context = context_ref.read(token.token());
|
||||||
|
|||||||
+24
-18
@@ -74,30 +74,36 @@ pub fn total_frames() -> usize {
|
|||||||
|
|
||||||
/// Allocate a range of frames
|
/// Allocate a range of frames
|
||||||
pub fn allocate_p2frame(order: u32) -> Option<Frame> {
|
pub fn allocate_p2frame(order: u32) -> Option<Frame> {
|
||||||
let initial_index = get_round_robin_index();
|
static RR_INDEX: Mutex<u8> = Mutex::new(0);
|
||||||
let mut index = initial_index;
|
let len = FREE_LISTS.get().unwrap().len();
|
||||||
|
|
||||||
loop {
|
if len == 1 {
|
||||||
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, index).map(|(f, _)| f)
|
return allocate_p2frame_complex(order, (), None, order, 0).map(|e| e.0);
|
||||||
{
|
}
|
||||||
return Some(frame);
|
|
||||||
}
|
let index = {
|
||||||
index = get_round_robin_index();
|
let mut lock = RR_INDEX.lock();
|
||||||
if index == initial_index {
|
let index =
|
||||||
return None;
|
usize::try_from(*lock).expect("Maximum number of memory regions supported is 128");
|
||||||
|
*lock = u8::try_from((index + 1) % len).expect("Maximum number of memory regions is 128");
|
||||||
|
index
|
||||||
|
};
|
||||||
|
for i in index..len {
|
||||||
|
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) {
|
||||||
|
return Some(frame.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i in 0..index {
|
||||||
|
if let Some(frame) = allocate_p2frame_complex(order, (), None, order, i) {
|
||||||
|
return Some(frame.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
pub fn allocate_frame() -> Option<Frame> {
|
pub fn allocate_frame() -> Option<Frame> {
|
||||||
allocate_p2frame(0)
|
allocate_p2frame(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_round_robin_index() -> usize {
|
|
||||||
static CURRENT_INDEX: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
let len = FREE_LISTS.get().unwrap().len();
|
|
||||||
CURRENT_INDEX.fetch_add(1, Ordering::Relaxed) % len
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Flags, strategy
|
// TODO: Flags, strategy
|
||||||
pub fn allocate_p2frame_complex(
|
pub fn allocate_p2frame_complex(
|
||||||
_req_order: u32,
|
_req_order: u32,
|
||||||
@@ -543,7 +549,7 @@ const _: () = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
fn init_sections(mut allocator: &mut BumpAllocator<RmmA>) {
|
||||||
let number_of_memory_regions = numa::number_of_memory_regions();
|
let number_of_memory_regions = numa::number_of_memory_regions();
|
||||||
|
|
||||||
let (free_areas, offset_into_first_free_area) = allocator.free_areas();
|
let (free_areas, offset_into_first_free_area) = allocator.free_areas();
|
||||||
@@ -952,7 +958,7 @@ fn init_sections(mut allocator: BumpAllocator<RmmA>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
pub fn init_mm(allocator: BumpAllocator<RmmA>) {
|
pub fn init_mm(allocator: &mut BumpAllocator<RmmA>) {
|
||||||
init_sections(allocator);
|
init_sections(allocator);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|||||||
+14
-10
@@ -3,8 +3,8 @@ use crate::{
|
|||||||
self,
|
self,
|
||||||
context::{HardBlockedReason, LockedFdTbl, SignalState},
|
context::{HardBlockedReason, LockedFdTbl, SignalState},
|
||||||
file::InternalFlags,
|
file::InternalFlags,
|
||||||
memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan},
|
memory::{handle_notify_files, AddrSpace, AddrSpaceWrapper, Grant, PageSpan, UnmapVec},
|
||||||
Context, ContextLock, Status,
|
unblock_context, wakeup_context, Context, ContextLock, Status,
|
||||||
},
|
},
|
||||||
memory::{Page, VirtualAddress, PAGE_SIZE},
|
memory::{Page, VirtualAddress, PAGE_SIZE},
|
||||||
ptrace,
|
ptrace,
|
||||||
@@ -1580,6 +1580,7 @@ impl ContextHandle {
|
|||||||
reason: HardBlockedReason::NotYetStarted,
|
reason: HardBlockedReason::NotYetStarted,
|
||||||
} => {
|
} => {
|
||||||
*status = Status::Runnable;
|
*status = Status::Runnable;
|
||||||
|
wakeup_context(&context);
|
||||||
Ok(buf.len())
|
Ok(buf.len())
|
||||||
}
|
}
|
||||||
_ => Err(Error::new(EINVAL)),
|
_ => Err(Error::new(EINVAL)),
|
||||||
@@ -1747,12 +1748,12 @@ impl ContextHandle {
|
|||||||
} = guard.status
|
} = guard.status
|
||||||
{
|
{
|
||||||
guard.status = Status::Runnable;
|
guard.status = Status::Runnable;
|
||||||
|
wakeup_context(&context);
|
||||||
}
|
}
|
||||||
Ok(size_of::<usize>())
|
Ok(size_of::<usize>())
|
||||||
}
|
}
|
||||||
ContextVerb::Interrupt => {
|
ContextVerb::Interrupt => {
|
||||||
let mut guard = context.write(token.token());
|
unblock_context(&context, &mut token.token().downgrade());
|
||||||
guard.unblock();
|
|
||||||
Ok(size_of::<usize>())
|
Ok(size_of::<usize>())
|
||||||
}
|
}
|
||||||
ContextVerb::ForceKill => {
|
ContextVerb::ForceKill => {
|
||||||
@@ -1783,13 +1784,16 @@ impl ContextHandle {
|
|||||||
}
|
}
|
||||||
crate::syscall::exit_this_context(None, token);
|
crate::syscall::exit_this_context(None, token);
|
||||||
} else {
|
} else {
|
||||||
let mut ctxt = context.write(token.token());
|
{
|
||||||
//trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id);
|
let mut ctxt = context.write(token.token());
|
||||||
if let context::Status::Dead { .. } = ctxt.status {
|
//trace!("FORCEKILL NONSELF={} {}, SELF={}", ctxt.debug_id, ctxt.pid, context::current().read().debug_id);
|
||||||
return Ok(size_of::<usize>());
|
if ctxt.status.is_dead() {
|
||||||
|
return Ok(size_of::<usize>());
|
||||||
|
}
|
||||||
|
ctxt.status = context::Status::Runnable;
|
||||||
|
ctxt.being_sigkilled = true;
|
||||||
}
|
}
|
||||||
ctxt.status = context::Status::Runnable;
|
wakeup_context(&context);
|
||||||
ctxt.being_sigkilled = true;
|
|
||||||
Ok(size_of::<usize>())
|
Ok(size_of::<usize>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-5
@@ -22,10 +22,12 @@ use crate::{
|
|||||||
AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode,
|
AddrSpace, AddrSpaceWrapper, BorrowedFmapSource, Grant, GrantFileRef, MmapMode,
|
||||||
PageSpan, DANGLING,
|
PageSpan, DANGLING,
|
||||||
},
|
},
|
||||||
BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1, Status,
|
unblock_context, wakeup_context, BorrowedHtBuf, ContextLock, PreemptGuard, PreemptGuardL1,
|
||||||
|
Status, WeakContextRef,
|
||||||
},
|
},
|
||||||
event,
|
event,
|
||||||
memory::{Frame, Page, VirtualAddress, PAGE_SIZE},
|
memory::{Frame, Page, VirtualAddress, PAGE_SIZE},
|
||||||
|
percpu::PercpuBlock,
|
||||||
scheme::SchemeId,
|
scheme::SchemeId,
|
||||||
sync::{CleanLockToken, LockToken, Mutex, RwLock, WaitQueue, L1},
|
sync::{CleanLockToken, LockToken, Mutex, RwLock, WaitQueue, L1},
|
||||||
syscall::{
|
syscall::{
|
||||||
@@ -894,7 +896,7 @@ impl UserInner {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let context = context.upgrade().ok_or(Error::new(ESRCH))?;
|
let context_lock = context.upgrade().ok_or(Error::new(ESRCH))?;
|
||||||
|
|
||||||
let mut lock_token = token.token();
|
let mut lock_token = token.token();
|
||||||
let (frame, _) = AddrSpace::current()?
|
let (frame, _) = AddrSpace::current()?
|
||||||
@@ -905,12 +907,13 @@ impl UserInner {
|
|||||||
.ok_or(Error::new(EFAULT))?;
|
.ok_or(Error::new(EFAULT))?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut context = context.write(token.token());
|
let mut context = context_lock.write(token.token());
|
||||||
if let Status::HardBlocked {
|
if let Status::HardBlocked {
|
||||||
reason: HardBlockedReason::AwaitingMmap { .. },
|
reason: HardBlockedReason::AwaitingMmap { .. },
|
||||||
} = context.status
|
} = context.status
|
||||||
{
|
{
|
||||||
context.status = Status::Runnable
|
context.status = Status::Runnable;
|
||||||
|
wakeup_context(&context_lock);
|
||||||
}
|
}
|
||||||
context.fmap_ret = Some(Frame::containing(frame));
|
context.fmap_ret = Some(Frame::containing(frame));
|
||||||
}
|
}
|
||||||
@@ -982,7 +985,7 @@ impl UserInner {
|
|||||||
match context.upgrade() {
|
match context.upgrade() {
|
||||||
Some(context) => {
|
Some(context) => {
|
||||||
*o = State::Responded(response);
|
*o = State::Responded(response);
|
||||||
context.write(lock_token.token()).unblock();
|
unblock_context(&context, &mut lock_token.token().downgrade());
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
states.remove(tag as usize);
|
states.remove(tag as usize);
|
||||||
|
|||||||
+4
-2
@@ -7,7 +7,7 @@ use core::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
arch::interrupt,
|
arch::interrupt,
|
||||||
context::{self, switch::SwitchResult},
|
context::{self, switch::SwitchResult, wakeup_context, Status},
|
||||||
memory::{PhysicalAddress, RmmA, RmmArch},
|
memory::{PhysicalAddress, RmmA, RmmArch},
|
||||||
profiling, scheme,
|
profiling, scheme,
|
||||||
sync::CleanLockToken,
|
sync::CleanLockToken,
|
||||||
@@ -175,13 +175,15 @@ pub(crate) fn kmain(bootstrap: Bootstrap) -> ! {
|
|||||||
match context::spawn(true, owner, userspace_init, &mut token) {
|
match context::spawn(true, owner, userspace_init, &mut token) {
|
||||||
Ok(context_lock) => {
|
Ok(context_lock) => {
|
||||||
let mut context = context_lock.write(token.token());
|
let mut context = context_lock.write(token.token());
|
||||||
context.status = context::Status::Runnable;
|
context.status = Status::Runnable;
|
||||||
context.name.clear();
|
context.name.clear();
|
||||||
context.name.push_str("[bootstrap]");
|
context.name.push_str("[bootstrap]");
|
||||||
|
|
||||||
// TODO: Remove these from kernel
|
// TODO: Remove these from kernel
|
||||||
context.euid = 0;
|
context.euid = 0;
|
||||||
context.egid = 0;
|
context.egid = 0;
|
||||||
|
|
||||||
|
wakeup_context(&context_lock);
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
panic!("failed to spawn userspace_init: {:?}", err);
|
panic!("failed to spawn userspace_init: {:?}", err);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use alloc::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::{self, ContextLock, PreemptGuardL2},
|
context::{self, unblock_context, ContextLock, PreemptGuardL2},
|
||||||
sync::{CleanLockToken, LockToken, Mutex, L1, L2, L3},
|
sync::{CleanLockToken, LockToken, Mutex, L1, L2, L3},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ impl WaitCondition {
|
|||||||
let len = contexts.len();
|
let len = contexts.len();
|
||||||
while let Some(context_weak) = contexts.pop() {
|
while let Some(context_weak) = contexts.pop() {
|
||||||
if let Some(context_ref) = context_weak.upgrade() {
|
if let Some(context_ref) = context_weak.upgrade() {
|
||||||
context_ref.write(token.token()).unblock();
|
unblock_context(&context_ref, &mut token.token());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len
|
len
|
||||||
@@ -46,7 +46,7 @@ impl WaitCondition {
|
|||||||
let len = contexts.len();
|
let len = contexts.len();
|
||||||
for context_weak in contexts.iter() {
|
for context_weak in contexts.iter() {
|
||||||
if let Some(context_ref) = context_weak.upgrade() {
|
if let Some(context_ref) = context_weak.upgrade() {
|
||||||
context_ref.write(token.token()).unblock();
|
unblock_context(&context_ref, &mut token.token());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
len
|
len
|
||||||
|
|||||||
+24
-31
@@ -311,43 +311,36 @@ pub fn dup2(
|
|||||||
buf: UserSliceRo,
|
buf: UserSliceRo,
|
||||||
token: &mut CleanLockToken,
|
token: &mut CleanLockToken,
|
||||||
) -> Result<FileHandle> {
|
) -> Result<FileHandle> {
|
||||||
if fd == new_fd {
|
if fd == new_fd && buf.is_empty() {
|
||||||
// Self-dup: a no-op if no buffer is supplied. But a self-dup2 WITH a
|
return Ok(new_fd);
|
||||||
// buffer is a meaningful in-place operation — e.g. relibc issues
|
}
|
||||||
// dup2(ft, ft, "refresh") to re-materialize the inherited filetable, so
|
|
||||||
// it must NOT short-circuit. Duplicate first (fd is still valid), then
|
|
||||||
// replace the descriptor in place. Only this self-dup path is new; the
|
|
||||||
// normal path below is unchanged so ordinary dup2 (process-spawn stdio)
|
|
||||||
// keeps its exact original behavior.
|
|
||||||
if buf.is_empty() {
|
|
||||||
return Ok(new_fd);
|
|
||||||
}
|
|
||||||
let new_file = duplicate_file(fd, buf, false, token)?;
|
|
||||||
let old_file = {
|
|
||||||
let current_lock = context::current();
|
|
||||||
let mut current = current_lock.read(token.token());
|
|
||||||
let (context, mut token) = current.token_split();
|
|
||||||
let old_file = context.remove_file(new_fd, &mut token);
|
|
||||||
context
|
|
||||||
.insert_file(new_fd, new_file, &mut token)
|
|
||||||
.ok_or(Error::new(EMFILE))?;
|
|
||||||
old_file
|
|
||||||
};
|
|
||||||
if let Some(old) = old_file {
|
|
||||||
let _ = old.close(token);
|
|
||||||
}
|
|
||||||
Ok(new_fd)
|
|
||||||
} else {
|
|
||||||
let _ = close(new_fd, token);
|
|
||||||
let new_file = duplicate_file(fd, buf, false, token)?;
|
|
||||||
|
|
||||||
|
let new_file = duplicate_file(fd, buf, false, token)?;
|
||||||
|
|
||||||
|
let old_file = {
|
||||||
let current_lock = context::current();
|
let current_lock = context::current();
|
||||||
let mut current = current_lock.read(token.token());
|
let mut current = current_lock.read(token.token());
|
||||||
let (context, mut token) = current.token_split();
|
let (context, mut token) = current.token_split();
|
||||||
context
|
|
||||||
|
let old_file = context.remove_file(new_fd, &mut token);
|
||||||
|
|
||||||
|
if context
|
||||||
.insert_file(new_fd, new_file, &mut token)
|
.insert_file(new_fd, new_file, &mut token)
|
||||||
.ok_or(Error::new(EMFILE))
|
.is_none()
|
||||||
|
{
|
||||||
|
if let Some(old) = old_file {
|
||||||
|
context.insert_file(new_fd, old, &mut token);
|
||||||
|
}
|
||||||
|
return Err(Error::new(EMFILE));
|
||||||
|
}
|
||||||
|
old_file
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(old) = old_file {
|
||||||
|
let _ = old.close(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(new_fd)
|
||||||
}
|
}
|
||||||
pub fn call(
|
pub fn call(
|
||||||
fd: FileHandle,
|
fd: FileHandle,
|
||||||
|
|||||||
+40
-26
@@ -15,11 +15,10 @@ use crate::{
|
|||||||
context::{
|
context::{
|
||||||
self,
|
self,
|
||||||
memory::{AddrSpace, AddrSpaceWrapper},
|
memory::{AddrSpace, AddrSpaceWrapper},
|
||||||
ContextLock,
|
unblock_context, ContextLock,
|
||||||
},
|
},
|
||||||
memory::{Page, PhysicalAddress, VirtualAddress},
|
memory::{Page, PhysicalAddress, VirtualAddress},
|
||||||
sync::{CleanLockToken, Mutex, L1},
|
sync::{CleanLockToken, Mutex, L1},
|
||||||
time,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::syscall::{
|
use crate::syscall::{
|
||||||
@@ -39,9 +38,11 @@ pub struct FutexEntry {
|
|||||||
// CoW.
|
// CoW.
|
||||||
target_virtaddr: VirtualAddress,
|
target_virtaddr: VirtualAddress,
|
||||||
// Context to wake up, and compare address spaces.
|
// Context to wake up, and compare address spaces.
|
||||||
context_lock: Arc<ContextLock>,
|
context_lock: Weak<ContextLock>,
|
||||||
// address space to check against if virt matches but not phys
|
// address space to check against if virt matches but not phys
|
||||||
addr_space: Weak<AddrSpaceWrapper>,
|
addr_space: Weak<AddrSpaceWrapper>,
|
||||||
|
// FUTEX_WAIT_MULTIPLE: which array index this waiter corresponds to,
|
||||||
|
// reported back through Context::futex_wake_index on wake.
|
||||||
waitv_index: Option<usize>,
|
waitv_index: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +53,17 @@ pub struct FutexEntry {
|
|||||||
static FUTEXES: Mutex<L1, FutexList> =
|
static FUTEXES: Mutex<L1, FutexList> =
|
||||||
Mutex::new(FutexList::with_hasher(DefaultHashBuilder::new()));
|
Mutex::new(FutexList::with_hasher(DefaultHashBuilder::new()));
|
||||||
|
|
||||||
|
pub fn get_futex_stat(token: &mut CleanLockToken) -> (usize, usize) {
|
||||||
|
let mut regc = 0;
|
||||||
|
let mut regl = 0;
|
||||||
|
let registry = FUTEXES.lock(token.token());
|
||||||
|
for (_, v) in registry.iter() {
|
||||||
|
regl += v.len();
|
||||||
|
regc += 1;
|
||||||
|
}
|
||||||
|
(regc, regl)
|
||||||
|
}
|
||||||
|
|
||||||
fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Option<PhysicalAddress> {
|
fn validate_and_translate_virt(space: &AddrSpace, addr: VirtualAddress) -> Option<PhysicalAddress> {
|
||||||
// TODO: Move this elsewhere!
|
// TODO: Move this elsewhere!
|
||||||
if addr.data().saturating_add(size_of::<usize>()) >= crate::USER_END_OFFSET {
|
if addr.data().saturating_add(size_of::<usize>()) >= crate::USER_END_OFFSET {
|
||||||
@@ -142,9 +154,7 @@ pub fn futex(
|
|||||||
{
|
{
|
||||||
let mut context = context_lock.write(token.token());
|
let mut context = context_lock.write(token.token());
|
||||||
|
|
||||||
context.wake = timeout_opt.map(|TimeSpec { tv_sec, tv_nsec }| {
|
context.wake = timeout_opt.map(|time| time.to_nanos());
|
||||||
tv_sec as u128 * time::NANOS_PER_SEC + tv_nsec as u128
|
|
||||||
});
|
|
||||||
if let Some((tctl, pctl, _)) = context.sigcontrol()
|
if let Some((tctl, pctl, _)) = context.sigcontrol()
|
||||||
&& tctl.currently_pending_unblocked(pctl) != 0
|
&& tctl.currently_pending_unblocked(pctl) != 0
|
||||||
{
|
{
|
||||||
@@ -159,7 +169,7 @@ pub fn futex(
|
|||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(FutexEntry {
|
.push(FutexEntry {
|
||||||
target_virtaddr,
|
target_virtaddr,
|
||||||
context_lock: context_lock.clone(),
|
context_lock: Arc::downgrade(&context_lock),
|
||||||
addr_space: Arc::downgrade(¤t_addrsp),
|
addr_space: Arc::downgrade(¤t_addrsp),
|
||||||
waitv_index: None,
|
waitv_index: None,
|
||||||
});
|
});
|
||||||
@@ -198,25 +208,28 @@ pub fn futex(
|
|||||||
if futex.target_virtaddr != target_virtaddr
|
if futex.target_virtaddr != target_virtaddr
|
||||||
|| !current_addrsp_weak.ptr_eq(&futex.addr_space)
|
|| !current_addrsp_weak.ptr_eq(&futex.addr_space)
|
||||||
{
|
{
|
||||||
i += 1;
|
if futex.addr_space.strong_count() == 0 {
|
||||||
|
futexes.swap_remove(i);
|
||||||
|
} else {
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let should_wake = {
|
if let Some(ctx) = futex.context_lock.upgrade() {
|
||||||
let mut context = futex.context_lock.write(token.token());
|
// FUTEX_WAIT_MULTIPLE wake-index bookkeeping:
|
||||||
|
// record which array index woke the waiter (the
|
||||||
|
// first wake wins) before unblocking.
|
||||||
if let Some(index) = futex.waitv_index {
|
if let Some(index) = futex.waitv_index {
|
||||||
|
let mut context = ctx.write(token.token());
|
||||||
if context.futex_wake_index.is_none() {
|
if context.futex_wake_index.is_none() {
|
||||||
context.futex_wake_index = Some(index);
|
context.futex_wake_index = Some(index);
|
||||||
}
|
}
|
||||||
|
drop(context);
|
||||||
}
|
}
|
||||||
context.unblock();
|
unblock_context(&ctx, &mut token.token().downgrade());
|
||||||
true
|
|
||||||
};
|
|
||||||
if should_wake {
|
|
||||||
futexes.swap_remove(i);
|
|
||||||
woken += 1;
|
|
||||||
} else {
|
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
|
futexes.swap_remove(i);
|
||||||
|
woken += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
futexes.is_empty()
|
futexes.is_empty()
|
||||||
@@ -260,7 +273,9 @@ pub fn futex(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if woken < val {
|
if woken < val {
|
||||||
futex.context_lock.write(token.token()).unblock();
|
if let Some(ctx) = futex.context_lock.upgrade() {
|
||||||
|
unblock_context(&ctx, &mut token.token().downgrade());
|
||||||
|
}
|
||||||
futexes.remove(i);
|
futexes.remove(i);
|
||||||
woken += 1;
|
woken += 1;
|
||||||
} else if requeued < val2 {
|
} else if requeued < val2 {
|
||||||
@@ -288,9 +303,10 @@ pub fn futex(
|
|||||||
Ok(woken + requeued)
|
Ok(woken + requeued)
|
||||||
}
|
}
|
||||||
FUTEX_WAIT_MULTIPLE => {
|
FUTEX_WAIT_MULTIPLE => {
|
||||||
// Wait on multiple futexes atomically; return the index of the first
|
// Wait on multiple futexes atomically; return the index of the
|
||||||
// one that is woken. Semantics: val = number of FutexWaitv entries,
|
// first one that is woken. Semantics: val = number of FutexWaitv
|
||||||
// addr2 = pointer to the array, val2 = optional TimeSpec timeout.
|
// entries, addr2 = pointer to the array, val2 = optional TimeSpec
|
||||||
|
// timeout. Linux 7.1 kernel/futex/waitv.c futex_waitv().
|
||||||
let count = val;
|
let count = val;
|
||||||
if count == 0 || count > 128 {
|
if count == 0 || count > 128 {
|
||||||
return Err(Error::new(EINVAL));
|
return Err(Error::new(EINVAL));
|
||||||
@@ -344,9 +360,7 @@ pub fn futex(
|
|||||||
|
|
||||||
{
|
{
|
||||||
let mut context = context_lock.write(token.token());
|
let mut context = context_lock.write(token.token());
|
||||||
context.wake = timeout_opt.map(|TimeSpec { tv_sec, tv_nsec }| {
|
context.wake = timeout_opt.map(|ts| ts.to_nanos());
|
||||||
tv_sec as u128 * time::NANOS_PER_SEC + tv_nsec as u128
|
|
||||||
});
|
|
||||||
context.futex_wake_index = None;
|
context.futex_wake_index = None;
|
||||||
if let Some((tctl, pctl, _)) = context.sigcontrol()
|
if let Some((tctl, pctl, _)) = context.sigcontrol()
|
||||||
&& tctl.currently_pending_unblocked(pctl) != 0
|
&& tctl.currently_pending_unblocked(pctl) != 0
|
||||||
@@ -362,7 +376,7 @@ pub fn futex(
|
|||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(FutexEntry {
|
.push(FutexEntry {
|
||||||
target_virtaddr: virt,
|
target_virtaddr: virt,
|
||||||
context_lock: context_lock.clone(),
|
context_lock: Arc::downgrade(&context_lock),
|
||||||
addr_space: Arc::downgrade(¤t_addrsp),
|
addr_space: Arc::downgrade(¤t_addrsp),
|
||||||
waitv_index: Some(i),
|
waitv_index: Some(i),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user