0.3.0: converge kernel onto upstream master
- Rebase all Red Bear kernel changes onto upstream master (4d5d36d4).
- Update version to 0.5.12+rb0.3.0 and add Red Bear author attribution.
- Switch redox_syscall direct dependency to local fork path (../syscall).
- Bump rust-toolchain.toml to nightly-2026-05-24.
- Regenerate Cargo.lock for +rb0.3.0 suffixes and path deps.
This commit is contained in:
+17
-133
@@ -9,14 +9,13 @@ use rmm::Arch;
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
use crate::arch::{idt::Idt, interrupt::irq::aux_timer};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::arch::{
|
||||
interrupt::{self, InterruptStack},
|
||||
CurrentRmmArch,
|
||||
};
|
||||
use crate::{
|
||||
arch::{
|
||||
interrupt::{self, InterruptStack},
|
||||
CurrentRmmArch,
|
||||
},
|
||||
cpu_set::LogicalCpuId,
|
||||
memory::VirtualAddress,
|
||||
percpu::PercpuBlock,
|
||||
syscall::{error::*, usercopy::UserSliceWo},
|
||||
};
|
||||
@@ -161,117 +160,35 @@ pub fn drain_buffer(cpu_num: LogicalCpuId, buf: UserSliceWo) -> Result<usize> {
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub unsafe fn nmi_handler(stack: &InterruptStack) {
|
||||
// Inside an NMI handler, so don't acquire any locks or trigger any page faults or other
|
||||
// exceptions!
|
||||
|
||||
if cfg!(not(feature = "profiling")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let percpu = crate::percpu::PercpuBlock::current();
|
||||
let Some(profiling) = percpu.profiling else {
|
||||
let Some(profiling) = crate::percpu::PercpuBlock::current().profiling else {
|
||||
return;
|
||||
};
|
||||
if !IS_PROFILING.load(Ordering::Relaxed) {
|
||||
return;
|
||||
}
|
||||
let user_not_kernel = if stack.iret.cs & 0b11 == 0b11 {
|
||||
if stack.iret.cs & 0b00 == 0b11 {
|
||||
profiling.nmi_ucount.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
true
|
||||
return;
|
||||
} else if stack.iret.rflags & (1 << 9) != 0 {
|
||||
// Interrupts were enabled, i.e. we were in kmain, so ignore.
|
||||
return;
|
||||
} else {
|
||||
profiling.nmi_kcount.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
false
|
||||
};
|
||||
|
||||
let mut buf = [0_usize; 32];
|
||||
buf[0] = 0xfedfac00; // allows 8-bit length
|
||||
buf[1] = stack.iret.rip;
|
||||
buf[2] = unsafe { x86::time::rdtsc() } as usize;
|
||||
buf[0] = stack.iret.rip & !(1 << 63);
|
||||
buf[1] = unsafe { x86::time::rdtsc() } as usize;
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
{
|
||||
buf[3] = percpu
|
||||
.switch_internals
|
||||
.current_dbg_id
|
||||
.load(Ordering::Relaxed) as usize;
|
||||
}
|
||||
let mut bp = stack.preserved.rbp;
|
||||
|
||||
let mut len = 4;
|
||||
let mut len = 2;
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
if user_not_kernel {
|
||||
unsafe {
|
||||
walk_ustack(stack.preserved.rbp, &mut buf, &mut len);
|
||||
}
|
||||
} else {
|
||||
// TODO: Support walking past a syscall boundary? If so, should be sufficient to check
|
||||
// against syscall_instruction, then get registers from InterruptStack and call walk_ustack
|
||||
// on the rest.
|
||||
unsafe {
|
||||
walk_kstack(stack.preserved.rbp, &mut buf, &mut len);
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] |= len;
|
||||
|
||||
let _ = unsafe { profiling.extend(&buf[..len]) };
|
||||
}
|
||||
#[cfg(feature = "profiling")]
|
||||
unsafe fn walk_ustack(mut bp: usize, buf: &mut [usize; 32], len: &mut usize) {
|
||||
// Runs inside an NMI handler!
|
||||
|
||||
// It's pretty unsafe to do this without locks, but we can pretend it's the CPU that is
|
||||
// resolving the mappings. We already track logical CPU usage bits in each address space,
|
||||
// forbidding any page table modifications until the kernel has switched away from this
|
||||
// context, and any modifications will also need to wait for TLB shootdown, which this NMI will
|
||||
// postpone due to disabled interrupts.
|
||||
let mapper =
|
||||
unsafe { rmm::PageMapper::<CurrentRmmArch, ()>::current(rmm::TableKind::User, ()) };
|
||||
|
||||
#[expect(clippy::needless_range_loop)]
|
||||
for i in *len..32 {
|
||||
// Unlike in kernel mode, we don't know where the user executable starts or ends, but this
|
||||
// can be post-processed later by profiled. However, some criteria can be applied such as
|
||||
// the 16-byte alignedness of bp, and whether the next page is mapped at all.
|
||||
|
||||
if bp >= crate::USER_END_OFFSET
|
||||
|| bp % 16 > 0
|
||||
|| !CurrentRmmArch::virt_is_valid(VirtualAddress::new(bp))
|
||||
{
|
||||
break;
|
||||
}
|
||||
// Since we are reading 16 bytes and bp is aligned to 16 bytes, this can't span a page
|
||||
// boundary!
|
||||
|
||||
let Some((bp_frame, bp_flags)) = mapper.translate(VirtualAddress::new(bp)) else {
|
||||
break;
|
||||
};
|
||||
if !bp_flags.has_user() || !bp_flags.has_write() {
|
||||
break;
|
||||
}
|
||||
|
||||
let [next_bp, ip] =
|
||||
unsafe { (CurrentRmmArch::phys_to_virt(bp_frame).data() as *const [usize; 2]).read() };
|
||||
if ip >= crate::USER_END_OFFSET || !CurrentRmmArch::virt_is_valid(VirtualAddress::new(ip)) {
|
||||
break;
|
||||
}
|
||||
|
||||
buf[i] = ip;
|
||||
bp = next_bp;
|
||||
*len = i + 1;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "profiling")]
|
||||
unsafe fn walk_kstack(mut bp: usize, buf: &mut [usize; 32], len: &mut usize) {
|
||||
// Runs inside an NMI handler!
|
||||
|
||||
#[expect(clippy::needless_range_loop)]
|
||||
for i in *len..32 {
|
||||
for i in 2..32 {
|
||||
if bp < CurrentRmmArch::PHYS_OFFSET
|
||||
|| bp.saturating_add(16) >= CurrentRmmArch::PHYS_OFFSET + crate::PML4_SIZE
|
||||
{
|
||||
@@ -287,26 +204,10 @@ unsafe fn walk_kstack(mut bp: usize, buf: &mut [usize; 32], len: &mut usize) {
|
||||
}
|
||||
buf[i] = ip;
|
||||
|
||||
*len = i + 1;
|
||||
|
||||
let start = crate::arch::interrupt::syscall::syscall_instruction as *const () as usize;
|
||||
let end = crate::arch::interrupt::syscall::__syscall_instruction_end as *const () as usize;
|
||||
|
||||
if ip >= start && ip <= end {
|
||||
let stack = unsafe {
|
||||
&*((*crate::arch::x86_64::gdt::pcr()).tss.rsp[0] as *const InterruptStack).sub(1)
|
||||
};
|
||||
if *len >= buf.len() {
|
||||
break;
|
||||
}
|
||||
buf[*len] = stack.iret.rip;
|
||||
*len += 1;
|
||||
unsafe {
|
||||
walk_ustack(stack.preserved.rbp, buf, len);
|
||||
}
|
||||
break;
|
||||
}
|
||||
len = i + 1;
|
||||
}
|
||||
|
||||
let _ = unsafe { profiling.extend(&buf[..len]) };
|
||||
}
|
||||
|
||||
static NUM_ORDINARY_CPUS: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
@@ -402,7 +303,7 @@ pub fn maybe_run_profiling_helper_forever(cpu_id: LogicalCpuId) {
|
||||
let apic = &mut crate::arch::device::local_apic::the_local_apic();
|
||||
apic.set_lvt_timer((0b01 << 17) | 32);
|
||||
apic.set_div_conf(0b1011);
|
||||
apic.set_init_count(0x000f_ffff);
|
||||
apic.set_init_count(0xffff_f);
|
||||
|
||||
while ACK.load(Ordering::Relaxed) < NUM_ORDINARY_CPUS.load(Ordering::SeqCst) {
|
||||
core::hint::spin_loop();
|
||||
@@ -427,20 +328,3 @@ pub fn maybe_setup_timer(idt: &mut Idt, cpu_id: LogicalCpuId) {
|
||||
idt.entries[32].set_func(aux_timer);
|
||||
idt.set_reserved_mut(32, true);
|
||||
}
|
||||
#[cfg(feature = "profiling")]
|
||||
pub static DBG_ID_MAP: crate::sync::RwLock<
|
||||
crate::sync::L1,
|
||||
hashbrown::HashMap<u32, arrayvec::ArrayString<32>>,
|
||||
> = crate::sync::RwLock::new(hashbrown::HashMap::with_hasher(
|
||||
hashbrown::hash_map::DefaultHashBuilder::new(),
|
||||
));
|
||||
|
||||
#[cfg(feature = "profiling")]
|
||||
pub fn lookup_dbg_id(
|
||||
id: u32,
|
||||
token: &mut crate::sync::CleanLockToken,
|
||||
) -> Option<arrayvec::ArrayString<32>> {
|
||||
// TODO: Map is necessary to track contexts that were removed afterwards. However, this
|
||||
// function should also scan for contexts that currently exist.
|
||||
DBG_ID_MAP.read(token.token()).get(&id).copied()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user