@@ -7,6 +7,8 @@ const dim = @import("../../dim.zig");
7
7
8
8
const PartTable = @This ();
9
9
10
+ const block_size = 512 ;
11
+
10
12
bootloader : ? dim.Content ,
11
13
disk_id : ? u32 ,
12
14
partitions : [4 ]? Partition ,
@@ -41,7 +43,7 @@ pub fn parse(ctx: dim.Context) !dim.Content {
41
43
pf .bootloader = bootloader_content ;
42
44
},
43
45
.ignore = > {
44
- pf .partitions [next_part_id ] = .unused ;
46
+ pf .partitions [next_part_id ] = null ;
45
47
next_part_id += 1 ;
46
48
},
47
49
.part = > {
@@ -58,6 +60,22 @@ pub fn parse(ctx: dim.Context) !dim.Content {
58
60
try ctx .report_nonfatal_error ("MBR partition {} does not have a size, but is not last." , .{prev });
59
61
}
60
62
}
63
+
64
+ var all_auto = true ;
65
+ var all_manual = true ;
66
+ for (pf .partitions ) | part_or_null | {
67
+ const part = part_or_null orelse continue ;
68
+
69
+ if (part .offset != null ) {
70
+ all_auto = false ;
71
+ } else {
72
+ all_manual = false ;
73
+ }
74
+ }
75
+
76
+ if (! all_auto and ! all_manual ) {
77
+ try ctx .report_nonfatal_error ("not all partitions have an explicit offset!" , .{});
78
+ }
61
79
}
62
80
63
81
return .create_handle (pf , .create (PartTable , .{
@@ -104,120 +122,121 @@ fn parse_partition(ctx: dim.Context) !Partition {
104
122
return part ;
105
123
}
106
124
107
- fn render (self : * PartTable , stream : * dim.BinaryStream ) dim.Content.RenderError ! void {
108
- _ = self ;
109
- _ = stream ;
110
- }
111
-
112
- // .mbr => |table| { // MbrTable
113
- // {
114
- // var boot_sector: [512]u8 = .{0} ** 512;
115
-
116
- // @memcpy(boot_sector[0..table.bootloader.len], &table.bootloader);
117
-
118
- // std.mem.writeInt(u32, boot_sector[0x1B8..0x1BC], if (table.disk_id) |disk_id| disk_id else 0x0000_0000, .little);
119
- // std.mem.writeInt(u16, boot_sector[0x1BC..0x1BE], 0x0000, .little);
120
-
121
- // var all_auto = true;
122
- // var all_manual = true;
123
- // for (table.partitions) |part_or_null| {
124
- // const part = part_or_null orelse continue;
125
-
126
- // if (part.offset != null) {
127
- // all_auto = false;
128
- // } else {
129
- // all_manual = false;
130
- // }
131
- // }
132
-
133
- // if (!all_auto and !all_manual) {
134
- // std.log.err("{s}: not all partitions have an explicit offset!", .{context.slice()});
135
- // return error.InvalidSectorBoundary;
136
- // }
137
-
138
- // const part_base = 0x01BE;
139
- // var auto_offset: u64 = 2048;
140
- // for (table.partitions, 0..) |part_or_null, part_id| {
141
- // const reset_len = context.len;
142
- // defer context.len = reset_len;
143
-
144
- // var buffer: [64]u8 = undefined;
145
- // context.appendSliceAssumeCapacity(std.fmt.bufPrint(&buffer, "[{}]", .{part_id}) catch unreachable);
146
-
147
- // const desc = boot_sector[part_base + 16 * part_id ..][0..16];
148
-
149
- // if (part_or_null) |part| {
150
- // // https://wiki.osdev.org/MBR#Partition_table_entry_format
151
-
152
- // const part_offset = part.offset orelse auto_offset;
153
-
154
- // if ((part_offset % 512) != 0) {
155
- // std.log.err("{s}: .offset is not divisible by 512!", .{context.slice()});
156
- // return error.InvalidSectorBoundary;
157
- // }
158
- // if ((part.size % 512) != 0) {
159
- // std.log.err("{s}: .size is not divisible by 512!", .{context.slice()});
160
- // return error.InvalidSectorBoundary;
161
- // }
162
-
163
- // const lba_u64 = @divExact(part_offset, 512);
164
- // const size_u64 = @divExact(part.size, 512);
165
-
166
- // const lba = std.math.cast(u32, lba_u64) orelse {
167
- // std.log.err("{s}: .offset is out of bounds!", .{context.slice()});
168
- // return error.InvalidSectorBoundary;
169
- // };
170
- // const size = std.math.cast(u32, size_u64) orelse {
171
- // std.log.err("{s}: .size is out of bounds!", .{context.slice()});
172
- // return error.InvalidSectorBoundary;
173
- // };
174
-
175
- // desc[0] = if (part.bootable) 0x80 else 0x00;
176
-
177
- // desc[1..4].* = mbr.encodeMbrChsEntry(lba); // chs_start
178
- // desc[4] = @intFromEnum(part.type);
179
- // desc[5..8].* = mbr.encodeMbrChsEntry(lba + size - 1); // chs_end
180
- // std.mem.writeInt(u32, desc[8..12], lba, .little); // lba_start
181
- // std.mem.writeInt(u32, desc[12..16], size, .little); // block_count
182
-
183
- // auto_offset += part.size;
184
- // } else {
185
- // @memset(desc, 0); // inactive
186
- // }
187
- // }
188
- // boot_sector[0x01FE] = 0x55;
189
- // boot_sector[0x01FF] = 0xAA;
190
-
191
- // try disk.handle.writeAll(&boot_sector);
192
- // }
193
-
194
- // {
195
- // var auto_offset: u64 = 2048;
196
- // for (table.partitions, 0..) |part_or_null, part_id| {
197
- // const part = part_or_null orelse continue;
198
-
199
- // const reset_len = context.len;
200
- // defer context.len = reset_len;
201
-
202
- // var buffer: [64]u8 = undefined;
203
- // context.appendSliceAssumeCapacity(std.fmt.bufPrint(&buffer, "[{}]", .{part_id}) catch unreachable);
204
-
205
- // try writeDiskImage(b, asking, disk, base + auto_offset, part.size, part.data, context);
206
-
207
- // auto_offset += part.size;
208
- // }
209
- // }
210
- // },
125
+ fn render (table : * PartTable , stream : * dim.BinaryStream ) dim.Content.RenderError ! void {
126
+ const last_part_id = blk : {
127
+ var last : usize = 0 ;
128
+ for (table .partitions , 0.. ) | p , i | {
129
+ if (p != null )
130
+ last = i ;
131
+ }
132
+ break :blk last ;
133
+ };
211
134
212
- pub const Partition = struct {
213
- pub const unused : Partition = .{
214
- .offset = null ,
215
- .size = 0 ,
216
- .bootable = false ,
217
- .type = .empty ,
218
- .contains = .empty ,
135
+ const PartInfo = struct {
136
+ offset : u64 ,
137
+ size : u64 ,
219
138
};
139
+ var part_infos : [4 ]? PartInfo = @splat (null );
140
+
141
+ // Compute and write boot sector, based on the follow:
142
+ // - https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout
143
+ {
144
+ var boot_sector : [block_size ]u8 = @splat (0 );
145
+
146
+ if (table .bootloader ) | bootloader | {
147
+ var sector : dim.BinaryStream = .init_buffer (& boot_sector );
148
+
149
+ try bootloader .render (& sector );
150
+
151
+ const upper_limit : u64 = if (table .disk_id != null )
152
+ 0x01B8
153
+ else
154
+ 0x1BE ;
155
+
156
+ if (sector .virtual_offset >= upper_limit ) {
157
+ // TODO(fqu): Emit warning diagnostics here that parts of the bootloader will be overwritten by the MBR data.
158
+ }
159
+ }
160
+
161
+ if (table .disk_id ) | disk_id | {
162
+ std .mem .writeInt (u32 , boot_sector [0x1B8.. 0x1BC], disk_id , .little );
163
+ }
220
164
165
+ // TODO(fqu): Implement "0x5A5A if copy-protected"
166
+ std .mem .writeInt (u16 , boot_sector [0x1BC.. 0x1BE], 0x0000 , .little );
167
+
168
+ const part_base = 0x01BE ;
169
+ var auto_offset : u64 = 2048 * block_size ; // TODO(fqu): Make this configurable by allowing `offset` on the first partition, but still allow auto-layouting
170
+ for (table .partitions , & part_infos , 0.. ) | part_or_null , * pinfo , part_id | {
171
+ const desc : * [16 ]u8 = boot_sector [part_base + 16 * part_id .. ][0.. 16];
172
+
173
+ // Initialize to "inactive" state
174
+ desc .* = @splat (0 );
175
+ pinfo .* = null ;
176
+
177
+ if (part_or_null ) | part | {
178
+ // https://wiki.osdev.org/MBR#Partition_table_entry_format
179
+
180
+ const part_offset = part .offset orelse auto_offset ;
181
+ const part_size = part .size orelse if (part_id == last_part_id )
182
+ std .mem .alignBackward (u64 , stream .length - part_offset , block_size )
183
+ else
184
+ return error .ConfigurationError ;
185
+
186
+ pinfo .* = .{
187
+ .offset = part_offset ,
188
+ .size = part_size ,
189
+ };
190
+
191
+ if ((part_offset % block_size ) != 0 ) {
192
+ std .log .err ("partition offset is not divisible by {}!" , .{block_size });
193
+ return error .ConfigurationError ;
194
+ }
195
+ if ((part_size % block_size ) != 0 ) {
196
+ std .log .err ("partition size is not divisible by {}!" , .{block_size });
197
+ return error .ConfigurationError ;
198
+ }
199
+
200
+ const lba_u64 = @divExact (part_offset , block_size );
201
+ const size_u64 = @divExact (part_size , block_size );
202
+
203
+ const lba = std .math .cast (u32 , lba_u64 ) orelse {
204
+ std .log .err ("partition offset is out of bounds!" , .{});
205
+ return error .ConfigurationError ;
206
+ };
207
+ const size = std .math .cast (u32 , size_u64 ) orelse {
208
+ std .log .err ("partition size is out of bounds!" , .{});
209
+ return error .ConfigurationError ;
210
+ };
211
+
212
+ desc [0 ] = if (part .bootable ) 0x80 else 0x00 ;
213
+
214
+ desc [1.. 4].* = encodeMbrChsEntry (lba ); // chs_start
215
+ desc [4 ] = @intFromEnum (part .type );
216
+ desc [5.. 8].* = encodeMbrChsEntry (lba + size - 1 ); // chs_end
217
+ std .mem .writeInt (u32 , desc [8.. 12], lba , .little ); // lba_start
218
+ std .mem .writeInt (u32 , desc [12.. 16], size , .little ); // block_count
219
+
220
+ auto_offset += part_size ;
221
+ }
222
+ }
223
+ boot_sector [0x01FE ] = 0x55 ;
224
+ boot_sector [0x01FF ] = 0xAA ;
225
+
226
+ try stream .write (0 , & boot_sector );
227
+ }
228
+
229
+ for (part_infos , table .partitions ) | maybe_info , maybe_part | {
230
+ const part = maybe_part orelse continue ;
231
+ const info = maybe_info orelse unreachable ;
232
+
233
+ var sub_view = try stream .slice (info .offset , info .size );
234
+
235
+ try part .contains .render (& sub_view );
236
+ }
237
+ }
238
+
239
+ pub const Partition = struct {
221
240
offset : ? u64 = null ,
222
241
size : ? u64 ,
223
242
0 commit comments