Skip to content

Commit d5966c8

Browse files
std.os.heap.uefi_allocators: split each allocator into a separate file
1 parent 7952530 commit d5966c8

File tree

4 files changed

+315
-299
lines changed

4 files changed

+315
-299
lines changed

lib/std/heap/uefi/PageAllocator.zig

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/// Allocates memory in pages.
2+
///
3+
/// This allocator is backed by `allocatePages` and is therefore only suitable for usage when Boot Services are available.
4+
const std = @import("../../std.zig");
5+
const PageAllocator = @This();
6+
7+
const mem = std.mem;
8+
const uefi = std.os.uefi;
9+
10+
const Allocator = mem.Allocator;
11+
12+
const assert = std.debug.assert;
13+
14+
memory_type: uefi.tables.MemoryType = .loader_data,
15+
16+
pub fn allocator(self: *PageAllocator) Allocator {
17+
return Allocator{
18+
.ptr = self,
19+
.vtable = &vtable,
20+
};
21+
}
22+
23+
const vtable = Allocator.VTable{
24+
.alloc = alloc,
25+
.resize = resize,
26+
.remap = remap,
27+
.free = free,
28+
};
29+
30+
fn alloc(
31+
ctx: *anyopaque,
32+
len: usize,
33+
alignment: mem.Alignment,
34+
ret_addr: usize,
35+
) ?[*]u8 {
36+
_ = alignment;
37+
_ = ret_addr;
38+
39+
const self: *PageAllocator = @ptrCast(@alignCast(ctx));
40+
41+
assert(len > 0);
42+
const pages = mem.alignForward(usize, len, 4096) / 4096;
43+
44+
const buf = uefi.system_table.boot_services.?.allocatePages(.any, self.memory_type, pages) catch return null;
45+
return buf.ptr;
46+
}
47+
48+
fn resize(
49+
ctx: *anyopaque,
50+
buf: []u8,
51+
alignment: mem.Alignment,
52+
new_len: usize,
53+
ret_addr: usize,
54+
) bool {
55+
_ = alignment;
56+
_ = ret_addr;
57+
58+
const self: *PageAllocator = @ptrCast(@alignCast(ctx));
59+
60+
// If the buffer was originally larger than the new length, we can grow or shrink it in place.
61+
const original_len = mem.alignForward(usize, buf.len, 4096);
62+
const new_aligned_len = mem.alignForward(usize, new_len, 4096);
63+
64+
if (original_len >= new_aligned_len) return true;
65+
66+
const new_pages_required = (new_aligned_len - original_len) / 4096;
67+
const start_of_new_pages = @intFromPtr(buf.ptr) + original_len;
68+
69+
// Try to allocate the necessary pages at the end of the buffer.
70+
const new_pages = uefi.system_table.boot_services.?.allocatePages(.{ .at_address = start_of_new_pages }, self.memory_type, new_pages_required) catch return false;
71+
_ = new_pages;
72+
73+
// If the above function succeeds, then the new pages were successfully allocated.
74+
return true;
75+
}
76+
77+
fn remap(
78+
ctx: *anyopaque,
79+
buf: []u8,
80+
alignment: mem.Alignment,
81+
new_len: usize,
82+
ret_addr: usize,
83+
) ?[*]u8 {
84+
_ = ctx;
85+
_ = buf;
86+
_ = alignment;
87+
_ = new_len;
88+
_ = ret_addr;
89+
return null;
90+
}
91+
92+
fn free(
93+
ctx: *anyopaque,
94+
buf: []u8,
95+
alignment: mem.Alignment,
96+
ret_addr: usize,
97+
) void {
98+
_ = ctx;
99+
_ = alignment;
100+
_ = ret_addr;
101+
102+
const aligned_len = mem.alignForward(usize, buf.len, 4096);
103+
const ptr: [*]align(4096) u8 = @alignCast(buf.ptr);
104+
105+
uefi.system_table.boot_services.?.freePages(ptr[0..aligned_len]);
106+
}

lib/std/heap/uefi/PoolAllocator.zig

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/// Supports the full std.mem.Allocator interface, including up to page alignment.
2+
///
3+
/// This allocator is backed by `allocatePool` and is therefore only suitable for usage when Boot Services are available.
4+
const std = @import("../../std.zig");
5+
const PoolAllocator = @This();
6+
7+
const mem = std.mem;
8+
const uefi = std.os.uefi;
9+
10+
const Allocator = mem.Allocator;
11+
12+
const assert = std.debug.assert;
13+
14+
memory_type: uefi.tables.MemoryType = .loader_data,
15+
16+
pub fn allocator(self: *PoolAllocator) Allocator {
17+
return Allocator{
18+
.ptr = self,
19+
.vtable = &vtable,
20+
};
21+
}
22+
23+
const vtable = Allocator.VTable{
24+
.alloc = alloc,
25+
.resize = resize,
26+
.remap = remap,
27+
.free = free,
28+
};
29+
30+
const Header = struct {
31+
ptr: [*]align(8) u8,
32+
len: usize,
33+
};
34+
35+
fn getHeader(ptr: [*]u8) *align(1) Header {
36+
return @ptrCast(ptr - @sizeOf(Header));
37+
}
38+
39+
fn alloc(
40+
ctx: *anyopaque,
41+
len: usize,
42+
alignment: mem.Alignment,
43+
ret_addr: usize,
44+
) ?[*]u8 {
45+
_ = ret_addr;
46+
const self: *PoolAllocator = @ptrCast(@alignCast(ctx));
47+
48+
assert(len > 0);
49+
50+
const ptr_align = alignment.toByteUnits();
51+
52+
// The maximum size of the metadata and any alignment padding.
53+
const metadata_len = mem.alignForward(usize, @sizeOf(Header), ptr_align);
54+
55+
const full_len = metadata_len + len;
56+
57+
const buf = uefi.system_table.boot_services.?.allocatePool(self.memory_type, full_len) catch return null;
58+
const unaligned_ptr = buf.ptr;
59+
60+
const unaligned_addr = @intFromPtr(unaligned_ptr);
61+
const aligned_addr = mem.alignForward(usize, unaligned_addr + @sizeOf(Header), ptr_align);
62+
63+
const aligned_ptr: [*]u8 = @ptrFromInt(aligned_addr);
64+
getHeader(aligned_ptr).ptr = unaligned_ptr;
65+
getHeader(aligned_ptr).len = unaligned_addr + full_len - aligned_addr;
66+
67+
return aligned_ptr;
68+
}
69+
70+
fn resize(
71+
ctx: *anyopaque,
72+
buf: []u8,
73+
alignment: mem.Alignment,
74+
new_len: usize,
75+
ret_addr: usize,
76+
) bool {
77+
_ = ctx;
78+
_ = alignment;
79+
_ = ret_addr;
80+
81+
// If the buffer was originally larger than the new length, we can grow or shrink it in place.
82+
if (getHeader(buf.ptr).len >= new_len) return true;
83+
84+
// Otherwise, we cannot grow the buffer.
85+
return false;
86+
}
87+
88+
fn remap(
89+
ctx: *anyopaque,
90+
buf: []u8,
91+
alignment: mem.Alignment,
92+
new_len: usize,
93+
ret_addr: usize,
94+
) ?[*]u8 {
95+
_ = ctx;
96+
_ = buf;
97+
_ = alignment;
98+
_ = new_len;
99+
_ = ret_addr;
100+
return null;
101+
}
102+
103+
fn free(
104+
ctx: *anyopaque,
105+
buf: []u8,
106+
alignment: mem.Alignment,
107+
ret_addr: usize,
108+
) void {
109+
_ = ctx;
110+
_ = alignment;
111+
_ = ret_addr;
112+
113+
const header = getHeader(buf.ptr);
114+
115+
uefi.system_table.boot_services.?.freePool(header.ptr[0..header.len]);
116+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/// Asserts all allocations are at most 8 byte aligned. This is the highest alignment UEFI will give us directly.
2+
///
3+
/// This allocator is backed by `allocatePool` and is therefore only suitable for usage when Boot Services are available.
4+
const std = @import("../../std.zig");
5+
const RawPoolAllocator = @This();
6+
7+
const mem = std.mem;
8+
const uefi = std.os.uefi;
9+
10+
const Allocator = mem.Allocator;
11+
12+
const assert = std.debug.assert;
13+
14+
memory_type: uefi.tables.MemoryType = .loader_data,
15+
16+
pub fn allocator(self: *RawPoolAllocator) Allocator {
17+
return Allocator{
18+
.ptr = self,
19+
.vtable = &vtable,
20+
};
21+
}
22+
23+
pub const vtable = Allocator.VTable{
24+
.alloc = alloc,
25+
.resize = resize,
26+
.remap = remap,
27+
.free = free,
28+
};
29+
30+
fn alloc(
31+
ctx: *anyopaque,
32+
len: usize,
33+
alignment: mem.Alignment,
34+
ret_addr: usize,
35+
) ?[*]u8 {
36+
_ = ret_addr;
37+
const self: *RawPoolAllocator = @ptrCast(@alignCast(ctx));
38+
39+
// UEFI pool allocations are 8 byte aligned, so we can't do better than that.
40+
std.debug.assert(@intFromEnum(alignment) <= 3);
41+
42+
const buf = uefi.system_table.boot_services.?.allocatePool(self.memory_type, len) catch return null;
43+
return buf.ptr;
44+
}
45+
46+
fn resize(
47+
ctx: *anyopaque,
48+
buf: []u8,
49+
alignment: mem.Alignment,
50+
new_len: usize,
51+
ret_addr: usize,
52+
) bool {
53+
_ = ctx;
54+
_ = alignment;
55+
_ = ret_addr;
56+
57+
// The original capacity is not known, so we can't ever grow the buffer.
58+
if (new_len > buf.len) return false;
59+
60+
// If this is a shrink, it will happen in place.
61+
return true;
62+
}
63+
64+
fn remap(
65+
ctx: *anyopaque,
66+
buf: []u8,
67+
alignment: mem.Alignment,
68+
new_len: usize,
69+
ret_addr: usize,
70+
) ?[*]u8 {
71+
_ = ctx;
72+
_ = buf;
73+
_ = alignment;
74+
_ = new_len;
75+
_ = ret_addr;
76+
return null;
77+
}
78+
79+
fn free(
80+
ctx: *anyopaque,
81+
buf: []u8,
82+
alignment: mem.Alignment,
83+
ret_addr: usize,
84+
) void {
85+
_ = ctx;
86+
_ = alignment;
87+
_ = ret_addr;
88+
89+
uefi.system_table.boot_services.?.freePool(@alignCast(buf));
90+
}

0 commit comments

Comments
 (0)