/
stivale2.zig
662 lines (586 loc) · 24.5 KB
/
stivale2.zig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
const std = @import("std");
const expect = std.testing.expect;
/// Anchor for non-ELF kernels
pub const Anchor = packed struct {
anchor: [15]u8 = "STIVALE2 ANCHOR",
bits: u8,
phys_load_addr: u64,
phys_bss_start: u64,
phys_bss_end: u64,
phys_stivale2hdr: u64,
};
/// A tag containing a unique identifier, which must belong to a type which is a u64, non-exhaustive enum.
pub fn TagGeneric(comptime Id: type) type {
if (@typeInfo(Id) != .Enum) @compileError("Tag identifier must be an enum");
if (@typeInfo(Id).Enum.tag_type != u64) @compileError("Tag identifier enum tag type isn't u64");
if (@typeInfo(Id).Enum.is_exhaustive) @compileError("Tag identifier must be a non-exhaustive enum");
return packed struct {
const Self = @This();
/// The unique identifier of the tag
identifier: Id,
/// The next tag in the linked list
next: ?*const Self = null,
};
}
test "TagGeneric" {
const Identifier = enum(u64) {
hello = 0x0,
world = 0x1,
_,
};
const Tag = TagGeneric(Identifier);
try expect(@bitSizeOf(Tag) == 128);
}
/// The Header contains information passed from the kernel to the bootloader.
/// The kernel must have a section `.stivale2hdr` either containing a header, or an anchor pointing to one.
pub const Header = packed struct {
/// The address to be jumped to as the entry point of the kernel. If 0, the ELF entry point will be used.
entry_point: u64 = 0,
/// The stack address which will be in ESP/RSP when the kernel is loaded.
/// The stack must be at least 256 bytes, and must have a 16 byte aligned address.
stack: ?*u8,
flags: Flags,
/// Pointer to the first tag of the linked list of header tags.
tags: ?*const Tag,
pub const Flags = packed struct {
/// Reserved and unused
reserved: u1 = 0,
/// If set, all pointers are to be offset to the higher half.
higher_half: u1 = 0,
/// If set, enables protected memory ranges.
pmr: u1 = 0,
/// Undefined and must be set to 0.
zeros: u61 = 0,
};
pub const Tag = TagGeneric(Identifier);
/// Unique identifiers for each header tag
pub const Identifier = enum(u64) {
any_video = 0xc75c9fa92a44c4db,
framebuffer = 0x3ecc1bc43d0f7971,
framebuffer_mtrr = 0x4c7bb07731282e00,
terminal = 0xa85d499b1823be72,
smp = 0x1ab015085f3273df,
five_level_paging = 0x932f477032007e8f,
unmap_null = 0x92919432b16fe7e7,
_,
};
/// This tag tells the bootloader that the kernel has no requirement for a framebuffer to be initialised.
/// Using neither this tag nor `FramebufferTag` means "force CGA text mode", and the bootloader will
/// refuse to boot the kernel if that cannot be fulfilled.
pub const AnyVideoTag = packed struct {
tag: Tag = .{ .identifier = .any_video },
preference: Preference,
pub const Preference = enum(u64) {
linear = 0,
no_linear = 1,
};
};
/// This tag tells the bootloader framebuffer preferences. If used without `AnyVideo`, the bootloader
/// will refuse to boot the kernel if a framebuffer cannot be initialised. Using neither means force CGA
/// text mode, and the bootloader will refuse to boot the kernel if that cannot be fulfilled.
pub const FramebufferTag = packed struct {
tag: Tag = .{ .identifier = .framebuffer },
width: u16,
height: u16,
bpp: u16,
unused: u16 = 0,
};
/// **WARNING:** This tag is deprecated. Use is discouraged and may not be supported on newer bootloaders!
/// This tag tells the bootloader to set up MTRR write-combining for the framebuffer.
pub const FramebufferMtrrTag = packed struct {
tag: Tag = .{ .identifier = .framebuffer_mtrr },
};
/// This tag tells the bootloader to set up a terminal for the kernel. The terminal may run in framebuffer
/// or text mode.
pub const TerminalTag = packed struct {
tag: Tag = .{ .identifier = .terminal },
flags: @This().Flags,
/// Address of the terminal callback function
callback: u64,
pub const Flags = packed struct {
/// Set if a callback function is provided
callback: u1 = 0,
/// Undefined and must be set to 0.
zeros: u63 = 0,
};
};
/// This tag enables support for booting up application processors.
pub const SmpTag = packed struct {
tag: Tag = .{ .identifier = .smp },
flags: @This().Flags,
pub const Flags = packed struct {
/// Use xAPIC
xapic: u1 = 0,
/// Use x2APIC if possible
x2apic: u1 = 0,
/// Undefined and must be set to 0.
zeros: u62 = 0,
};
};
/// This tag enables support for 5-level paging, if available.
pub const FiveLevelPagingTag = packed struct {
tag: Tag = .{ .identifier = .five_level_paging },
};
/// This tag tells the bootloader to unmap the first page of the virtual address space.
pub const UnmapNullTag = packed struct {
tag: Tag = .{ .identifier = .unmap_null },
};
};
test "Header Size" {
try expect(@bitSizeOf(Header) == 256);
}
test "Header Tag Sizes" {
try expect(@bitSizeOf(Header.AnyVideoTag) == 192);
try expect(@bitSizeOf(Header.FramebufferTag) == 192);
try expect(@bitSizeOf(Header.FramebufferMtrrTag) == 128);
try expect(@bitSizeOf(Header.TerminalTag) == 256);
try expect(@bitSizeOf(Header.SmpTag) == 192);
try expect(@bitSizeOf(Header.FiveLevelPagingTag) == 128);
try expect(@bitSizeOf(Header.UnmapNullTag) == 128);
}
/// The Struct contains information passed from the bootloader to the kernel.
/// A pointer to this is passed to the kernel as an argument to the entry point.
pub const Struct = packed struct {
/// Null terminated ASCII string
bootloader_brand: [64]u8,
/// Null terminated ASCII string
bootloader_version: [64]u8,
/// Pointer to the first tag of the linked list of tags.
tags: ?*const Tag = null,
pub const Tag = TagGeneric(Identifier);
/// Unique identifiers for each struct tag
pub const Identifier = enum(u64) {
pmrs = 0x5df266a64047b6bd,
cmdline = 0xe5e76a1b4597a781,
memmap = 0x2187f79e8612de07,
framebuffer = 0x506461d2950408fa,
framebuffer_mtrr = 0x6bc1a78ebe871172,
textmode = 0x38d74c23e0dca893,
edid = 0x968609d7af96b845,
terminal = 0xc2b3f4c3233b0974,
modules = 0x4b6fe466aade04ce,
rsdp = 0x9e1786930a375e78,
smbios = 0x274bd246c62bf7d1,
epoch = 0x566a7bed888e1407,
firmware = 0x359d837855e3858c,
efi_system_table = 0x4bc5ec15845b558e,
kernel_file = 0xe599d90c2975584a,
kernel_file_v2 = 0x37c13018a02c6ea2,
kernel_slide = 0xee80847d01506c57,
smp = 0x34d1d96339647025,
pxe_server_info = 0x29d1e96239247032,
mmio32_uart = 0xb813f9b8dbc78797,
dtb = 0xabb29bd49a2833fa,
vmap = 0xb0ed257db18cb58f,
_,
};
/// This struct contains all detected tags, returned by `Struct.parse()`
pub const Parsed = struct {
bootloader_brand: []const u8,
bootloader_version: []const u8,
pmrs: ?*const PmrsTag = null,
cmdline: ?*const CmdlineTag = null,
memmap: ?*const MemmapTag = null,
framebuffer: ?*const FramebufferTag = null,
framebuffer_mtrr: ?*const FramebufferMtrrTag = null,
textmode: ?*const TextModeTag = null,
edid: ?*const EdidTag = null,
terminal: ?*const TerminalTag = null,
modules: ?*const ModulesTag = null,
rsdp: ?*const RsdpTag = null,
smbios: ?*const SmbiosTag = null,
epoch: ?*const EpochTag = null,
firmware: ?*const FirmwareTag = null,
efi_system_table: ?*const EfiSystemTableTag = null,
kernel_file: ?*const KernelFileTag = null,
kernel_file_v2: ?*const KernelFileV2Tag = null,
kernel_slide: ?*const KernelSlideTag = null,
smp: ?*const SmpTag = null,
pxe_server_info: ?*const PxeServerInfoTag = null,
mmio32_uart: ?*const Mmio32UartTag = null,
dtb: ?*const DtbTag = null,
vmap: ?*const VmapTag = null,
};
/// Returns `Struct.Parsed`, filled with all detected tags
pub fn parse(self: *const Struct) Parsed {
var parsed = Parsed{
.bootloader_brand = std.mem.sliceTo(&self.bootloader_brand, 0),
.bootloader_version = std.mem.sliceTo(&self.bootloader_version, 0),
};
var tag_opt = self.tags;
while (tag_opt) |tag| : (tag_opt = tag.next) {
switch (tag.identifier) {
.pmrs => parsed.pmrs = @ptrCast(*const PmrsTag, tag),
.cmdline => parsed.cmdline = @ptrCast(*const CmdlineTag, tag),
.memmap => parsed.memmap = @ptrCast(*const MemmapTag, tag),
.framebuffer => parsed.framebuffer = @ptrCast(*const FramebufferTag, tag),
.framebuffer_mtrr => parsed.framebuffer_mtrr = @ptrCast(*const FramebufferMtrrTag, tag),
.textmode => parsed.textmode = @ptrCast(*const TextModeTag, tag),
.edid => parsed.edid = @ptrCast(*const EdidTag, tag),
.terminal => parsed.terminal = @ptrCast(*const TerminalTag, tag),
.modules => parsed.modules = @ptrCast(*const ModulesTag, tag),
.rsdp => parsed.rsdp = @ptrCast(*const RsdpTag, tag),
.smbios => parsed.smbios = @ptrCast(*const SmbiosTag, tag),
.epoch => parsed.epoch = @ptrCast(*const EpochTag, tag),
.firmware => parsed.firmware = @ptrCast(*const FirmwareTag, tag),
.efi_system_table => parsed.efi_system_table = @ptrCast(*const EfiSystemTableTag, tag),
.kernel_file => parsed.kernel_file = @ptrCast(*const KernelFileTag, tag),
.kernel_file_v2 => parsed.kernel_file_v2 = @ptrCast(*const KernelFileV2Tag, tag),
.kernel_slide => parsed.kernel_slide = @ptrCast(*const KernelSlideTag, tag),
.smp => parsed.smp = @ptrCast(*const SmpTag, tag),
.pxe_server_info => parsed.pxe_server_info = @ptrCast(*const PxeServerInfoTag, tag),
.mmio32_uart => parsed.mmio32_uart = @ptrCast(*const Mmio32UartTag, tag),
.dtb => parsed.dtb = @ptrCast(*const DtbTag, tag),
.vmap => parsed.vmap = @ptrCast(*const VmapTag, tag),
_ => {}, // Ignore unknown tags
}
}
return parsed;
}
/// This tag tells the kernel that th4e PMR flag in the header was recognised and that the kernel has been
/// successfully mapped by its ELF segments. It also provides the array of ranges and their corresponding
/// permissions.
pub const PmrsTag = packed struct {
tag: Tag = .{ .identifier = .pmrs },
/// Number of entries in array
entries: u64,
/// Returns array of Pmr structs
pub fn getPmrs(self: *const PmrsTag) []Pmr {
return @intToPtr([*]Pmr, @ptrToInt(&self.entries) + 8)[0..self.entries];
}
};
pub const Pmr = packed struct {
base: u64,
length: u64,
permissions: Permissions,
pub const Permissions = packed struct {
executable: u1,
writable: u1,
readable: u1,
unused: u61,
};
};
/// This tag provides the kernel with the command line string.
pub const CmdlineTag = packed struct {
tag: Tag = .{ .identifier = .cmdline },
/// Null-terminated array
cmdline: [*:0]const u8,
};
/// This tag provides the kernel with the memory map.
pub const MemmapTag = packed struct {
tag: Tag = .{ .identifier = .memmap },
/// Number of entries in array
entries: u64,
/// Returns array of `MemmapEntry` structs
pub fn getMemmap(self: *const MemmapTag) []MemmapEntry {
return @intToPtr([*]MemmapEntry, @ptrToInt(&self.entries) + 8)[0..self.entries];
}
};
pub const MemmapEntry = packed struct {
/// Physical address of the base of the memory section
base: u64,
/// Length of the memory section
length: u64,
type: Type,
unused: u32,
pub const Type = enum(u32) {
usable = 1,
reserved = 2,
acpi_reclaimable = 3,
acpi_nvs = 4,
bad_memory = 5,
bootloader_reclaimable = 0x1000,
kernel_and_modules = 0x1001,
framebuffer = 0x1002,
};
};
/// This tag provides the kernel with details of the currently set-up framebuffer, if any
pub const FramebufferTag = packed struct {
tag: Tag = .{ .identifier = .framebuffer },
/// The address of the framebuffer
address: u64,
/// Width and height of the framebuffer in pixels
width: u16,
height: u16,
/// Pitch in bytes
pitch: u16,
/// Bits per pixel
bpp: u16,
memory_model: MemoryModel,
red_mask_size: u8,
red_mask_shift: u8,
green_mask_size: u8,
green_mask_shift: u8,
blue_mask_size: u8,
blue_mask_shift: u8,
unused: u8,
pub const MemoryModel = enum(u8) {
rgb = 1,
_,
};
};
/// **WARNING:** This tag is deprecated. Use is discouraged and may not be supported on newer bootloaders!
/// This tag signals to the kernel that MTRR write-combining for the framebuffer was enabled.
pub const FramebufferMtrrTag = packed struct {
tag: Tag = .{ .identifier = .framebuffer_mtrr },
};
/// This tag provides the kernel with details of the currently set up CGA text mode, if any.
pub const TextModeTag = packed struct {
tag: Tag = .{ .identifier = .textmode },
/// The address of the text mode buffer
address: u64,
unused: u16,
rows: u16,
columns: u16,
bytes_per_char: u16,
};
/// This tag provides the kernel with EDID information.
pub const EdidTag = packed struct {
tag: Tag = .{ .identifier = .edid },
/// The number of bytes in the array
edid_size: u64,
/// Returns edid information
pub fn getEdidInformation(self: *const EdidTag) []const u8 {
return @intToPtr([*]u8, @ptrToInt(&self.edid_size) + 8)[0..self.edid_size];
}
};
/// This tag provides the kernel with the entry point of the `stivale2_term_write()` function, if it was
/// requested, and supported by the bootloader.
pub const TerminalTag = packed struct {
tag: Tag = .{ .identifier = .terminal },
flags: Flags,
cols: u16,
rows: u16,
/// Pointer to the entry point of the `stivale2_term_write()` function.
term_write: u64,
/// If `Flags.max_length` is set, this field specifies the maximum allowed string length to be passed
/// to `term_write()`. If this is 0, then there is limit.
max_length: u64,
pub const Flags = packed struct {
/// If set, cols and rows are provided
cols_and_rows: u1,
/// If not set, assume a max_length of 1024
max_length: u1,
/// If the callback was requested and supported by the bootloader, this is set
callback: u1,
/// If set, context control is available
context_control: u1,
unused: u28,
};
};
/// This tag provides the kernel with a list of modules loaded alongside the kernel.
pub const ModulesTag = packed struct {
tag: Tag = .{ .identifier = .modules },
/// Number of modules in the array
module_count: u64,
/// Returns array of `Module` structs
pub fn getModules(self: *const ModulesTag) []Module {
return @intToPtr([*]Module, @ptrToInt(&self.module_count) + 8)[0..self.module_count];
}
};
pub const Module = packed struct {
/// Address where the module is loaded
begin: u64,
/// End address of the module
end: u64,
/// ASCII null-terminated string passed to the module
string: [128]u8,
};
/// This tag provides the kernel with the location of the ACPI RSDP structure
pub const RsdpTag = packed struct {
tag: Tag = .{ .identifier = .rsdp },
/// Address of the ACPI RSDP structure
rsdp: u64,
};
/// This tag provides the kernel with the location of SMBIOS entry points in memory
pub const SmbiosTag = packed struct {
tag: Tag = .{ .identifier = .smbios },
/// Flags are for future use and currently all unused
flags: Flags,
/// 32-bit SMBIOS entry point address, 0 if unavailable
smbios_entry_32: u64,
/// 64-bit SMBIOS entry point address, 0 if unavailable
smbios_entry_64: u64,
pub const Flags = packed struct {
unused: u64,
};
};
/// This tag provides the kernel with the current UNIX epoch
pub const EpochTag = packed struct {
tag: Tag = .{ .identifier = .epoch },
/// UNIX epoch at boot, read from RTC
epoch: u64,
};
/// This tag provides the kernel with info about the firmware
pub const FirmwareTag = packed struct {
tag: Tag = .{ .identifier = .firmware },
flags: Flags,
pub const Flags = packed struct {
/// If set, BIOS, if unset, UEFI
bios: u1,
unused: u63,
};
};
/// This tag provides the kernel with a pointer to the EFI system table if available
pub const EfiSystemTableTag = packed struct {
tag: Tag = .{ .identifier = .efi_system_table },
/// Address of the EFI system table
system_table: u64,
};
/// This tag provides the kernel with a pointer to a copy of the executable file of the kernel
pub const KernelFileTag = packed struct {
tag: Tag = .{ .identifier = .kernel_file },
/// Address of the kernel file
kernel_file: u64,
};
/// This tag provides the kernel with a pointer to a copy of the executable file of the kernel, along with
/// the size of the file
pub const KernelFileV2Tag = packed struct {
tag: Tag = .{ .identifier = .kernel_file_v2 },
/// Address of the kernel file
kernel_file: u64,
/// Size of the kernel file
kernel_size: u64,
};
/// This tag provides the kernel with the slide that the bootloader has applied to the kernel's address
pub const KernelSlideTag = packed struct {
tag: Tag = .{ .identifier = .kernel_slide },
kernel_slide: u64,
};
/// This tag provides the kernel with info about a multiprocessor environment
pub const SmpTag = packed struct {
tag: Tag = .{ .identifier = .smp },
flags: Flags,
/// LAPIC ID of the BSP
bsp_lapic_id: u32,
unused: u32,
/// Total number of logical CPUs (incl BSP)
cpu_count: u64,
/// Returns array of `SmpInfo` structs
pub fn getSmpInfo(self: *const SmpTag) []SmpInfo {
return @intToPtr([*]SmpInfo, @ptrToInt(&self.cpu_count) + 8)[0..self.cpu_count];
}
pub const Flags = packed struct {
/// Set if x2APIC was requested, supported, and sucessfully enabled
x2apic: u1,
unused: u63,
};
};
pub const SmpInfo = packed struct {
/// ACPI processor UID as specified by MADT
acpi_processor_uid: u32,
/// LAPIC ID as specified by MADT
lapic_id: u32,
/// The stack that will be loaded in ESP/RSP once the goto_address field is loaded. This **MUST** point
/// to a valid stack of at least 256 bytes in size, and 16-byte aligned. `target_stack` is unused for
/// the struct describing the BSP.
target_stack: u64,
/// This field is polled by the started APs until the kernel on another CPU performs a write to this field.
/// When that happens, bootloader code will load up ESP/RSP with the stack value specified in
/// `target_stack`. It will then proceed to load a pointer to this structure in either RDI for 64-bit, or
/// onto the stack for 32-bit. Then, `goto_address` is called, and execution is handed off.
goto_address: u64,
/// This field is here for the kernel to use for whatever it wants. Writes here should be performed before
/// writing to `goto_address`.
extra_argument: u64,
};
/// This tag provides the kernel with the server ip that it was booted from, if the kernel has been booted
/// via PXE
pub const PxeServerInfoTag = packed struct {
tag: Tag = .{ .identifier = .pxe_server_info },
/// Server IP in network byte order
server_ip: u32,
};
/// This tag provides the kernel with the address of a memory mapped UART port
pub const Mmio32UartTag = packed struct {
tag: Tag = .{ .identifier = .mmio32_uart },
/// The address of the UART port
addr: u64,
};
/// This tag describes a device tree blob
pub const DtbTag = packed struct {
tag: Tag = .{ .identifier = .dtb },
/// The address of the DTB
addr: u64,
/// The size of the DTB
size: u64,
};
/// This tag describes the high physical memory location (`VMAP_HIGH`)
pub const VmapTag = packed struct {
tag: Tag = .{ .identifier = .vmap },
/// `VMAP_HIGH`, where the physical memory is mapped in the higher half
addr: u64,
};
};
test "Struct Size" {
try expect(@bitSizeOf(Struct) == 64 * 8 * 2 + 64);
}
test "Struct Tag Sizes" {
try expect(@bitSizeOf(Struct.PmrsTag) == 192);
try expect(@bitSizeOf(Struct.CmdlineTag) == 192);
try expect(@bitSizeOf(Struct.MemmapTag) == 192);
try expect(@bitSizeOf(Struct.FramebufferTag) == 320);
try expect(@bitSizeOf(Struct.FramebufferMtrrTag) == 128);
try expect(@bitSizeOf(Struct.TextModeTag) == 256);
try expect(@bitSizeOf(Struct.EdidTag) == 192);
try expect(@bitSizeOf(Struct.TerminalTag) == 320);
try expect(@bitSizeOf(Struct.ModulesTag) == 192);
try expect(@bitSizeOf(Struct.RsdpTag) == 192);
try expect(@bitSizeOf(Struct.SmbiosTag) == 320);
try expect(@bitSizeOf(Struct.EpochTag) == 192);
try expect(@bitSizeOf(Struct.FirmwareTag) == 192);
try expect(@bitSizeOf(Struct.EfiSystemTableTag) == 192);
try expect(@bitSizeOf(Struct.KernelFileTag) == 192);
try expect(@bitSizeOf(Struct.KernelFileV2Tag) == 256);
try expect(@bitSizeOf(Struct.KernelSlideTag) == 192);
try expect(@bitSizeOf(Struct.SmpTag) == 320);
try expect(@bitSizeOf(Struct.PxeServerInfoTag) == 160);
try expect(@bitSizeOf(Struct.Mmio32UartTag) == 192);
try expect(@bitSizeOf(Struct.DtbTag) == 256);
try expect(@bitSizeOf(Struct.VmapTag) == 192);
}
test "Struct Other Sizes" {
try expect(@bitSizeOf(Struct.Pmr) == 192);
try expect(@bitSizeOf(Struct.MemmapEntry) == 192);
try expect(@bitSizeOf(Struct.Module) == 1152);
try expect(@bitSizeOf(Struct.SmpInfo) == 256);
}
// We use this only as a helper for our test
fn padString(comptime string: anytype, comptime len: usize) [len]u8 {
comptime {
if (string.len >= len) unreachable;
const paddingLen = len - string.len;
const padding = [1]u8{0} ** paddingLen;
return string ++ padding;
}
}
test "Parse Struct" {
var info = Struct{
.bootloader_brand = padString("Foo...".*, 64),
.bootloader_version = padString("Bar!".*, 64),
};
var epochtag = Struct.EpochTag{ .epoch = 0x6969696969696969 };
info.tags = &epochtag.tag;
const parsed = info.parse();
try expect(parsed.epoch.?.*.epoch == 0x6969696969696969);
try expect(std.mem.eql(u8, parsed.bootloader_brand, "Foo..."));
try expect(std.mem.eql(u8, parsed.bootloader_version, "Bar!"));
}
/// This function takes your kernel entry point as an argument. It parses the `Struct` for you, and provides `Struct.Parsed` as an argument to the
/// entry point. You may export it as `_start` using `@export`.
pub fn entryPoint(comptime entrypoint: fn (*const Struct.Parsed) noreturn) fn (*const Struct) callconv(.C) noreturn {
return struct {
pub fn entryPoint(info: *const Struct) callconv(.C) noreturn {
const parsed = info.parse();
entrypoint(&parsed);
}
}.entryPoint;
}
test "entryPoint" {
const kmain = struct {
pub fn kmain(_: *const Struct.Parsed) noreturn {
while (true) {}
}
}.kmain;
_ = entryPoint(kmain);
}