-
-
Notifications
You must be signed in to change notification settings - Fork 3k
std: rework uefi allocators #22818
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
Open
RossComputerGuy
wants to merge
4
commits into
ziglang:master
Choose a base branch
from
RossComputerGuy:feat/uefi-alloc-rework
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
std: rework uefi allocators #22818
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
b287b34
std.os.uefi.tables.boot_services: adjust allocate page
RossComputerGuy 789a932
std.os.uefi: refactor allocators
RossComputerGuy 7952530
std.os.uefi: move allocators to heap
RossComputerGuy d5966c8
std.os.heap.uefi_allocators: split each allocator into a separate file
RossComputerGuy File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/// Allocates memory in pages. | ||
/// | ||
/// This allocator is backed by `allocatePages` and is therefore only suitable for usage when Boot Services are available. | ||
const std = @import("../../std.zig"); | ||
const PageAllocator = @This(); | ||
|
||
const mem = std.mem; | ||
const uefi = std.os.uefi; | ||
|
||
const Allocator = mem.Allocator; | ||
|
||
const assert = std.debug.assert; | ||
|
||
memory_type: uefi.tables.MemoryType = .loader_data, | ||
|
||
pub fn allocator(self: *PageAllocator) Allocator { | ||
return Allocator{ | ||
.ptr = self, | ||
.vtable = &vtable, | ||
}; | ||
} | ||
|
||
const vtable = Allocator.VTable{ | ||
.alloc = alloc, | ||
.resize = resize, | ||
.remap = remap, | ||
.free = free, | ||
}; | ||
|
||
fn alloc( | ||
ctx: *anyopaque, | ||
len: usize, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
const self: *PageAllocator = @ptrCast(@alignCast(ctx)); | ||
|
||
assert(len > 0); | ||
const pages = mem.alignForward(usize, len, 4096) / 4096; | ||
|
||
const buf = uefi.system_table.boot_services.?.allocatePages(.any, self.memory_type, pages) catch return null; | ||
return buf.ptr; | ||
} | ||
|
||
fn resize( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) bool { | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
const self: *PageAllocator = @ptrCast(@alignCast(ctx)); | ||
|
||
// If the buffer was originally larger than the new length, we can grow or shrink it in place. | ||
const original_len = mem.alignForward(usize, buf.len, 4096); | ||
const new_aligned_len = mem.alignForward(usize, new_len, 4096); | ||
|
||
if (original_len >= new_aligned_len) return true; | ||
|
||
const new_pages_required = (new_aligned_len - original_len) / 4096; | ||
const start_of_new_pages = @intFromPtr(buf.ptr) + original_len; | ||
|
||
// Try to allocate the necessary pages at the end of the buffer. | ||
const new_pages = uefi.system_table.boot_services.?.allocatePages(.{ .at_address = start_of_new_pages }, self.memory_type, new_pages_required) catch return false; | ||
_ = new_pages; | ||
|
||
// If the above function succeeds, then the new pages were successfully allocated. | ||
return true; | ||
} | ||
|
||
fn remap( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = ctx; | ||
_ = buf; | ||
_ = alignment; | ||
_ = new_len; | ||
_ = ret_addr; | ||
return null; | ||
} | ||
|
||
fn free( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) void { | ||
_ = ctx; | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
const aligned_len = mem.alignForward(usize, buf.len, 4096); | ||
const ptr: [*]align(4096) u8 = @alignCast(buf.ptr); | ||
|
||
uefi.system_table.boot_services.?.freePages(ptr[0..aligned_len]); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
/// Supports the full std.mem.Allocator interface, including up to page alignment. | ||
/// | ||
/// This allocator is backed by `allocatePool` and is therefore only suitable for usage when Boot Services are available. | ||
const std = @import("../../std.zig"); | ||
const PoolAllocator = @This(); | ||
|
||
const mem = std.mem; | ||
const uefi = std.os.uefi; | ||
|
||
const Allocator = mem.Allocator; | ||
|
||
const assert = std.debug.assert; | ||
|
||
memory_type: uefi.tables.MemoryType = .loader_data, | ||
|
||
pub fn allocator(self: *PoolAllocator) Allocator { | ||
return Allocator{ | ||
.ptr = self, | ||
.vtable = &vtable, | ||
}; | ||
} | ||
|
||
const vtable = Allocator.VTable{ | ||
.alloc = alloc, | ||
.resize = resize, | ||
.remap = remap, | ||
.free = free, | ||
}; | ||
|
||
const Header = struct { | ||
ptr: [*]align(8) u8, | ||
len: usize, | ||
}; | ||
|
||
fn getHeader(ptr: [*]u8) *align(1) Header { | ||
return @ptrCast(ptr - @sizeOf(Header)); | ||
} | ||
|
||
fn alloc( | ||
ctx: *anyopaque, | ||
len: usize, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = ret_addr; | ||
const self: *PoolAllocator = @ptrCast(@alignCast(ctx)); | ||
|
||
assert(len > 0); | ||
|
||
const ptr_align = alignment.toByteUnits(); | ||
|
||
// The maximum size of the metadata and any alignment padding. | ||
const metadata_len = mem.alignForward(usize, @sizeOf(Header), ptr_align); | ||
|
||
const full_len = metadata_len + len; | ||
|
||
const buf = uefi.system_table.boot_services.?.allocatePool(self.memory_type, full_len) catch return null; | ||
const unaligned_ptr = buf.ptr; | ||
|
||
const unaligned_addr = @intFromPtr(unaligned_ptr); | ||
const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(Header), ptr_align); | ||
|
||
const aligned_ptr: [*]u8 = @ptrFromInt(aligned_addr); | ||
getHeader(aligned_ptr).ptr = unaligned_ptr; | ||
getHeader(aligned_ptr).len = unaligned_addr + full_len - aligned_addr; | ||
|
||
return aligned_ptr; | ||
} | ||
|
||
fn resize( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) bool { | ||
_ = ctx; | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
// If the buffer was originally larger than the new length, we can grow or shrink it in place. | ||
if (getHeader(buf.ptr).len >= new_len) return true; | ||
|
||
// Otherwise, we cannot grow the buffer. | ||
return false; | ||
} | ||
|
||
fn remap( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = ctx; | ||
_ = buf; | ||
_ = alignment; | ||
_ = new_len; | ||
_ = ret_addr; | ||
return null; | ||
} | ||
|
||
fn free( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) void { | ||
_ = ctx; | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
const header = getHeader(buf.ptr); | ||
|
||
uefi.system_table.boot_services.?.freePool(header.ptr[0..header.len]); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/// Asserts all allocations are at most 8 byte aligned. This is the highest alignment UEFI will give us directly. | ||
/// | ||
/// This allocator is backed by `allocatePool` and is therefore only suitable for usage when Boot Services are available. | ||
const std = @import("../../std.zig"); | ||
const RawPoolAllocator = @This(); | ||
|
||
const mem = std.mem; | ||
const uefi = std.os.uefi; | ||
|
||
const Allocator = mem.Allocator; | ||
|
||
const assert = std.debug.assert; | ||
|
||
memory_type: uefi.tables.MemoryType = .loader_data, | ||
|
||
pub fn allocator(self: *RawPoolAllocator) Allocator { | ||
return Allocator{ | ||
.ptr = self, | ||
.vtable = &vtable, | ||
}; | ||
} | ||
|
||
pub const vtable = Allocator.VTable{ | ||
.alloc = alloc, | ||
.resize = resize, | ||
.remap = remap, | ||
.free = free, | ||
}; | ||
|
||
fn alloc( | ||
ctx: *anyopaque, | ||
len: usize, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = ret_addr; | ||
const self: *RawPoolAllocator = @ptrCast(@alignCast(ctx)); | ||
|
||
// UEFI pool allocations are 8 byte aligned, so we can't do better than that. | ||
std.debug.assert(@intFromEnum(alignment) <= 3); | ||
|
||
const buf = uefi.system_table.boot_services.?.allocatePool(self.memory_type, len) catch return null; | ||
return buf.ptr; | ||
} | ||
|
||
fn resize( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) bool { | ||
_ = ctx; | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
// The original capacity is not known, so we can't ever grow the buffer. | ||
if (new_len > buf.len) return false; | ||
|
||
// If this is a shrink, it will happen in place. | ||
return true; | ||
} | ||
|
||
fn remap( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
new_len: usize, | ||
ret_addr: usize, | ||
) ?[*]u8 { | ||
_ = ctx; | ||
_ = buf; | ||
_ = alignment; | ||
_ = new_len; | ||
_ = ret_addr; | ||
return null; | ||
} | ||
|
||
fn free( | ||
ctx: *anyopaque, | ||
buf: []u8, | ||
alignment: mem.Alignment, | ||
ret_addr: usize, | ||
) void { | ||
_ = ctx; | ||
_ = alignment; | ||
_ = ret_addr; | ||
|
||
uefi.system_table.boot_services.?.freePool(@alignCast(buf)); | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
pub var global_page_allocator = PageAllocator{}; | ||
pub var global_pool_allocator = PoolAllocator{}; | ||
|
||
pub const PageAllocator = @import("uefi/PageAllocator.zig"); | ||
pub const PoolAllocator = @import("uefi/PoolAllocator.zig"); | ||
pub const RawPoolAllocator = @import("uefi/RawPoolAllocator.zig"); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 prefer to keep these in
std.os.uefi
, any reason to move them?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 forget who mentioned it but someone here mentioned that this is what should be done.
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.
It was me, but I've changed my mind - the split into file-as-struct is nice but let's keep them in
std.os.uefi
for now.