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
#define _LARGEFILE_SOURCE 1
#define _FILE_OFFSET_BITS 64
#include <sys/types.h>
#include <inttypes.h>
#include <fcntl.h>
#include "elf.h"
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#if defined(_MSDOS) || defined(_WIN32)
# include <fcntl.h>
# include <io.h>
#endif
/* This program makes a boot image.
* It takes at least one argument, the boot sector file.
* Any succeeding arguments are written verbatim to the output file.
*
* Before jumping to the boot sector, the BIOS checks that the last
* two bytes in the sector equal 0x55 and 0xAA.
* This code makes sure the code intended for the boot sector is at most
* 512 - 2 = 510 bytes long, then appends the 0x55-0xAA signature.
*/
int diskfd;
off_t maxoff = 0;
off_t curoff = 0;
int find_partition(off_t partition_sect, off_t extended_sect, int partoff);
void do_multiboot(const char *filename);
void usage(void) {
fprintf(stderr, "Usage: mkbootdisk BOOTSECTORFILE [FILE | @SECNUM]...\n");
fprintf(stderr, " or: mkbootdisk -p DISK [FILE | @SECNUM]...\n");
fprintf(stderr, " or: mkbootdisk -m KERNELFILE\n");
exit(1);
}
FILE *fopencheck(const char *name) {
FILE *f = fopen(name, "rb");
if (!f) {
fprintf(stderr, "%s: %s\n", name, strerror(errno));
usage();
}
return f;
}
void diskwrite(const void *data, size_t amt) {
if (maxoff && curoff + amt > size_t(maxoff)) {
fprintf(stderr, "more data than allowed in partition!\n");
usage();
}
while (amt > 0) {
ssize_t w = write(diskfd, data, amt);
if (w == -1 && errno != EINTR) {
perror("write");
usage();
} else if (w == 0) {
fprintf(stderr, "write hit end of file\n");
usage();
} else if (w > 0) {
amt -= w;
curoff += w;
data = (const unsigned char *) data + w;
}
}
}
int main(int argc, char *argv[]) {
char buf[4096];
char zerobuf[512];
FILE *f;
size_t n;
size_t nsectors;
int i;
int bootsector_special = 1;
#if defined(_MSDOS) || defined(_WIN32)
// As our output file is binary, we must set its file mode to binary.
diskfd = _fileno(stdout);
_setmode(diskfd, _O_BINARY);
#else
diskfd = fileno(stdout);
#endif
// Check for a partition
if (argc >= 2 && strcmp(argv[1], "-p") == 0) {
if (argc < 3) {
usage();
}
if ((diskfd = open(argv[2], O_RDWR)) < 0) {
fprintf(stderr, "%s: %s\n", argv[2], strerror(errno));
usage();
}
if (find_partition(0, 0, 0) <= 0) {
fprintf(stderr, "%s: no JOS partition (type 0x27) found!\n", argv[2]);
usage();
}
argc -= 2;
argv += 2;
bootsector_special = 0;
}
// Check for multiboot option
if (argc >= 2 && strcmp(argv[1], "-m") == 0) {
if (argc < 3) {
usage();
}
do_multiboot(argv[2]);
}
// Read files
if (argc < 2) {
usage();
}
// Read boot sector
if (bootsector_special) {
f = fopencheck(argv[1]);
n = fread(buf, 1, 4096, f);
if (n > 510) {
fprintf(stderr, "%s: boot block too large: %s%u bytes (max 510)\n", argv[1], (n == 4096 ? ">= " : ""), (unsigned) n);
usage();
}
fclose(f);
// Append signature and write modified boot sector
memset(buf + n, 0, 510 - n);
buf[510] = 0x55;
buf[511] = 0xAA;
diskwrite(buf, 512);
nsectors = 1;
argc--;
argv++;
} else {
nsectors = 0;
}
// Read any succeeding files, then write them out
memset(zerobuf, 0, 512);
for (i = 1; i < argc; i++) {
size_t pos;
char *str;
unsigned long skipto_sector;
// An argument like "@X" means "skip to sector X".
if (argv[i][0] == '@' && isdigit(argv[i][1])
&& ((skipto_sector = strtoul(argv[i] + 1, &str, 0)), *str == 0)) {
if (nsectors > skipto_sector) {
fprintf(stderr, "mkbootdisk: can't skip to sector %u, already at sector %u\n", (unsigned) skipto_sector, (unsigned) nsectors);
usage();
}
while (nsectors < skipto_sector) {
diskwrite(zerobuf, 512);
nsectors++;
}
continue;
}
// Otherwise, read the file.
f = fopencheck(argv[i]);
pos = 0;
while ((n = fread(buf, 1, 4096, f)) > 0) {
diskwrite(buf, n);
pos += n;
}
if (pos % 512 != 0) {
diskwrite(zerobuf, 512 - (pos % 512));
pos += 512 - (pos % 512);
}
nsectors += pos / 512;
fclose(f);
}
// Fill out to 1024 sectors with 0 blocks
while (nsectors < 1024) {
diskwrite(zerobuf, 512);
nsectors++;
}
return 0;
}
#define SECTORSIZE 512
static void readsect(void *buf, uint32_t sectno) {
ssize_t s;
off_t o = lseek(diskfd, (off_t) sectno * (off_t) SECTORSIZE, SEEK_SET);
if (o == (off_t) -1) {
perror("lseek");
usage();
}
do {
s = read(diskfd, buf, SECTORSIZE);
} while (s == -1 && errno == EINTR);
if (s != SECTORSIZE) {
perror("read");
usage();
}
}
// Partitions
// Offset of 1st partition descriptor in a partition sector
#define PTABLE_OFFSET 446
// 2-byte partition table magic number location and value
#define PTABLE_MAGIC_OFFSET 510
#define PTABLE_MAGIC1 0x55
#define PTABLE_MAGIC2 0xAA
// Partition type constants
#define PTYPE_JOS_KERN 0x27 // JOS kernel
#define PTYPE_JOSFS 0x28 // JOS file system
// Extended partition identifiers
#define PTYPE_DOS_EXTENDED 0x05
#define PTYPE_W95_EXTENDED 0x0F
#define PTYPE_LINUX_EXTENDED 0x85
struct Partitiondesc {
uint8_t boot;
uint8_t chs_begin[3];
uint8_t type;
uint8_t chs_end[3];
uint32_t lba_start;
uint32_t lba_length;
};
// Find a JOS kernel partition
int find_partition(off_t partition_sect, off_t extended_sect, int partoff) {
int i, r;
uint8_t buf[SECTORSIZE];
off_t o;
struct Partitiondesc *ptable;
// read the partition sector: initially sector 0
readsect(buf, partition_sect);
// check for partition table magic number
if ((uint8_t) buf[PTABLE_MAGIC_OFFSET] != PTABLE_MAGIC1
|| (uint8_t) buf[PTABLE_MAGIC_OFFSET + 1] != PTABLE_MAGIC2) {
return 0;
}
// search partition table
ptable = (struct Partitiondesc*) (buf + PTABLE_OFFSET);
for (i = 0; i < 4; i++) {
if (ptable[i].lba_length == 0) {
/* ignore entry */;
} else if (ptable[i].type == PTYPE_JOS_KERN) {
// use this partition
partition_sect += (off_t) ptable[i].lba_start;
fprintf(stderr, "Using partition %d (start sector %ld, sector length %ld)\n", partoff + i + 1, (long) partition_sect, (long) ptable[i].lba_length);
o = lseek(diskfd, partition_sect * SECTORSIZE, SEEK_SET);
if (o != partition_sect * SECTORSIZE) {
fprintf(stderr, "cannot seek to partition start: %s\n", strerror(errno));
usage();
}
maxoff = (off_t) ptable[i].lba_length * SECTORSIZE;
return 1;
} else if (ptable[i].type == PTYPE_DOS_EXTENDED
|| ptable[i].type == PTYPE_W95_EXTENDED
|| ptable[i].type == PTYPE_LINUX_EXTENDED) {
off_t inner_sect = extended_sect;
if (!inner_sect) {
inner_sect = ptable[i].lba_start;
}
if ((r = find_partition(ptable[i].lba_start + extended_sect, inner_sect, (partoff ? partoff + 1 : 4))) > 0) {
return r;
}
}
}
// no partition number found
return 0;
}
const uint32_t multiboot_header[3] = { 0x1BADB002U, 0U, -0x1BADB002U };
void do_multiboot(const char *filename) {
uint8_t buf[SECTORSIZE];
elf_header *elfh = (elf_header *) buf;
off_t o;
if ((diskfd = open(filename, O_RDWR)) < 0) {
fprintf(stderr, "%s: %s\n", filename, strerror(errno));
usage();
}
readsect(buf, 0);
if (elfh->e_magic != ELF_MAGIC) {
fprintf(stderr, "%s: not an ELF executable file\n", filename);
usage();
}
o = elfh->e_phoff + sizeof(elf_program) * elfh->e_phnum;
if (size_t(o) >= 4096 - sizeof(multiboot_header)) {
fprintf(stderr, "%s: ELF header too large to accommodate multiboot header\n", filename);
usage();
} else if (lseek(diskfd, o, SEEK_SET) != o) {
perror("lseek");
usage();
}
diskwrite(multiboot_header, sizeof(multiboot_header));
exit(0);
}