const std = @import("std"); const mem = std.mem; const uefi = std.os.uefi; const assert = std.debug.assert; const Allocator = mem.Allocator; const UefiPoolAllocator = struct { fn getHeader(ptr: [*]u8) *[*]align(8) u8 { return @intToPtr(*[*]align(8) u8, @ptrToInt(ptr) - @sizeOf(usize)); } fn alignedAlloc(len: usize, alignment: usize) ?[*]u8 { var unaligned_ptr: [*]align(8) u8 = undefined; if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &unaligned_ptr) != .Success) return null; const unaligned_addr = @ptrToInt(unaligned_ptr); const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment); var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr); getHeader(aligned_ptr).* = unaligned_ptr; return aligned_ptr; } fn alignedFree(ptr: [*]u8) void { _ = uefi.system_table.boot_services.?.freePool(getHeader(ptr).*); } fn alloc( _: *anyopaque, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize, ) Allocator.Error![]u8 { _ = ret_addr; assert(len > 0); assert(std.math.isPowerOfTwo(ptr_align)); var ptr = alignedAlloc(len, ptr_align) orelse return error.OutOfMemory; if (len_align == 0) return ptr[0..len]; return ptr[0..mem.alignBackwardAnyAlign(len, len_align)]; } fn resize( _: *anyopaque, buf: []u8, buf_align: u29, new_len: usize, len_align: u29, ret_addr: usize, ) ?usize { _ = buf_align; _ = ret_addr; return if (new_len <= buf.len) mem.alignAllocLen(buf.len, new_len, len_align) else null; } fn free( _: *anyopaque, buf: []u8, buf_align: u29, ret_addr: usize, ) void { _ = buf_align; _ = ret_addr; alignedFree(buf.ptr); } }; /// Supports the full Allocator interface, including alignment. /// For a direct call of `allocatePool`, see `raw_pool_allocator`. pub const pool_allocator = Allocator{ .ptr = undefined, .vtable = &pool_allocator_vtable, }; const pool_allocator_vtable = Allocator.VTable{ .alloc = UefiPoolAllocator.alloc, .resize = UefiPoolAllocator.resize, .free = UefiPoolAllocator.free, }; /// Asserts allocations are 8 byte aligned and calls `boot_services.allocatePool`. pub const raw_pool_allocator = Allocator{ .ptr = undefined, .vtable = &raw_pool_allocator_table, }; const raw_pool_allocator_table = Allocator.VTable{ .alloc = uefi_alloc, .resize = uefi_resize, .free = uefi_free, }; fn uefi_alloc( _: *anyopaque, len: usize, ptr_align: u29, len_align: u29, ret_addr: usize, ) Allocator.Error![]u8 { _ = len_align; _ = ret_addr; std.debug.assert(ptr_align <= 8); var ptr: [*]align(8) u8 = undefined; if (uefi.system_table.boot_services.?.allocatePool(uefi.efi_pool_memory_type, len, &ptr) != .Success) { return error.OutOfMemory; } return ptr[0..len]; } fn uefi_resize( _: *anyopaque, buf: []u8, old_align: u29, new_len: usize, len_align: u29, ret_addr: usize, ) ?usize { _ = old_align; _ = ret_addr; if (new_len <= buf.len) { return mem.alignAllocLen(buf.len, new_len, len_align); } return null; } fn uefi_free( _: *anyopaque, buf: []u8, buf_align: u29, ret_addr: usize, ) void { _ = buf_align; _ = ret_addr; _ = uefi.system_table.boot_services.?.freePool(@alignCast(8, buf.ptr)); }