Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

display page fault address in oopses, improve how page fault codes are displayed #420

Merged
merged 3 commits into from
Apr 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 120 additions & 12 deletions bitfield/src/bitfield.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
/// | `fn get<U>(&self, packer: Self::Packer<U>) -> U` | Given one of this type's generated packing specs for a `U`-typed value, unpacks the bit range represented by that value as a `U` and returns it. This method panics if the requested bit range does not contain a valid bit pattern for a `U`-typed value, as determined by `U`'s implementation of the [`FromBits`] trait. |
/// | `fn try_get<U>(&self, packer: Self::Packer<U>) -> Result<U, <U as FromBits>::Error>` | Like `get`, but returns a `Result` instead of panicking. |
/// | `fn assert_valid()` | Asserts that the generated bitfield type is valid. This is primarily intended to be used in tests; the macro cannot generate tests for a bitfield type on its own, so a test that simply calls `assert_valid` can be added to check the bitfield type's validity. |
/// | `fn display_ascii(&self) -> impl core::fmt::Display` | Returns a `Display` implementation that formats the bitfield in a multi-line format, using only ASCII characters. See [here](#example-display-output) for examples of this format. |
/// | `fn display_unicode(&self) -> impl core::fmt::Display` | Returns a `Display` implementation that formats the bitfield in a multi-line format, always using Unicode box-drawing characters. See [here](#example-display-output) for examples of this format. |
///
/// The visibility of these methods depends on the visibility of the bitfield
/// struct --- if the struct is defined as `pub(crate) struct MyBitfield<u16> {
Expand Down Expand Up @@ -206,8 +208,14 @@
///
/// ## Example `Display` Output
///
/// Bitfields will automatically generate a pretty [`fmt::Display`]
/// implementation:
/// Bitfields will automatically generate a pretty, multi-line [`fmt::Display`]
/// implementation. The default [`fmt::Display`] specifier uses only ASCII
/// characters, but when Unicode box-drawing characters are also available, the
/// alternate (`{:#}`) [`fmt::Display`] specifier may be used to select a
/// `Display` implementation that uses those characters, and is (in my opinion)
/// even prettier than the default.
///
/// For example:
///
/// ```
/// # use mycelium_bitfield::{bitfield, FromBits};
Expand Down Expand Up @@ -246,9 +254,24 @@
/// # pub const A_BYTE: u8;
/// # }
/// # }
///
/// // Create an example bitfield.
/// let my_bitfield = TypedBitfield::from_bits(0b0011_0101_1001_1110);
/// let formatted = format!("{my_bitfield}");
///
/// // The default `Display` implementation uses only ASCII characters:
/// let formatted_ascii = format!("{my_bitfield}");
/// let expected = r#"
/// 00000000000000000011010110011110
///..............................10 ENUM_VALUE: Baz
///........................100111.. SOME_BITS: 39
///.......................1........ FLAG_1: true
///......................0......... FLAG_2: false
///..............00001101.......... A_BYTE: 13
/// "#.trim_start();
/// assert_eq!(formatted_ascii, expected);
///
/// // The alternate `Display` format uses Unicode box-drawing characters,
/// // and looks even nicer:
/// let formatted_unicode = format!("{my_bitfield:#}");
/// let expected = r#"
/// 00000000000000000011010110011110
/// └┬─────┘││└┬───┘└┤
Expand All @@ -258,8 +281,17 @@
/// │ └──────────── FLAG_2: false (0)
/// └─────────────────── A_BYTE: 13 (00001101)
/// "#.trim_start();
/// assert_eq!(formatted, expected);
/// assert_eq!(formatted_unicode, expected);
/// ```
///
/// For situations where the use of ASCII or Unicode formats is always desired
/// regardless of the behavior of an upstream formatter (e.g., when returning a
/// `fmt::Display` value that doesn't know what format specifier will be used),
/// bitfield types also generate `display_ascii()` and `display_unicode()`
/// methods. These methods return `impl fmt::Display` values that *always*
/// select either the ASCII or Unicode `Display` implementations explicitly,
/// regardless of whether or not the alternate formatting specifier is used.
///
/// [`fmt::Debug`]: core::fmt::Debug
/// [`fmt::Display`]: core::fmt::Display
/// [`fmt::Binary`]: core::fmt::Binary
Expand Down Expand Up @@ -400,11 +432,71 @@ macro_rules! bitfield {
$vis fn assert_valid() {
<$crate::bitfield! { @t $T, $T, Self }>::assert_all_valid(&Self::FIELDS);
}
}

#[automatically_derived]
impl core::fmt::Display for $Name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
/// Returns a value that formats this bitfield in a multi-line
/// format, using only ASCII characters.
///
/// This is equivalent to formatting this bitfield using a `{}`
/// display specifier, but will *never* use Unicode box-drawing
/// characters, even when an upstream formatter uses the `{:#}`
/// `fmt::Display` specifier. This is intended for use on platforms
/// where Unicode box drawing characters are never available.
$vis fn display_ascii(&self) -> impl core::fmt::Display {
struct DisplayAscii($Name);
impl core::fmt::Display for DisplayAscii {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt_ascii(f)
}
}
DisplayAscii(*self)
}

/// Returns a value that formats this bitfield in a multi-line
/// format, always using Unicode box-drawing characters.
///
/// This is equivalent to formatting this bitfield using a `{:#}`
/// format specifier, but will *always* use Unicode box-drawing
/// characters, even when an upstream formatter uses the `{}`
/// `fmt::Display` specifier.
$vis fn display_unicode(&self) -> impl core::fmt::Display {
struct DisplayUnicode($Name);
impl core::fmt::Display for DisplayUnicode {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt_unicode(f)
}
}
DisplayUnicode(*self)
}


fn fmt_ascii(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.pad("")?;
writeln!(f, "{:0width$b}", self.0, width = $T::BITS as usize)?;
f.pad("")?;
$({
let field = Self::$Field;
const NAME: &str = stringify!($Field);
if !NAME.starts_with("_") {
f.pad("")?;
let mut cur_pos = $T::BITS;
while cur_pos > field.most_significant_index() {
f.write_str(".")?;
cur_pos -= 1;
}
write!(f, "{:0width$b}", field.unpack_bits(self.0), width = field.bits() as usize)?;
cur_pos -= field.bits();
while cur_pos > 0 {
f.write_str(".")?;
cur_pos -= 1;
}
writeln!(f, " {NAME}: {:?}", field.unpack(self.0))?
}
})+

Ok(())
}

fn fmt_unicode(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.pad("")?;
writeln!(f, "{:0width$b}", self.0, width = $T::BITS as usize)?;
f.pad("")?;
Expand Down Expand Up @@ -503,6 +595,17 @@ macro_rules! bitfield {
}
}

#[automatically_derived]
impl core::fmt::Display for $Name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if f.alternate() {
self.fmt_unicode(f)
} else {
self.fmt_ascii(f)
}
}
}

#[automatically_derived]
impl core::fmt::Binary for $Name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
Expand Down Expand Up @@ -743,16 +846,21 @@ mod tests {
.with(TestBitfield::LOTS, 0b11010)
.with(TestBitfield::OF, 0)
.with(TestBitfield::FUN, 9);
println!("{test_bitfield}");

let ascii = test_bitfield.to_string();
assert_eq!(ascii, test_bitfield.display_ascii().to_string());

println!("test ASCII display:\n{ascii}");
println!("test unicode display:\n{test_bitfield:#}\n");

let test_debug = TestDebug {
value: 42,
bits: test_bitfield,
};

println!("test_debug(alt): {test_debug:#?}");
println!("test_debug(alt): {test_debug:#?}\n");

println!("test_debug: {test_debug:?}")
println!("test_debug: {test_debug:?}\n");
}

#[test]
Expand Down
17 changes: 16 additions & 1 deletion hal-x86_64/src/control_regs.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use core::arch::asm;
use hal_core::VAddr;
use mycelium_util::bits::bitfield;

pub mod cr3 {
Expand All @@ -15,7 +16,7 @@ pub mod cr3 {
asm!("mov {0}, cr3", out(reg) val, options(readonly));
};
let addr = PAddr::from_u64(val);
tracing::debug!(?addr);
tracing::trace!(rax = ?addr, "mov rax, cr3");
let pml4_page = Page::starting_at_fixed(addr)
.expect("PML4 physical addr not aligned! this is very bad");
(pml4_page, Flags(val))
Expand Down Expand Up @@ -236,6 +237,20 @@ impl Cr4 {
}
}

/// Control Register 2 (CR2) contains the Page Fault Linear Address (PFLA).
pub struct Cr2;

impl Cr2 {
/// Returns the 32-bit Page Fault Linear Address (PFLA) stored in CR2.
pub fn read() -> VAddr {
let addr: u64;
unsafe {
asm!("mov {0}, cr2", out(reg) addr, options(readonly));
};
VAddr::from_u64(addr)
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
2 changes: 1 addition & 1 deletion hal-x86_64/src/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ impl<'a, T> hal_core::interrupt::Context for Context<'a, T> {

impl<'a> ctx::PageFault for Context<'a, PageFaultCode> {
fn fault_vaddr(&self) -> crate::VAddr {
unimplemented!("eliza")
crate::control_regs::Cr2::read()
}

fn debug_error_code(&self) -> &dyn fmt::Debug {
Expand Down
4 changes: 3 additions & 1 deletion src/arch/x86_64/interrupt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ impl hal_core::interrupt::Handlers<Registers> for InterruptHandlers {
where
C: interrupt::Context<Registers = Registers> + hal_core::interrupt::ctx::PageFault,
{
let fault_vaddr = cx.fault_vaddr();
let code = cx.display_error_code();
oops(Oops::fault_with_details(
&cx,
"PAGE FAULT",
&format_args!("\n{}", cx.display_error_code()),
&format_args!("at {fault_vaddr:?}\n{code}"),
))
}

Expand Down