Skip to content

make VirtAddr generic over address validity#586

Draft
Freax13 wants to merge 1 commit intorust-osdev:masterfrom
Freax13:feature/la-57-with-generics
Draft

make VirtAddr generic over address validity#586
Freax13 wants to merge 1 commit intorust-osdev:masterfrom
Freax13:feature/la-57-with-generics

Conversation

@Freax13
Copy link
Copy Markdown
Member

@Freax13 Freax13 commented Mar 7, 2026

As proposed by Joe and Philipp, this patch adds a generic parameter for VirtAddr. This generic parameter can be used to enforce validity for addresses with 4 level paging or 5 level paging.

This patch is still a work in progress because I wanted to make that this is what you had in mind before I spend more time on it.

I slightly modified the VirtValidity interface to because check_virt_addr by itself cannot be used to implement truncation, but truncation can be used to implement check_virt_addr. That's just a minor implementation detail though, we can discuss this later.

TODO:

  • Add a feature to gate enabling the const_trait_impl feature.
  • Fix up the remaining code. I didn't adjust all of the code to take a generic parameter for the validity yet. As of right now, there are a ton of compilation errors.
  • Figure out how to make the mapper impls work with VirtValidity. VirtValidity by itself doesn't tell the mappers how many levels there are. I don't expect this to be difficult to solve.
  • Default the validity generic parameter to Virt48. Currently, I intentionally left the default out, so that the compiler would tell me about all the code that needed to become aware of it. Rust doesn't allow defaulted generic parameters after non-defaulted ones, so I currently put the validity parameter before the page size parameter for Page. We'll probably want to swap that once we set the default.
  • Test that the Add/Sub/Step impls work and fix them if they don't. Up until now I mostly focused on fixing compilation errors and not so much on the code actually working.
  • Add type aliases? User would probably benefit from VirtAddr48, VirtAddr57, Page48, and Page57.
  • Do the same thing for PhysAddr.
  • Rebase this on next. This will very likely be a breaking change.

Closes #435

As proposed by Joe and Philipp, this patch adds a generic parameter for
VirtAddr. This generic parameter can be used to enforce validity for
addresses with 4 level paging or 5 level paging.

This patch is still a work in progress because I wanted to make that
this is what you had in mind before I spend more time on it.

I slightly modified the VirtValidity interface to because
check_virt_addr by itself cannot be used to implement truncation, but
truncation can be used to implement check_virt_addr. That's just a
minor implementation detail though, we can discuss this later.

TODO:
- Add a feature to gate enabling the const_trait_impl feature.
- Fix up the remaining code. I didn't adjust all of the code to take a
  generic parameter for the validity yet. As of right now, there are a
  ton of compilation errors.
- Figure out how to make the mapper impls work with VirtValidity.
  VirtValidity by itself doesn't tell the mappers how many levels there
  are. I don't expect this to be difficult to solve.
- Default the validity generic parameter to Virt48. Currently, I
  intentionally left the default out, so that the compiler would tell
  me about all the code that needed to become aware of it. Rust doesn't
  allow defaulted generic parameters after non-defaulted ones, so I
  currently put the validity parameter before the page size parameter
  for Page. We'll probably want to swap that once we set the default.
- Test that the Add/Sub/Step impls work and fix them if they don't. Up
  until now I mostly focused on fixing compilation errors and not so
  much on the code actually working.
- Add type aliases? User would probably benefit from VirtAddr48,
  VirtAddr57, Page48, and Page57.
- Do the same thing for PhysAddr.
- Rebase this on next. This will very likely be a breaking change.
Copy link
Copy Markdown
Member

@phil-opp phil-opp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the slow reply!

I think the approach in general looks good, it just adds quite a bit of noise.

How about we add a non-generic RawVirtAddr type to avoid the generic read methods? This RawVirtAddr could implement TryInto for the two possible VirtAddr<V> types so that you can do the conversion yourself.

For the corresponding write methods, we could take an Into<RawVirtAddr> as argument and implement this trait for all VirtAddr variants.

unsafe {
let val: u64;
asm!(concat!("rd", $name, "base {}"), out(reg) val, options(nomem, nostack, preserves_flags));
VirtAddr::new_unsafe(val)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be problematic if the value is a 5-level address and V is a 48 bit address.

Comment thread src/addr.rs
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct VirtAddr(u64);
pub struct VirtAddr<T: VirtValidity>(u64, PhantomData<T>);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we set the default for T to Virt48?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it's on my TODO list.

Comment thread src/lib.rs
#![warn(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(unsafe_op_in_unsafe_fn)]
#![feature(const_trait_impl)]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to implement this feature on stable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not with the current interface, although another interface certainly could work.

For example we could define the trait like this:

pub trait VirtValidity {
    /// Bitmask of all the bits that may be set in the address.
    const MASK: u64;
}

This would work on stable, but would limit the VirtValidity traits usability somewhat because the bitmask would have to be defined at compile time. This would allow implementing Virt48 and Virt57, but not VirtRt.

@Freax13
Copy link
Copy Markdown
Member Author

Freax13 commented Apr 18, 2026

I think we should take a step back and think about how this is going to be used. Based on the discussion in #435, my impression was that the goal of this approach is to help support LA48 and LA57 within the same binary, but I'm still not sure how that's supposed to work.

Here's the problem I have: Personally, I like using the VirtAddr and Page wherever possible and not just near code that actually interacts with this crate. I do that because I like that these types have the invariant of always containing valid addresses and that's a property that's often useful. I also like storing address types in data structures for the same reason. If we make the address types generic over the address bits, I'm not sure how that can be used to implement code that supports both LA48 and LA57 within the same binary. My best guess is that in order to be able to support the biggest possible address space, I would have to start using VirtAddr<LA57> everywhere including functions and data structures. But, of course, that comes of the disadvantage of no longer being able to rely on my addresses actually being valid if the CPU doesn't support LA57 and is running in LA48 mode and I'm not too happy about that.

What do we expect users to do? Do we expect them to use the wider LA57 address types and manually check that the upper bits are not set when running in LA48 mode? Do we expect them to use the wider LA57 address types and simply not care if the upper bits are set when running in LA48 mode? Do we expect them to write generic code supporting both and switching between the two instantiations based on a runtime flag? Do we expect them to use VirtRt? If so, what do we expect them to use when writing const code? Should const code always be conservative and assume that only LA48 is available?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Question] Is there a reason to not support pagemaps level 5 ?

2 participants