-
Notifications
You must be signed in to change notification settings - Fork 130
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
Handle parent table flags in Mapper methods #114
Conversation
This is my take on #112 |
ah, doh.. should have named it |
done |
Thanks for the pull request! I like your approach more than the approach in #112 because it is more predictable. However, I'm still not sure if this is the best design. Mainly, I have the following design questions:
|
There might be.. there are other flags, too.
I don't know, we need them to build the entries, so it probably would lead to failures, if someone forgets them. |
like NO_CACHE and WRITE_THROUGH |
Good point! Note that this is only true for the recursive page table. For the mapped page table we use the separate physical mapping for modifying the page tables. |
Good examples… However, I still think we can set some flags automatically, such as I think my preferred solution is currently this:
This gives a high amount of control to the user but still keeps the API for common operations simple. What do you think? |
Sounds good to me |
I might work on this in a week. |
Ah, I just saw this thread. I think there is a use-case for mapping a page as user-accessible but not making it user-accessible yet. For example, if I was implementing a kernel-side elf-loader, I might want to set up mappings to a bunch of user pages but not allow user-mode to access the page yet. One could then flip the bit at the higher level to allow access to all of the pages. |
Why would the user-mode program be running before it's ELF file is loaded? The table hierarchy is not used before you load it into the CR3 register, so I don't think that you need an additional "global" switch in this case. |
In general, I think there are two approaches that are both valid:
For me, variant 2 makes most sense as a default because that's you want most of the case. For example, if you call Variant 1 also has valid use cases, e.g. setting flags that behave differently on higher level tables such as the mentioned cache flags. So I think it makes sense to support this variant too, e.g. in form of a visitor API (proposed in #121). I think we can ergonomically cover most use-cases by defaulting to variant 2, but additionally providing advanced methods that give the user full control (variant 1). |
Imagine for example, that the kernel loads another binary/library into the same address space as another thread. Another example would be some sort of ipc mechanism for atomically bulk-transferring data. I agree though that variant 1 makes sense to support via something like visitors, rather than the default. |
Thanks for the examples! I don't think that such a setup would be common, but I agree that we should still support it somehow. |
Yep, admittedly I tend to have weird use cases because I like to play around with non-conventional OS designs... |
Updated PR with changes mentioned in #114 (comment) Additional methods for p[432] entries can be added later in a separate PR. |
@@ -462,13 +608,16 @@ impl<P: PhysToVirt> PageTableWalker<P> { | |||
if let Some(frame) = allocator.allocate_frame() { | |||
entry.set_frame( | |||
frame.frame(), | |||
PageTableFlags::PRESENT | PageTableFlags::WRITABLE, | |||
PageTableFlags::PRESENT | PageTableFlags::WRITABLE | insert_flags, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just pass insert_flags
here. Since we access the page table frame through a separate mapping, the PRESENT
and WRITABLE
flags should be only needed if the page is mapped with these flags (or if the user explicitly specified them in parent_table_flags
).
Thanks a lot for the update! I only took a quick glance, but what I saw looks very good. I try to do a full review soon. |
src/structures/paging/mapper/mod.rs
Outdated
/// Create USER_ACCESSIBLE mapping and update the top hierarchy to be USER_ACCESSIBLE, too: | ||
/// | ||
/// ``` | ||
/// # use x86_64::structures::paging::{ | ||
/// # Mapper, UnusedPhysFrame, Page, FrameAllocator, | ||
/// # Size4KiB, OffsetPageTable, page_table::PageTableFlags | ||
/// # }; | ||
/// # fn test(mapper: &mut OffsetPageTable, frame_allocator: &mut impl FrameAllocator<Size4KiB>, | ||
/// # page: Page<Size4KiB>, frame: UnusedPhysFrame) { | ||
/// mapper | ||
/// .map_to_with_table_flags( | ||
/// page, | ||
/// frame, | ||
/// PageTableFlags::PRESENT | ||
/// | PageTableFlags::WRITABLE | ||
/// | PageTableFlags::USER_ACCESSIBLE, | ||
/// PageTableFlags::PRESENT | ||
/// | PageTableFlags::WRITABLE | ||
/// | PageTableFlags::USER_ACCESSIBLE, | ||
/// frame_allocator, | ||
/// ) | ||
/// .unwrap() | ||
/// .flush(); | ||
/// # } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe make this example use different flags for the entry and the parent tables to show a potential use case for this method.
src/structures/paging/mapper/mod.rs
Outdated
/// Updates the flags of an existing mapping. | ||
fn update_flags_p4_entry( | ||
&mut self, | ||
page: Page<S>, | ||
flags: PageTableFlags, | ||
) -> Result<MapperFlush<S>, FlagUpdateError>; | ||
|
||
/// Updates the flags of an existing mapping. | ||
fn update_flags_p3_entry( | ||
&mut self, | ||
page: Page<S>, | ||
flags: PageTableFlags, | ||
) -> Result<MapperFlush<S>, FlagUpdateError>; | ||
|
||
/// Updates the flags of an existing mapping. | ||
fn update_flags_p2_entry( | ||
&mut self, | ||
page: Page<S>, | ||
flags: PageTableFlags, | ||
) -> Result<MapperFlush<S>, FlagUpdateError>; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should name these methods "set" instead of "update" to make it more clear that they overwrite the entry flags (in contrast to ORing the flags). Also, I think I'm in favor of naming the methods set_pX_entry_flags
instead of set_flags_pX_entry
, but this is only personal preference.
@@ -39,6 +41,7 @@ struct PhysOffset { | |||
} | |||
|
|||
impl PhysToVirt for PhysOffset { | |||
#[inline] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These #[inline]
attributes are fine with me, but perhaps mention their addition in the pull request/commit description.
entry.set_frame(frame.frame(), Flags::PRESENT | Flags::WRITABLE); | ||
entry.set_frame( | ||
frame.frame(), | ||
Flags::PRESENT | Flags::WRITABLE | insert_flags, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should document that the PRESENT
and WRITABLE
flags are always set for higher level page table entries because the recursive page table approach requires it. Preferably we both add a code comment here and also add it to the documentation of the RecursivePageTable
type so that users see it too.
@haraldh Friendly ping :). |
yeah, sorry, got distracted by other projects 😄 |
No worries! I just wanted to ensure that you saw my comments. |
not part of this patch |
Addressed in #151 |
Add a new map_to_with_table_flags method with an additional parent_table_flags argument to the flags of the parent table entries. map_to is kept without API changes as a convenience method. It automatically infers the parent table flags from the flags of the mapping and then calls into map_to_with_table_flags. To ensure that flags like NO_CACHE are not automatically set, which have a different meaning on higher level tables, a hardcoded mask of "safe" flags (PRESENT | WRITABLE | USER_ACCESSIBLE) is used. To keep the recursive page table working, PRESENT and WRITABLE flags for parent entries are also added.
rebased to latest master |
@phil-opp Friendly ping :). |
Sorry for the delay. Unfortunately I'm a bit busy right now, but I try to review this as soon as possible. |
Any updates on this? Sorry for bothering you guys, but I’ve encountered the same problem lately, and I desperately want this feature:) |
Sorry for the delay! I'm taking a look at it right now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the update @haraldh, it looks very good now! I only spotted one remaining issue (see the inline comment), afterwards this should be ready to merge.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the quick update!
I classified this as a breaking change because it changes the behavior of the |
Published as version 0.11.0. |
Thanks! |
Thank you! |
Adds a new
map_to_with_table_flags
method with an additionalparent_table_flags
argument to the flags of the parent table entries.The
map_to
method is kept without API changes as a convenience method. It automatically infers the parent table flags from the flags of the mapping and then calls into map_to_with_table_flags. To ensure that flags like NO_CACHE are not automatically set, which have a different meaning on higher level tables, a hardcoded mask of "safe" flags (PRESENT | WRITABLE | USER_ACCESSIBLE) is used.To keep the recursive page table working, PRESENT and WRITABLE flags for parent entries are also added.
This also adds methods to set the flags of the level 4/3/2 page tables.
Fixes #113
This is a breaking change.
(Description updated by @phil-opp)