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

Guard the lower 1MB of memory #317

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

jasoncouture
Copy link
Contributor

This also adds a new fuction to ignore the attempts to guard the lower 1MB if it's really required.

Closes #311

@jasoncouture jasoncouture force-pushed the protect_lower_1mb branch 6 times, most recently from 0e8dd0d to 0be989a Compare January 9, 2023 16:42
Copy link
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.

Thanks a lot for this PR!

I'm not sure if I understand the approach though. Why are the changes to LegacyMemoryRegion::kind necessary and how do they help in guarding the first MB?

I think the main issue is that the UEFI allocation used for loading the kernel might already use the first MB if we are unlucky. So to be safe, we would need to allocate that first megabyte before creating the allocation. We can use the "allocate at address" API mentioned by @tsoutsman in #311 (comment) for that.

uefi/src/memory_descriptor.rs Outdated Show resolved Hide resolved
api/src/info.rs Outdated Show resolved Hide resolved
@phil-opp phil-opp added the enhancement New feature or request label Jan 9, 2023
@jasoncouture jasoncouture force-pushed the protect_lower_1mb branch 3 times, most recently from 6acd260 to 2b84ae0 Compare January 11, 2023 01:26
@jasoncouture
Copy link
Contributor Author

@phil-opp this is ready for review. I also added tests.

This also adds a new fuction to ignore the attempts to guard the
lower 1MB if it's really required.
@@ -28,8 +28,11 @@ pub struct LegacyFrameAllocator<I, D> {
memory_map: I,
current_descriptor: Option<D>,
next_frame: PhysFrame,
start_frame: PhysFrame,
Copy link
Member

Choose a reason for hiding this comment

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

We already use the name start_frame in multiple places, so we should name this field differently to avoid confusion. I think min_frame would be a fitting name:

Suggested change
start_frame: PhysFrame,
min_frame: PhysFrame,

Comment on lines +56 to +67
if frame.start_address().as_u64() < LOWER_MEMORY_END_PAGE {
// Skip the first 1MB of frames, regardless of what was requested.
// there are use cases that require lower conventional memory access. (Such as SMP SIPI)
return Self::new(memory_map);
}

return unsafe { Self::unsafe_new_starting_at(frame, memory_map) };
}

/// Creates a new frame allocator based on the given legacy memory regions. Skips any frames
/// before the given `frame`, without attempting to protect the lower 1MB of memory.
pub unsafe fn unsafe_new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
Copy link
Member

Choose a reason for hiding this comment

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

This seems a bit complicated. How about we just use a max call in new_starting_at and keep calling it from the new function?

Suggested change
if frame.start_address().as_u64() < LOWER_MEMORY_END_PAGE {
// Skip the first 1MB of frames, regardless of what was requested.
// there are use cases that require lower conventional memory access. (Such as SMP SIPI)
return Self::new(memory_map);
}
return unsafe { Self::unsafe_new_starting_at(frame, memory_map) };
}
/// Creates a new frame allocator based on the given legacy memory regions. Skips any frames
/// before the given `frame`, without attempting to protect the lower 1MB of memory.
pub unsafe fn unsafe_new_starting_at(frame: PhysFrame, memory_map: I) -> Self {
// Skip the first 1MB of frames, regardless of what was requested.
// there are use cases that require lower conventional memory access. (Such as SMP SIPI)
let lower_mem_end = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
let frame = core::cmp::max(frame, lower_mem_end);

Comment on lines +48 to +50
// also skip the first 1MB of frames, there are use cases that require lower conventional memory access. (Such as SMP SIPI)
let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
unsafe { Self::unsafe_new_starting_at(start_frame, memory_map) }
Copy link
Member

Choose a reason for hiding this comment

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

With the suggested changes below, we could keep the previous implementation:

Suggested change
// also skip the first 1MB of frames, there are use cases that require lower conventional memory access. (Such as SMP SIPI)
let start_frame = PhysFrame::containing_address(PhysAddr::new(LOWER_MEMORY_END_PAGE));
unsafe { Self::unsafe_new_starting_at(start_frame, memory_map) }
let start_frame = PhysFrame::containing_address(PhysAddr::new(0x1000));
Self::new_starting_at(start_frame, memory_map)

Comment on lines +143 to +145
} else if descriptor.start() >= next_free
|| end <= self.start_frame.start_address()
{
Copy link
Member

Choose a reason for hiding this comment

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

I would prefer to split this condition so that we can add a comment:

Suggested change
} else if descriptor.start() >= next_free
|| end <= self.start_frame.start_address()
{
} else if descriptor.start() >= next_free {
MemoryRegionKind::Usable
} else if end <= self.start_frame.start_address() {
// treat regions that end before the minimum frame as usable too

} else if descriptor.start() >= next_free {
} else if descriptor.start() >= next_free
|| end <= self.start_frame.start_address()
{
MemoryRegionKind::Usable
} else {
// part of the region is used -> add it separately
Copy link
Member

Choose a reason for hiding this comment

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

I think we need to update this else branch too, in case the descriptor goes beyond 0x100000.

api/src/info.rs Outdated
Comment on lines 156 to 157
/// Unused conventional memory, can be used by the kernel.
Usable,
Copy link
Member

Choose a reason for hiding this comment

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

Reordering the variants changes their integer value, which is technically a breaking change. So we should undo this change if possible.

tests/test_kernels/lower_memory_free/src/lib.rs Outdated Show resolved Hide resolved
Comment on lines +25 to +28
if region.end <= LOWER_MEMORY_END_PAGE && region.kind == MemoryRegionKind::Usable {
let pages = (region.end - region.start) / 4096;
count += pages;
}
Copy link
Member

Choose a reason for hiding this comment

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

We should also account for regions that don't end at LOWER_MEMORY_END_PAGE. For example:

Suggested change
if region.end <= LOWER_MEMORY_END_PAGE && region.kind == MemoryRegionKind::Usable {
let pages = (region.end - region.start) / 4096;
count += pages;
}
if region.kind == MemoryRegionKind::Usable && region.start < LOWER_MEMORY_END_PAGE {
let end = core::cmp::min(region.end, LOWER_MEMORY_END_PAGE);
let pages = (end - region.start) / 4096;
count += pages;
}

@phil-opp
Copy link
Member

Thanks for the updates! I left some suggestions, otherwise these changes look good to me. However, I still think that we need to ensure that we don't use the first megabyte for the kernel allocation in the UEFI loader, as I noted above:

I think the main issue is that the UEFI allocation used for loading the kernel might already use the first MB if we are unlucky. So to be safe, we would need to allocate that first megabyte before creating the allocation. We can use the "allocate at address" API mentioned in #311 (comment) for that.

jasoncouture and others added 2 commits January 13, 2023 20:43
Co-authored-by: Philipp Oppermann <dev@phil-opp.com>
This change was not intentional.
@phil-opp
Copy link
Member

@jasoncouture Friendly ping :). What's the status of this?

@jasoncouture
Copy link
Contributor Author

@jasoncouture Friendly ping :). What's the status of this?

I'll take another look weekend after next. Got some personal things to attend for the next week or so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request waiting-on-author
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UEFI boot should avoid using the lower 1MB of physical RAM
2 participants