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
|
// Multiboot [1]
//
// * OS image requiremets
// - 32 bit executable file
// - multiboot header in the first 8192 bytes, and 4 byte aligned
//
// * Machine state
// - eax: contains the magic value 0x2BADB002
// - ebx: 32 bit phys addr of the multiboot info structure
// - cs: 32 bit rx segment [0..0xffff_ffff]
// - ds/es/fs/gs/ss: 32 bit rw segment [0..0xffff_ffff]
// - a20 gate enabled [3]
// - cr0: paging (PG) disabled, protection (PE) enabled
// - esp: os must setup its own stack when it needs one
// - gdtr/idtr: os image must setup own tables
//
// [1] https://www.gnu.org/software/grub/manual/multiboot/multiboot.html
// [2] https://www.gnu.org/software/grub/manual/multiboot/multiboot.pdf
// [3] https://wiki.osdev.org/A20_Line
// -- MULTIBOOT HEADER ----------------------------------------------------------
const MBH_MEMALIGN = (1 << 0);
const MBH_MEMINFO = (1 << 1);
const MAGIC = 0x1BADB002;
const FLAGS = MBH_MEMINFO;
const CHECKSUM: i32 = -MAGIC - FLAGS;
const MultibootHeader = extern struct {
magic: u32,
flags: u32,
checksum: i32,
};
pub const HEADER = MultibootHeader{
.magic = MAGIC,
.flags = FLAGS,
.checksum = CHECKSUM,
};
// -- MULTIBOOT INFO ------------------------------------------------------------
const MultibootInfoFlag = enum(u32) {
MEMORY = (1 << 0),
BOOTDEV = (1 << 1),
CMDLINE = (1 << 2),
MODS = (1 << 3),
SYMAOUT = (1 << 4),
SYMELF = (1 << 5),
MEMMAP = (1 << 6),
DRIVE = (1 << 7),
CONFIG = (1 << 8),
BOOTLOADER = (1 << 9),
APM = (1 << 10),
VBE = (1 << 11),
FRAMEBUFFER = (1 << 12),
};
pub const MultibootInfo = extern struct {
flags: u32,
// Valid iff MEMORY flag set.
mem_lower: u32,
mem_upper: u32,
// Valid iff BOOTDEV flag set.
boot_device: u32,
// Valid iff CMDLINE flag set.
cmdline: u32,
// Valid iff MODS flag set.
mods_count: u32,
mods_addr: u32,
// Valid iff SYMAOUT or SYMELF flag set.
_sym_stub: [4]u32,
// Valid iff MEMMAP flag set.
mmap_length: u32,
mmap_addr: u32,
// Valid iff DRIVE flag set.
drives_length: u32,
drives_addr: u32,
// Valid iff CONFIG flag set.
config_table: u32,
// Valid iff BOOTLOADER flag set.
boot_loader_name: u32,
// Valid iff APM flag set.
apm_table: u32,
// Valid iff VBE flag set.
vbe_control_info: u32,
vbe_mode_info: u32,
vbe_mode: u16,
vbe_interface_seg: u16,
vbe_interface_off: u16,
vbe_interface_len: u16,
// Valid iff FB flag set.
framebuffer_addr: u64,
framebuffer_pitch: u32,
framebuffer_width: u32,
framebuffer_height: u32,
framebuffer_bpp: u8,
framebuffer_type: u8,
_framebuffer_palette_stub: [6]u8,
/// Check if given multiboot info flag is set.
fn has(self: @This(), flag: MultibootInfoFlag) bool {
return (self.flags & @intFromEnum(flag)) != 0;
}
/// If the cmdline is available return a null-terminated c string.
fn cmdlineStr(self: @This()) ?[*:0]const u8 {
return if (self.has(.CMDLINE)) cstr(self.cmdline) else null;
}
/// If the bootloader name is available return a null-terminated c string.
fn bootloaderStr(self: @This()) ?[*:0]const u8 {
return if (self.has(.BOOTLOADER)) cstr(self.boot_loader_name) else null;
}
/// If the mmap info is available, return an iterator over the mmap entries.
fn mmapIter(self: @This()) ?MultibootMmapEntry.Iter {
return if (self.has(.MEMMAP)) .{ .addr = self.mmap_addr, .len = self.mmap_length, .off = 0 } else null;
}
};
// This structure is defined as packed structure in the mb spec.
pub const MultibootMmapEntry = extern struct {
size: u32 align(1),
addr: u64 align(1),
len: u64 align(1),
type: u32 align(1),
/// Get the `actual` size of an mmap entry.
fn getSize(self: @This()) usize {
// The `size` field expresses the actual size of the mmap structure
// (w/o the size field).
// The actual size can be larger as the struct definition.
return self.size + @sizeOf(@TypeOf(self.size));
}
/// Get the mmap type as string.
fn typeStr(self: @This()) []const u8 {
return switch (self.type) {
1 => "available",
2 => "reserved",
3 => "acpi-reclaim",
4 => "nvs",
5 => "badram",
else => "<unknown>",
};
}
/// Iterator type over mmap entries.
const Iter = struct {
addr: u32,
len: u32,
off: u32,
/// Return the next mmap entry if the iterator is not exhausted.
fn next(self: *@This()) ?*const MultibootMmapEntry {
return if ((self.off + @sizeOf(MultibootMmapEntry)) > self.len)
null
else blk: {
const m: *const MultibootMmapEntry = @ptrFromInt(self.addr + self.off);
self.off += m.getSize();
break :blk m;
};
}
};
};
// -- MULTIBOOT UTILS -----------------------------------------------------------
const format = @import("std").fmt.format;
pub fn bootinfo(ebx: u32) *const MultibootInfo {
return @ptrFromInt(ebx);
}
pub fn formatBootinfo(w: anytype, mbi: *const MultibootInfo) !void {
try w.writeAll("multiboot info\n");
try format(w, "flags: 0b{b}\n", .{mbi.flags});
if (mbi.bootloaderStr()) |bootloader| {
try format(w, "bootloader: {s}\n", .{bootloader});
}
if (mbi.cmdlineStr()) |cmdline| {
try format(w, "cmdline: {s}\n", .{cmdline});
}
if (mbi.has(.MEMORY)) {
try format(w, "mem_lower: {x:08} - {x:08}\n", .{ 0, mbi.mem_lower * 1024 });
try format(w, "mem_upper: {x:08} - {x:08}\n", .{ 1024 * 1024, mbi.mem_upper * 1024 });
}
if (mbi.mmapIter()) |iter| {
// Rebind iter, cant bind mutable reference to temporary object.
var it = iter;
try format(w, "mmap: @ {x:08} - {x:08}\n", .{ it.addr, it.addr + it.len - 1 });
while (it.next()) |map| {
try format(w, "region: {x:08} - {x:08} | {s}\n", .{ map.addr, map.addr + map.len - 1, map.typeStr() });
}
}
}
fn cstr(addr: u32) [*:0]const u8 {
return @ptrFromInt(addr);
}
|