ff4ff35918
Red Bear OS is a full fork. All sources must be available from git clone with zero network access. Removed gitignore rules that excluded fetched source trees under recipes/*/source/, local/recipes/kde/*/source/, local/recipes/qt/*/source/, and vendor source trees. Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded. 127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt frameworks, mesa, wayland, DRM drivers, and every other recipe source.
367 lines
13 KiB
Rust
367 lines
13 KiB
Rust
use crate::{
|
|
context::{context::SyscallFrame, contexts, Context, ContextLock},
|
|
memory::{
|
|
get_page_info, the_zeroed_frame, Frame, RefCount, RmmA, RmmArch, TableKind, PAGE_SIZE,
|
|
},
|
|
sync::CleanLockToken,
|
|
};
|
|
use alloc::sync::Arc;
|
|
use hashbrown::{HashMap, HashSet};
|
|
|
|
/// Super unsafe due to page table switching and raw pointers!
|
|
pub unsafe fn debugger(target_id: Option<*const ContextLock>, token: &mut CleanLockToken) {
|
|
println!("DEBUGGER START");
|
|
println!();
|
|
|
|
let mut tree = HashMap::new();
|
|
let mut spaces = HashSet::new();
|
|
|
|
tree.insert(the_zeroed_frame().0, (1, false));
|
|
|
|
let old_table = RmmA::table(TableKind::User);
|
|
|
|
{
|
|
let mut contexts = contexts(token.downgrade());
|
|
let (contexts, mut token) = contexts.token_split();
|
|
for context_arc in contexts.iter() {
|
|
if target_id.map_or(false, |target_id| Arc::as_ptr(&context_arc) != target_id) {
|
|
continue;
|
|
}
|
|
let context = context_arc.read(token.token());
|
|
println!("{:p}: {}", Arc::as_ptr(&context_arc), context.name);
|
|
|
|
let mut mark_frame_use = |frame| {
|
|
tree.entry(frame).or_insert((0, false)).0 += 1;
|
|
};
|
|
|
|
match &context.syscall_head {
|
|
SyscallFrame::Free(head) => mark_frame_use(head.get()),
|
|
SyscallFrame::Used { _frame: head } => mark_frame_use(*head),
|
|
SyscallFrame::Dummy => {}
|
|
}
|
|
match &context.syscall_tail {
|
|
SyscallFrame::Free(tail) => mark_frame_use(tail.get()),
|
|
SyscallFrame::Used { _frame: tail } => mark_frame_use(*tail),
|
|
SyscallFrame::Dummy => {}
|
|
}
|
|
|
|
if let Some(sig) = &context.sig {
|
|
mark_frame_use(sig.proc_control.get());
|
|
mark_frame_use(sig.thread_control.get());
|
|
}
|
|
|
|
// TODO: Lock ordering violation
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
|
|
// Switch to context page table to ensure syscall debug and stack dump will work
|
|
if let Some(ref space) = context.addr_space {
|
|
let was_new = spaces.insert(
|
|
space
|
|
.acquire_read(token.downgrade())
|
|
.table
|
|
.utable
|
|
.table()
|
|
.phys()
|
|
.data(),
|
|
);
|
|
unsafe {
|
|
RmmA::set_table(
|
|
TableKind::User,
|
|
space
|
|
.acquire_read(token.downgrade())
|
|
.table
|
|
.utable
|
|
.table()
|
|
.phys(),
|
|
);
|
|
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
|
|
check_page_table_consistency(
|
|
&mut space.acquire_write(token.downgrade()),
|
|
was_new,
|
|
&mut tree,
|
|
);
|
|
}
|
|
}
|
|
|
|
println!("status: {:?}", context.status);
|
|
if !context.status_reason.is_empty() {
|
|
println!("reason: {}", context.status_reason);
|
|
}
|
|
if let Some([a, b, c, d, e, f, g]) = context.current_syscall() {
|
|
println!(
|
|
"syscall: {}",
|
|
crate::syscall::debug::format_call(a, b, c, d, e, f, g)
|
|
);
|
|
}
|
|
if let Some(ref addr_space) = context.addr_space {
|
|
let addr_space = addr_space.acquire_read(token.downgrade());
|
|
if !addr_space.grants.is_empty() {
|
|
println!("grants:");
|
|
for (base, info) in addr_space.grants.iter() {
|
|
let size = info.page_count() * PAGE_SIZE;
|
|
|
|
let flags = format_args!(
|
|
"{}{}{}{}",
|
|
if info.flags().has_user() { "u" } else { "k" },
|
|
if info.flags().has_present() { "r" } else { "-" },
|
|
if info.flags().has_write() { "w" } else { "-" },
|
|
if info.flags().has_execute() { "x" } else { "-" },
|
|
);
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
println!(
|
|
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
|
|
base.start_address().data(),
|
|
base.next_by(info.page_count() - 1).start_address().data() + 0xFFF,
|
|
flags,
|
|
size,
|
|
info.provider,
|
|
);
|
|
|
|
// FIXME riscv64 implementation
|
|
|
|
#[cfg(target_arch = "x86")]
|
|
println!(
|
|
" virt 0x{:08x}:0x{:08x} {} size 0x{:08x} {:?}",
|
|
base.start_address().data(),
|
|
base.next_by(info.page_count()).start_address().data() + 0xFFF,
|
|
flags,
|
|
size,
|
|
info.provider,
|
|
);
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
println!(
|
|
" virt 0x{:016x}:0x{:016x} {} size 0x{:08x} {:?}",
|
|
base.start_address().data(),
|
|
base.start_address().data() + size - 1,
|
|
flags,
|
|
size,
|
|
info.provider,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
if let Some(regs) = context.regs() {
|
|
println!("regs:");
|
|
regs.dump();
|
|
|
|
#[cfg(target_arch = "aarch64")]
|
|
dump_stack(&*context, regs.iret.sp_el0);
|
|
|
|
// FIXME riscv64 implementation
|
|
|
|
#[cfg(target_arch = "x86")]
|
|
dump_stack(&*context, regs.iret.esp);
|
|
|
|
#[cfg(target_arch = "x86_64")]
|
|
{
|
|
unsafe {
|
|
x86::bits64::rflags::stac();
|
|
}
|
|
dump_stack(&*context, regs.iret.rsp);
|
|
unsafe {
|
|
x86::bits64::rflags::clac();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Switch to original page table
|
|
unsafe { RmmA::set_table(TableKind::User, old_table) };
|
|
|
|
println!();
|
|
}
|
|
}
|
|
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
|
|
crate::scheme::proc::foreach_addrsp(token, |addrsp, mut token| {
|
|
let was_new = spaces.insert(
|
|
addrsp
|
|
.acquire_read(token.downgrade())
|
|
.table
|
|
.utable
|
|
.table()
|
|
.phys()
|
|
.data(),
|
|
);
|
|
unsafe {
|
|
check_page_table_consistency(
|
|
&mut *addrsp.acquire_write(token.downgrade()),
|
|
was_new,
|
|
&mut tree,
|
|
)
|
|
};
|
|
});
|
|
for (frame, (count, p)) in tree {
|
|
let Some(info) = get_page_info(frame) else {
|
|
assert!(p);
|
|
continue;
|
|
};
|
|
let (c, s) = match info.refcount() {
|
|
None => (0, ""),
|
|
Some(RefCount::One) => (1, ""),
|
|
Some(RefCount::Cow(c)) => (c.get(), " cow"),
|
|
Some(RefCount::Shared(s)) => (s.get(), " shared"),
|
|
};
|
|
if c != count {
|
|
println!(
|
|
"frame refcount mismatch for {:?} ({} != {}{})",
|
|
frame, c, count, s
|
|
);
|
|
}
|
|
}
|
|
|
|
println!("DEBUGGER END");
|
|
}
|
|
|
|
fn dump_stack(context: &Context, mut sp: usize) {
|
|
let width = size_of::<usize>();
|
|
|
|
println!("stack: {:>0width$x}", sp, width = width);
|
|
let mut token = unsafe { CleanLockToken::new() };
|
|
//Maximum 64 usizes
|
|
for _ in 0..64 {
|
|
if context.addr_space.as_ref().map_or(false, |space| {
|
|
space
|
|
.acquire_read(token.downgrade())
|
|
.table
|
|
.utable
|
|
.translate(crate::memory::VirtualAddress::new(sp))
|
|
.is_some()
|
|
}) {
|
|
let value = unsafe { *(sp as *const usize) };
|
|
println!(" {:>0width$x}: {:>0width$x}", sp, value, width = width);
|
|
if let Some(next_sp) = sp.checked_add(size_of::<usize>()) {
|
|
sp = next_sp;
|
|
} else {
|
|
println!(" {:>0width$x}: OVERFLOW", sp, width = width);
|
|
break;
|
|
}
|
|
} else {
|
|
println!(" {:>0width$x}: GUARD PAGE", sp, width = width);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_arch = "aarch64", target_arch = "x86_64"))]
|
|
unsafe fn check_page_table_consistency(
|
|
addr_space: &mut crate::context::memory::AddrSpace,
|
|
new_as: bool,
|
|
tree: &mut HashMap<Frame, (usize, bool)>,
|
|
) {
|
|
use crate::{
|
|
context::memory::{PageSpan, Provider},
|
|
memory::{get_page_info, RefCount},
|
|
};
|
|
|
|
let p4 = addr_space.table.utable.table();
|
|
|
|
for p4i in 0..256 {
|
|
let p3 = match unsafe { p4.next(p4i) } {
|
|
Some(p3) => p3,
|
|
None => continue,
|
|
};
|
|
|
|
for p3i in 0..512 {
|
|
let p2 = match unsafe { p3.next(p3i) } {
|
|
Some(p2) => p2,
|
|
None => continue,
|
|
};
|
|
|
|
for p2i in 0..512 {
|
|
let p1 = match unsafe { p2.next(p2i) } {
|
|
Some(p1) => p1,
|
|
None => continue,
|
|
};
|
|
|
|
for p1i in 0..512 {
|
|
use crate::memory::Page;
|
|
use rmm::VirtualAddress;
|
|
|
|
let (physaddr, flags) = match unsafe { p1.entry(p1i) } {
|
|
Some(e) => {
|
|
if let Ok(address) = e.address() {
|
|
(address, e.flags())
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
_ => continue,
|
|
};
|
|
let address =
|
|
VirtualAddress::new((p1i << 12) | (p2i << 21) | (p3i << 30) | (p4i << 39));
|
|
|
|
let (base, grant) = match addr_space
|
|
.grants
|
|
.contains(Page::containing_address(address))
|
|
{
|
|
Some(g) => g,
|
|
None => {
|
|
error!(
|
|
"ADDRESS {:p} LACKING GRANT BUT MAPPED TO {:#0x} FLAGS {:?}!",
|
|
address.data() as *const u8,
|
|
physaddr.data(),
|
|
flags
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
|
|
const EXCLUDE: usize = (1 << 5) | (1 << 6); // accessed+dirty+writable
|
|
if grant.flags().write(false).data() & !EXCLUDE
|
|
!= flags.write(false).data() & !EXCLUDE
|
|
{
|
|
error!(
|
|
"FLAG MISMATCH: {:?} != {:?}, address {:p} in grant at {:?}",
|
|
grant.flags(),
|
|
flags,
|
|
address.data() as *const u8,
|
|
PageSpan::new(base, grant.page_count())
|
|
);
|
|
}
|
|
let p = matches!(
|
|
grant.provider,
|
|
Provider::PhysBorrowed { .. }
|
|
| Provider::External { .. }
|
|
| Provider::FmapBorrowed { .. }
|
|
);
|
|
let frame = Frame::containing(physaddr);
|
|
if new_as {
|
|
tree.entry(frame).or_insert((0, p)).0 += 1;
|
|
}
|
|
|
|
if let Some(page) = get_page_info(frame) {
|
|
match page.refcount() {
|
|
None => panic!("mapped page with zero refcount"),
|
|
|
|
Some(RefCount::One | RefCount::Shared(_)) => assert!(
|
|
!(flags.has_write() && !grant.flags().has_write()),
|
|
"page entry has higher permissions than grant!"
|
|
),
|
|
Some(RefCount::Cow(_)) => {
|
|
assert!(!flags.has_write(), "directly writable CoW page!")
|
|
}
|
|
}
|
|
} else {
|
|
//println!("!OWNED {:?}", frame);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*for (base, info) in addr_space.grants.iter() {
|
|
let span = PageSpan::new(base, info.page_count());
|
|
for page in span.pages() {
|
|
let _entry = match addr_space.table.utable.translate(page.start_address()) {
|
|
Some(e) => e,
|
|
None => {
|
|
error!("GRANT AT {:?} LACKING MAPPING AT PAGE {:p}", span, page.start_address().data() as *const u8);
|
|
continue;
|
|
}
|
|
};
|
|
}
|
|
}*/
|
|
println!("Consistency appears correct");
|
|
}
|