| 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; |
| 3 | |
| 4 | /// A protocol is an interface identified by a GUID. |
| 5 | pub const protocol = @import("uefi/protocol.zig"); |
| 6 | pub const DevicePath = @import("uefi/device_path.zig").DevicePath; |
| 7 | pub const hii = @import("uefi/hii.zig"); |
| 8 | |
| 9 | /// Status codes returned by EFI interfaces |
| 10 | pub const Status = @import("uefi/status.zig").Status; |
| 11 | pub const Error = UnexpectedError || Status.Error; |
| 12 | pub const tables = @import("uefi/tables.zig"); |
| 13 | |
| 14 | /// The memory type to allocate when using the pool. |
| 15 | /// Defaults to `.loader_data`, the default data allocation type |
| 16 | /// used by UEFI applications to allocate pool memory. |
| 17 | pub var efi_pool_memory_type: tables.MemoryType = .loader_data; |
| 18 | pub const pool_allocator = @import("uefi/pool_allocator.zig").pool_allocator; |
| 19 | pub const raw_pool_allocator = @import("uefi/pool_allocator.zig").raw_pool_allocator; |
| 20 | |
| 21 | /// The EFI image's handle that is passed to its entry point. |
| 22 | pub var handle: Handle = undefined; |
| 23 | |
| 24 | /// A pointer to the EFI System Table that is passed to the EFI image's entry point. |
| 25 | pub var system_table: *tables.SystemTable = undefined; |
| 26 | |
| 27 | /// UEFI's memory interfaces exclusively act on 4096-byte pages. |
| 28 | pub const Page = [4096]u8; |
| 29 | |
| 30 | /// A handle to an event structure. |
| 31 | pub const Event = *opaque {}; |
| 32 | |
| 33 | pub const EventRegistration = *const opaque {}; |
| 34 | |
| 35 | pub const EventType = packed struct(u32) { |
| 36 | lo_context: u8 = 0, |
| 37 | /// If an event of this type is not already in the signaled state, then |
| 38 | /// the event’s NotificationFunction will be queued at the event’s NotifyTpl |
| 39 | /// whenever the event is being waited on via EFI_BOOT_SERVICES.WaitForEvent() |
| 40 | /// or EFI_BOOT_SERVICES.CheckEvent() . |
| 41 | wait: bool = false, |
| 42 | /// The event’s NotifyFunction is queued whenever the event is signaled. |
| 43 | signal: bool = false, |
| 44 | hi_context: u20 = 0, |
| 45 | /// The event is allocated from runtime memory. If an event is to be signaled |
| 46 | /// after the call to EFI_BOOT_SERVICES.ExitBootServices() the event’s data |
| 47 | /// structure and notification function need to be allocated from runtime |
| 48 | /// memory. |
| 49 | runtime: bool = false, |
| 50 | timer: bool = false, |
| 51 | |
| 52 | /// This event should not be combined with any other event types. This event |
| 53 | /// type is functionally equivalent to the EFI_EVENT_GROUP_EXIT_BOOT_SERVICES |
| 54 | /// event group. |
| 55 | pub const signal_exit_boot_services: EventType = .{ |
| 56 | .signal = true, |
| 57 | .lo_context = 1, |
| 58 | }; |
| 59 | |
| 60 | /// The event is to be notified by the system when SetVirtualAddressMap() |
| 61 | /// is performed. This event type is a composite of EVT_NOTIFY_SIGNAL, |
| 62 | /// EVT_RUNTIME, and EVT_RUNTIME_CONTEXT and should not be combined with |
| 63 | /// any other event types. |
| 64 | pub const signal_virtual_address_change: EventType = .{ |
| 65 | .runtime = true, |
| 66 | .hi_context = 0x20000, |
| 67 | .signal = true, |
| 68 | .lo_context = 2, |
| 69 | }; |
| 70 | }; |
| 71 | |
| 72 | /// The calling convention used for all external functions part of the UEFI API. |
| 73 | pub const cc: std.builtin.CallingConvention = switch (@import("builtin").target.cpu.arch) { |
| 74 | .x86_64 => .{ .x86_64_win = .{} }, |
| 75 | else => .c, |
| 76 | }; |
| 77 | |
| 78 | pub const MacAddress = extern struct { |
| 79 | address: [32]u8, |
| 80 | }; |
| 81 | |
| 82 | pub const Ipv4Address = extern struct { |
| 83 | address: [4]u8, |
| 84 | }; |
| 85 | |
| 86 | pub const Ipv6Address = extern struct { |
| 87 | address: [16]u8, |
| 88 | }; |
| 89 | |
| 90 | pub const IpAddress = extern union { |
| 91 | v4: Ipv4Address, |
| 92 | v6: Ipv6Address, |
| 93 | }; |
| 94 | |
| 95 | /// GUIDs are align(8) unless otherwise specified. |
| 96 | pub const Guid = extern struct { |
| 97 | comptime { |
| 98 | std.debug.assert(std.mem.Alignment.of(Guid) == .@"8"); |
| 99 | } |
| 100 | |
| 101 | time_low: u32 align(8), |
| 102 | time_mid: u16, |
| 103 | time_high_and_version: u16, |
| 104 | clock_seq_high_and_reserved: u8, |
| 105 | clock_seq_low: u8, |
| 106 | node: [6]u8, |
| 107 | |
| 108 | /// Format GUID into hexadecimal lowercase xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx format |
| 109 | pub fn format(self: Guid, writer: *std.Io.Writer) std.Io.Writer.Error!void { |
| 110 | return writer.print("{x:0>8}-{x:0>4}-{x:0>4}-{x:0>2}{x:0>2}-{x}", .{ |
| 111 | self.time_low, |
| 112 | self.time_mid, |
| 113 | self.time_high_and_version, |
| 114 | self.clock_seq_high_and_reserved, |
| 115 | self.clock_seq_low, |
| 116 | self.node, |
| 117 | }); |
| 118 | } |
| 119 | |
| 120 | pub fn eql(a: Guid, b: Guid) bool { |
| 121 | return a.time_low == b.time_low and |
| 122 | a.time_mid == b.time_mid and |
| 123 | a.time_high_and_version == b.time_high_and_version and |
| 124 | a.clock_seq_high_and_reserved == b.clock_seq_high_and_reserved and |
| 125 | a.clock_seq_low == b.clock_seq_low and |
| 126 | std.mem.eql(u8, &a.node, &b.node); |
| 127 | } |
| 128 | }; |
| 129 | |
| 130 | /// An EFI Handle represents a collection of related interfaces. |
| 131 | pub const Handle = *opaque {}; |
| 132 | |
| 133 | /// This structure represents time information. |
| 134 | pub const Time = extern struct { |
| 135 | /// 1900 - 9999 |
| 136 | year: u16, |
| 137 | |
| 138 | /// 1 - 12 |
| 139 | month: u8, |
| 140 | |
| 141 | /// 1 - 31 |
| 142 | day: u8, |
| 143 | |
| 144 | /// 0 - 23 |
| 145 | hour: u8, |
| 146 | |
| 147 | /// 0 - 59 |
| 148 | minute: u8, |
| 149 | |
| 150 | /// 0 - 59 |
| 151 | second: u8, |
| 152 | |
| 153 | _pad1: u8, |
| 154 | |
| 155 | /// 0 - 999999999 |
| 156 | nanosecond: u32, |
| 157 | |
| 158 | /// The time's offset in minutes from UTC. |
| 159 | /// Allowed values are -1440 to 1440 or unspecified_timezone |
| 160 | timezone: i16, |
| 161 | daylight: packed struct(u8) { |
| 162 | /// If true, the time has been adjusted for daylight savings time. |
| 163 | in_daylight: bool, |
| 164 | |
| 165 | /// If true, the time is affected by daylight savings time. |
| 166 | adjust_daylight: bool, |
| 167 | |
| 168 | _: u6, |
| 169 | }, |
| 170 | |
| 171 | _pad2: u8, |
| 172 | |
| 173 | comptime { |
| 174 | std.debug.assert(@sizeOf(Time) == 16); |
| 175 | } |
| 176 | |
| 177 | /// Time is to be interpreted as local time |
| 178 | pub const unspecified_timezone: i16 = 0x7ff; |
| 179 | |
| 180 | fn daysInYear(year: u16, max_month: u4) u9 { |
| 181 | var days: u9 = 0; |
| 182 | var month: u4 = 0; |
| 183 | while (month < max_month) : (month += 1) { |
| 184 | days += std.time.epoch.getDaysInMonth(year, @fromBackingInt(@intCast(month + 1))); |
| 185 | } |
| 186 | return days; |
| 187 | } |
| 188 | |
| 189 | pub fn toEpoch(self: std.os.uefi.Time) u64 { |
| 190 | var year: u16 = 0; |
| 191 | var days: u32 = 0; |
| 192 | |
| 193 | while (year < (self.year - 1971)) : (year += 1) { |
| 194 | days += daysInYear(year + 1970, 12); |
| 195 | } |
| 196 | |
| 197 | days += daysInYear(self.year, @as(u4, @intCast(self.month)) - 1) + self.day; |
| 198 | const hours: u64 = self.hour + (days * 24); |
| 199 | const minutes: u64 = self.minute + (hours * 60); |
| 200 | const seconds: u64 = self.second + (minutes * std.time.s_per_min); |
| 201 | return self.nanosecond + (seconds * std.time.ns_per_s); |
| 202 | } |
| 203 | }; |
| 204 | |
| 205 | /// Capabilities of the clock device |
| 206 | pub const TimeCapabilities = extern struct { |
| 207 | /// Resolution in Hz |
| 208 | resolution: u32, |
| 209 | |
| 210 | /// Accuracy in an error rate of 1e-6 parts per million. |
| 211 | accuracy: u32, |
| 212 | |
| 213 | /// If true, a time set operation clears the device's time below the resolution level. |
| 214 | sets_to_zero: bool, |
| 215 | }; |
| 216 | |
| 217 | /// File Handle as specified in the EFI Shell Spec |
| 218 | pub const FileHandle = *opaque {}; |
| 219 | |
| 220 | test "GUID formatting" { |
| 221 | const bytes: [16]u8 = .{ 137, 60, 203, 50, 128, 128, 124, 66, 186, 19, 80, 73, 135, 59, 194, 135 }; |
| 222 | const guid: Guid = @bitCast(bytes); |
| 223 | |
| 224 | const str = try std.fmt.allocPrint(std.testing.allocator, "{f}", .{guid}); |
| 225 | defer std.testing.allocator.free(str); |
| 226 | |
| 227 | try std.testing.expectEqualStrings(str, "32cb3c89-8080-427c-ba13-5049873bc287"); |
| 228 | } |
| 229 | |
| 230 | test { |
| 231 | _ = tables; |
| 232 | _ = protocol; |
| 233 | } |
| 234 | |
| 235 | pub const UnexpectedError = error{Unexpected}; |
| 236 | |
| 237 | pub fn unexpectedStatus(status: Status) UnexpectedError { |
| 238 | // TODO: debug printing the encountered error? maybe handle warnings? |
| 239 | _ = status; |
| 240 | return error.Unexpected; |
| 241 | } |