From 3182f4e7f031906486f28eaaaef864f1adf083c0 Mon Sep 17 00:00:00 2001 From: janis Date: Fri, 31 Jul 2026 03:17:17 +0200 Subject: [PATCH] capybaraaaa!! --- Cargo.lock | 34 +++ Cargo.toml | 7 + crates/rbtree/Cargo.lock | 7 + crates/rbtree/Cargo.toml | 6 + crates/rbtree/src/lib.rs | 594 +++++++++++++++++++++++++++++++++++++ kernel/.cargo/config.toml | 3 - kernel/build.rs | 2 +- kernel/src/main.rs | 6 +- kernel/src/memory.rs | 66 ++++- kernel/src/x86_64/cpuid.rs | 33 ++- 10 files changed, 749 insertions(+), 9 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 crates/rbtree/Cargo.lock create mode 100644 crates/rbtree/Cargo.toml create mode 100644 crates/rbtree/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..3ed7163 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,34 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "2.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "bit_field", + "bitflags", + "seq-macro", +] + +[[package]] +name = "rbtree" +version = "0.1.0" + +[[package]] +name = "seq-macro" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b07946b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +resolver = "3" + +members = [ + "crates/*", + "kernel", +] \ No newline at end of file diff --git a/crates/rbtree/Cargo.lock b/crates/rbtree/Cargo.lock new file mode 100644 index 0000000..c087ff8 --- /dev/null +++ b/crates/rbtree/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "rbtree" +version = "0.1.0" diff --git a/crates/rbtree/Cargo.toml b/crates/rbtree/Cargo.toml new file mode 100644 index 0000000..6b15f41 --- /dev/null +++ b/crates/rbtree/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rbtree" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/crates/rbtree/src/lib.rs b/crates/rbtree/src/lib.rs new file mode 100644 index 0000000..8183fdd --- /dev/null +++ b/crates/rbtree/src/lib.rs @@ -0,0 +1,594 @@ +#![cfg_attr(not(test), no_std)] + +extern crate alloc; + +pub trait Node { + type Id: Eq + Copy; + type Key: Ord; + + fn parent(&self) -> Option; + fn set_parent(&mut self, parent: Option); + + fn left(&self) -> Option; + fn set_left(&mut self, left: Option); + + fn right(&self) -> Option; + fn set_right(&mut self, right: Option); + + fn child(&self, left: bool) -> Option { + if left { self.left() } else { self.right() } + } + + fn children(&self) -> (Option, Option) { + (self.left(), self.right()) + } + + fn key(&self) -> &Self::Key; + fn color(&self) -> bool; + fn set_color(&mut self, color: bool); +} + +#[allow(clippy::missing_safety_doc)] +pub trait NodeStore { + fn get(&self, id: N::Id) -> Option<&N>; + unsafe fn get_unchecked(&self, id: N::Id) -> &N { + self.get(id).unwrap() + } + fn get_mut(&mut self, id: N::Id) -> Option<&mut N>; + unsafe fn get_mut_unchecked(&mut self, id: N::Id) -> &mut N { + self.get_mut(id).unwrap() + } + fn insert(&mut self, node: N) -> N::Id; + fn remove(&mut self, id: N::Id) -> Option; +} + +pub struct RBTree> { + root: Option, + store: S, +} + +impl> RBTree { + pub fn new(store: S) -> Self { + Self { root: None, store } + } + + fn try_get(&self, id: N::Id) -> Option<&N> { + self.store.get(id) + } + + fn try_get_mut(&mut self, id: N::Id) -> Option<&mut N> { + self.store.get_mut(id) + } + + pub fn insert(&mut self, node: N) -> N::Id { + self.store.insert(node) + } + + pub fn minimum_of(&self, mut id: N::Id) -> N::Id { + while let Some(left_id) = self.store.get(id).and_then(|node| node.left()) { + id = left_id; + } + id + } + + pub fn maximum_of(&self, mut id: N::Id) -> N::Id { + while let Some(right_id) = self.store.get(id).and_then(|node| node.right()) { + id = right_id; + } + id + } + + pub fn next_of(&self, mut id: N::Id) -> Option { + let node = self.store.get(id)?; + match node.right() { + Some(r) => Some(self.minimum_of(r)), + None => { + let mut p = node.parent()?; + while id == self.store.get(p)?.right()? { + id = p; + p = self.store.get(p)?.parent()?; + } + Some(p) + } + } + } + + pub fn next_back_of(&self, mut id: N::Id) -> Option { + let node = self.store.get(id)?; + match node.left() { + Some(l) => Some(self.maximum_of(l)), + None => { + let mut p = node.parent()?; + while id == self.store.get(p)?.left()? { + id = p; + p = self.store.get(p)?.parent()?; + } + Some(p) + } + } + } + + // x y + // / \ / \ + // a y => x c + // / \ / \ + // b c a b + pub fn rotate_left(&mut self, x: N::Id) -> Option<()> { + let y = self.right_child_of(x)?; + let b = self.left_child_of(y); + + let x_parent = self.parent_of(x); + + if let Some(node) = self.store.get_mut(x) { + node.set_right(b) + } + + if let Some(b_mut) = b.and_then(|id| self.store.get_mut(id)) { + b_mut.set_parent(Some(x)); + } + + if let Some(node) = self.store.get_mut(y) { + node.set_left(Some(x)); + + if let Some(parent) = x_parent { + node.set_parent(Some(parent)); + if self.store.get(parent).unwrap().left() == Some(x) { + self.store.get_mut(parent).unwrap().set_left(Some(y)); + } else { + self.store.get_mut(parent).unwrap().set_right(Some(y)); + } + } else { + self.root = Some(y); + } + } + + Some(()) + } + + // x y + // / \ / \ + // y c => a x + // / \ / \ + // a b b c + pub fn rotate_right(&mut self, x: N::Id) -> Option<()> { + let y = self.left_child_of(x)?; + let b = self.right_child_of(y); + + let x_parent = self.parent_of(x); + + if let Some(node) = self.store.get_mut(x) { + node.set_left(b) + } + + if let Some(b_mut) = b.and_then(|id| self.store.get_mut(id)) { + b_mut.set_parent(Some(x)); + } + + if let Some(node) = self.store.get_mut(y) { + node.set_right(Some(x)); + + if let Some(parent) = x_parent { + node.set_parent(Some(parent)); + if self.store.get(parent).unwrap().left() == Some(x) { + self.store.get_mut(parent).unwrap().set_left(Some(y)); + } else { + self.store.get_mut(parent).unwrap().set_right(Some(y)); + } + } else { + self.root = Some(y); + } + } + + Some(()) + } + + pub fn rotate(&mut self, x: N::Id, left: bool) -> Option<()> { + if left { + self.rotate_left(x) + } else { + self.rotate_right(x) + } + } + + pub fn find_by_key(&self, key: &Q) -> SearchResult + where + N::Key: core::borrow::Borrow, + Q: Ord + ?Sized, + { + use core::borrow::Borrow; + use core::cmp::Ordering::*; + + let Some(mut current) = self.root else { + return SearchResult::Empty; + }; + + loop { + let node = self.store.get(current).unwrap(); + + match key.cmp(node.key().borrow()) { + Less => { + if let Some(left) = node.left() { + current = left; + } else { + return SearchResult::NotFoundLeftOf(current); + } + } + Greater => { + if let Some(right) = node.right() { + current = right; + } else { + return SearchResult::NotFoundRightOf(current); + } + } + Equal => return SearchResult::FoundAt(current), + } + } + } + + pub fn insert_id(&mut self, id: N::Id) { + let node = self.store.get(id).unwrap(); + + match self.find_by_key(node.key()) { + SearchResult::FoundAt(_) => { + panic!("Duplicate key insertion is not allowed"); + } + SearchResult::NotFoundRightOf(parent) => { + self.store.get_mut(parent).unwrap().set_right(Some(id)); + self.store.get_mut(id).unwrap().set_parent(Some(parent)); + self.store.get_mut(id).unwrap().set_color(true); // new node is always red + } + SearchResult::NotFoundLeftOf(parent) => { + self.store.get_mut(parent).unwrap().set_left(Some(id)); + self.store.get_mut(id).unwrap().set_parent(Some(parent)); + self.store.get_mut(id).unwrap().set_color(true); // new node is always red + } + SearchResult::Empty => { + let node = self.store.get_mut(id).unwrap(); + node.set_parent(None); + node.set_color(false); // root is always black + + self.root = Some(id); + } + } + + self.fix_insert(id); + } + + fn fix_insert(&mut self, mut id: N::Id) { + while let Some(parent) = self + .store + .get(id) + .and_then(|node| node.parent()) + .filter(|&p| self.store.get(p).unwrap().color()) + { + // gp is guaranteed to exist because parent is red and the root is black + let grandparent = self + .store + .get(parent) + .and_then(|node| node.parent()) + .unwrap(); + let (uncle, is_left) = if self.store.get(grandparent).unwrap().left() == Some(parent) { + (self.store.get(grandparent).unwrap().right(), true) + } else { + (self.store.get(grandparent).unwrap().left(), false) + }; + + if let Some(uncle_id) = uncle + && self.store.get(uncle_id).unwrap().color() + { + // Case 1: Uncle is red + self.store.get_mut(parent).unwrap().set_color(false); + self.store.get_mut(uncle_id).unwrap().set_color(false); + self.store.get_mut(grandparent).unwrap().set_color(true); + id = grandparent; + } else { + // Case 2: Uncle is black + if self.store.get(parent).unwrap().child(!is_left) == Some(id) { + // Case 2a: id is on the same side as uncle + self.rotate(parent, is_left); + id = parent; + } + + // Case 2b: id is on the opposite side as uncle + self.store.get_mut(parent).unwrap().set_color(false); + self.store.get_mut(grandparent).unwrap().set_color(true); + self.rotate(grandparent, !is_left); + } + } + + if let Some(root_id) = self.root { + self.store.get_mut(root_id).unwrap().set_color(false); + } + } + + fn color_of(&self, id: Option) -> bool { + id.map(|id| self.store.get(id).unwrap().color()) + .unwrap_or(false) + } + + fn set_color_of(&mut self, id: Option, color: bool) { + if let Some(id) = id { + self.store.get_mut(id).unwrap().set_color(color); + } + } + + fn parent_of(&self, id: N::Id) -> Option { + self.store.get(id).unwrap().parent() + } + + fn left_child_of(&self, id: N::Id) -> Option { + self.store.get(id).unwrap().left() + } + + fn right_child_of(&self, id: N::Id) -> Option { + self.store.get(id).unwrap().right() + } + + fn child_of(&self, id: N::Id, left: bool) -> Option { + self.store.get(id).unwrap().child(left) + } + + fn children_of(&self, id: N::Id) -> (Option, Option) { + self.store.get(id).unwrap().children() + } + + // When removing an node from an RB tree, we have to potentially fix the + // invariants of the tree starting from the node X that replaced a deleted + // node Y. + + // X is either root, or X is None and the child of a parent node P which is + // guaranteed to have a non-nil sibling W, since X is doubly-black, and so + // the path through W must hold at least 2 black nodes, including implicit + // black nil-leafs. + pub fn remove(&mut self, z: N::Id) { + // Y is either Z, the removed node in the case that Z has at most + // one child, or Y is Z's successor which is guaranteed to have at most one + // child (the right child). + + let y = match self.children_of(z) { + (None, None) | (Some(_), None) | (None, Some(_)) => z, + // z has a successor, since it has two children. + _ => self.next_of(z).unwrap(), + }; + + // In either case, X is the only child of Y, if it exists. + let x = match self.children_of(y) { + (Some(left), _) => Some(left), + (None, Some(right)) => Some(right), + (None, None) => None, + }; + // Therefore, X must be red or None, and if X is red Y must be black. + + let parent = self.store.get(y).unwrap().parent(); + + // If Y's parent was None, then Y was the root, and the tree is empty. + let Some(parent) = parent else { + self.root = x; + + if let Some(x) = x { + self.store.get_mut(x).unwrap().set_parent(None); + self.store.get_mut(x).unwrap().set_color(false); + } + + return; + }; + + // X is promoted to Y's position, and Y is unlinked from the tree. + if self.store.get(parent).unwrap().left() == Some(y) { + self.store.get_mut(parent).unwrap().set_left(x); + } else { + self.store.get_mut(parent).unwrap().set_right(x); + } + + if let Some(x_id) = x { + // If X was red, color it black. Since it replaces a black node, the + // black-height of the subtree is preserved. + let x = self.store.get_mut(x_id).unwrap(); + x.set_parent(Some(parent)); + x.set_color(false); + + return; + } + + let color = self.store.get(y).unwrap().color(); + // If Y is Z's successor, move Y's data into Z (or move Z's meta into Y). + if y != z { + self.copy_meta_to(z, y); + } + + // If X is None and Y was black, then a black node was removed, and the tree + // needs to be rebalanced. + if !color { + // x is the NIL leaf child of parent, and is doubly black. + let mut x = None; + let mut parent = Some(parent); + while x != self.root && !self.color_of(x) { + // safe because x is not root. + let parent_id = parent.unwrap(); + + // w exists because it is the sibling of x; the subtree at x has + // a black-height of 2, the subtree at w must equally have a + // black-height of 2. + let (mut w, is_left) = if self.store.get(parent_id).unwrap().left() == x { + (self.store.get(parent_id).unwrap().right().unwrap(), true) + } else { + (self.store.get(parent_id).unwrap().left().unwrap(), false) + }; + + // Case 1: w is red -> parent and w's children are black + if self.color_of(Some(w)) { + // set w to black and parent to red + self.set_color_of(Some(w), false); + self.set_color_of(Some(parent_id), true); + + // rotate around parent such that w becomes the parent of parent + self.rotate(parent_id, is_left); + + // x hasn't change, but now the sibling is the child of w, which is black + w = if is_left { + self.store.get(parent_id).unwrap().right().unwrap() + } else { + self.store.get(parent_id).unwrap().left().unwrap() + }; + } + + // Case 2: w is black + + match ( + self.color_of(self.left_child_of(w)), + self.color_of(self.right_child_of(w)), + ) { + (false, false) => { + // Case 2a: w's children are both black + // x carries a phantom black, and its sibling w is black + // we can remove one black from x and w by colouring w + // red and giving x's phantom black to the + // parent. + self.set_color_of(Some(w), true); + x = Some(parent_id); + parent = self.store.get(parent_id).unwrap().parent(); + continue; + } + (near_cousin @ true, false) | (near_cousin @ false, true) => { + // Case 2b: x's near-cousin is red and its far-cousin is black + // colour the near-cousin black and w red, rotate so + // that the near cousin becomes the sibling of x + if near_cousin == is_left { + self.set_color_of(self.child_of(w, !is_left), false); + self.set_color_of(Some(w), true); + self.rotate(w, !is_left); + } + + // fall through to case 2c + } + _ => {} + } + + // Case 2c: x's far-cousin is red + // we can now rotate around the parent to balance the + // subtree at parent without increasing the + // black-height. + // However, in the case that the far-cousin is red and + // the parent is black, this will result, after + // rotating, in the far-cousin's path having fewer black + // nodes than the parent's path: we have taken a black + // node from above the far-cousin and moved it into its + // sibling branch. + // This is fixed by colouring w the colour of the + // parent, and colouring the parent and w's far-cousin + // black. + self.set_color_of(Some(w), self.color_of(Some(parent_id))); + self.set_color_of(Some(parent_id), false); + self.set_color_of(self.child_of(w, !is_left), false); + self.rotate(parent_id, is_left); + + // After the rotation, the inbalance has been resolved. + break; + } + } + } + + fn copy_meta_to(&mut self, from: N::Id, to: N::Id) { + let (parent, color, left, right) = { + let from_node = self.store.get(from).unwrap(); + ( + from_node.parent(), + from_node.color(), + from_node.left(), + from_node.right(), + ) + }; + + let to_node = self.store.get_mut(to).unwrap(); + + to_node.set_parent(parent); + to_node.set_color(color); + to_node.set_left(left); + to_node.set_right(right); + + if let Some(parent) = parent { + let parent_node = self.store.get_mut(parent).unwrap(); + if parent_node.left() == Some(from) { + parent_node.set_left(Some(to)); + } else { + parent_node.set_right(Some(to)); + } + } else { + self.root = Some(to); + } + + if let Some(left) = left { + self.store.get_mut(left).unwrap().set_parent(Some(to)); + } + if let Some(right) = right { + self.store.get_mut(right).unwrap().set_parent(Some(to)); + } + } +} + +pub enum SearchResult { + FoundAt(T), + NotFoundRightOf(T), + NotFoundLeftOf(T), + Empty, +} + +mod default_node { + use crate::Node; + + type DefaultNodeId = u64; + + struct DefaultNodeStore { + nodes: alloc::collections::BTreeMap, + next_id: DefaultNodeId, + } + + struct DefaultNode { + parent: Option, + left: Option, + right: Option, + key: u64, + color: bool, + } + + impl Node for DefaultNode { + type Id = DefaultNodeId; + type Key = u64; + + fn parent(&self) -> Option { + self.parent + } + + fn set_parent(&mut self, parent: Option) { + self.parent = parent; + } + + fn left(&self) -> Option { + self.left + } + + fn set_left(&mut self, left: Option) { + self.left = left; + } + + fn right(&self) -> Option { + self.right + } + + fn set_right(&mut self, right: Option) { + self.right = right; + } + + fn key(&self) -> &Self::Key { + &self.key + } + + fn color(&self) -> bool { + self.color + } + + fn set_color(&mut self, color: bool) { + self.color = color; + } + } +} diff --git a/kernel/.cargo/config.toml b/kernel/.cargo/config.toml index 952962c..6c2340e 100644 --- a/kernel/.cargo/config.toml +++ b/kernel/.cargo/config.toml @@ -1,6 +1,3 @@ -[profile.dev] -codegen-backend = "llvm" - [unstable] json-target-spec = true # lets us specify a custom target specification file build-std-features = ["compiler-builtins-mem"] diff --git a/kernel/build.rs b/kernel/build.rs index a4f5cd9..3b8f48f 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -2,6 +2,6 @@ fn main() { println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=kernel.lds"); - println!("cargo::rustc-link-arg=-Tkernel.lds"); + println!("cargo::rustc-link-arg=-Tkernel/kernel.lds"); // println!("cargo::rustc-link-arg-tests=-Tkernel.lds"); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 585c52f..80ea239 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -3,6 +3,7 @@ use kernel::{ memory::VirtAddr, + serial_println, sync::LazyLock, x86_64::{gdt::GlobalDescriptorTable, idt::InterruptDescriptorTable}, }; @@ -68,7 +69,10 @@ extern "C" fn _start() -> ! { .memory_map, ); - kernel::serial_println!("PMM: {pmm:#?}"); + let leaf = kernel::x86_64::cpuid::Leaf8000008::get(); + serial_println!("max phy: {:#?}", leaf); + + // kernel::serial_println!("PMM: {pmm:#?}"); let fb = limine_requests::FRAMEBUFFER_REQUEST .framebuffers() diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index 2e541ad..986cdba 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -52,6 +52,22 @@ pub struct PageChunk { pub count: usize, } +pub struct PhysicalMemoryManager { + /// On amd64 platforms, there are at most 2^40 pages of addressable physical + /// memory. We maintain a binary tree of free page chunks for each + /// power-of-two range of free pages. + /// + /// When a range of (contiguous) pages is allocated, we find the smallest + /// power-of-two which is larger or equal to the number of pages requested, + /// and try top pop the head of the corresponding free list. If the free + /// list is empty, we try to find a larger free chunk and split it into two + /// smaller chunks recursively until we have a chunk of the desired size. + /// + /// When a range of pages is freed, we try to locate its buddy and merge + /// them into a larger chunk recursively. + buddies: [u64; 40], +} + pub struct PhysicalMemoryAllocator { tree: PageTree, } @@ -556,6 +572,8 @@ pub mod bump { } mod page_tree { + use bit_field::BitField; + use crate::{ memory::{PAGE_SIZE, PhyAddr}, serial_println, @@ -563,10 +581,53 @@ mod page_tree { use core::ops::{Index, IndexMut}; - enum SearchResult { + pub enum SearchResult { Found(T), NotFound(T), } + + /// On amd64 platforms, the maximum physical address is 52 bits, the lower + /// 12 of which are zero for page aligned addresses. + /// Our Tree Node entry needs to store 3 page indices (left, right, parent) and a color bit. + struct CompactPage(u128); + + impl CompactPage { + fn parent(&self) -> usize { + self.0.get_bits(0..40) as usize + } + fn set_parent(&mut self, parent: usize) { + self.0.set_bits(0..40, parent as u128); + } + + fn left_child(&self) -> usize { + self.0.get_bits(40..80) as usize + } + fn set_left_child(&mut self, left: usize) { + self.0.set_bits(40..80, left as u128); + } + + fn right_child(&self) -> usize { + self.0.get_bits(80..120) as usize + } + fn set_right_child(&mut self, right: usize) { + self.0.set_bits(80..120, right as u128); + } + + fn color(&self) -> bool { + self.0.get_bit(120) + } + fn set_color(&mut self, color: bool) { + self.0.set_bit(120, color); + } + + fn data(&self) -> u8 { + self.0.get_bits(121..128) as u8 + } + fn set_data(&mut self, data: u8) { + self.0.set_bits(121..128, data as u128); + } + } + struct Page { left_idx: usize, right_idx: usize, @@ -960,11 +1021,12 @@ mod page_tree { } } + let color = self.color_of(y); if y != z { self.replace(z, y); } - if !self.color_of(y) { + if !color { self.fixup_remove(x); } } diff --git a/kernel/src/x86_64/cpuid.rs b/kernel/src/x86_64/cpuid.rs index fed9655..428c43d 100644 --- a/kernel/src/x86_64/cpuid.rs +++ b/kernel/src/x86_64/cpuid.rs @@ -1,5 +1,7 @@ #![allow(clippy::identity_op)] +use core::fmt::Debug; + use bit_field::BitField; use bitflags::bitflags; @@ -339,18 +341,22 @@ bitflags! { pub struct Leaf8000008(CpuidResult); impl Leaf8000008 { + pub fn get() -> Self { + let result = cpuid(0x80000008, 0); + Self(result) + } pub fn physical_address_bits(&self) -> u8 { self.0.eax.get_bits(0..8) as u8 } pub fn num_linear_address_bits(&self) -> u8 { self.0.eax.get_bits(8..16) as u8 } - pub fn gest_physical_address_bits(&self) -> u8 { + pub fn guest_physical_address_bits(&self) -> u8 { self.0.eax.get_bits(16..24) as u8 } pub fn num_physical_threads(&self) -> u8 { - self.0.ecx.get_bits(0..8) as u8 - 1 + self.0.ecx.get_bits(0..8) as u8 + 1 } pub fn apic_id_size(&self) -> u8 { self.0.ecx.get_bits(12..16) as u8 @@ -371,7 +377,30 @@ impl Leaf8000008 { } } +impl Debug for Leaf8000008 { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("Leaf8000008") + .field("physical_address_bits", &self.physical_address_bits()) + .field("num_linear_address_bits", &self.num_linear_address_bits()) + .field( + "guest_physical_address_bits", + &self.guest_physical_address_bits(), + ) + .field("num_physical_threads", &self.num_physical_threads()) + .field("apic_id_size", &self.apic_id_size()) + .field( + "performance_timestamp_counter_size", + &self.performance_timestamp_counter_size(), + ) + .field("max_invlpgb_page_count", &self.max_invlpgb_page_count()) + .field("max_rdpru_ecx", &self.max_rdpru_ecx()) + .field("flags", &self.flags()) + .finish() + } +} + bitflags! { + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Leaf8000008Flags: u32 { const CLZERO = 1 << 0; const RETIRED_INSTR = 1 << 1;