#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); }