limine-install: Add more sanity checks and give the choice to install stage 2 to a partition with GPT

This commit is contained in:
mintsuki 2020-12-07 18:20:52 +01:00
parent 35181c2b54
commit eeb647d07c
2 changed files with 115 additions and 48 deletions

View File

@ -87,18 +87,21 @@ limine-install <bootloader image> <path to device/image>
Where `<bootloader image>` is the path to a `limine.bin` file.
### GPT
If using a GPT formatted device, it will be necessary to create an extra partition
(of at least 32K in size) to store stage 2 code. Then it will be necessary to tell
`limine-install` where this partition is located by specifying the start sector
number (in decimal).
If using a GPT formatted device, there are 2 options one can follow for installation:
* Specifying a dedicated stage 2 partition.
* Letting `limine-install` attempt to embed stage 2 within GPT structures.
In case one wants to specify a stage 2 partition, create a partition on the GPT
device of at least 32KiB in size, and pass the 1-based number of the partition
to `limine-install` as a third argument; such as:
```bash
fdisk <device> # Create bootloader partition using your favourite method
limine-install <bootloader image> <path to device/image> <start sector of boot partition> <sector size>
limine-install <bootloader image> <path to device/image> <partition 1-based number>
```
The `<sector size>` argument is optional. Use it to specify the sector size in bytes
if it is not Limine's expected default of 512 bytes.
In case one wants to let `limine-install` embed stage 2 within GPT's structures,
simply omit the partition number, and invoke `limine-install` the same as one would
do for an MBR partitioned device.
### Configuration
Then make sure the device/image contains at least 1 partition formatted in
@ -125,7 +128,7 @@ echfs-utils -m -p0 test.img import path/to/limine.cfg limine.cfg
echfs-utils -m -p0 test.img import path/to/kernel.elf kernel.elf
echfs-utils -m -p0 test.img import <path to file> <path in image>
...
limine-install test.img
./limine-install limine.bin test.img
```
One can get `echfs-utils` by installing https://github.com/qword-os/echfs.

View File

@ -30,6 +30,19 @@ struct gpt_table_header {
uint32_t partition_entry_array_crc32;
} __attribute__((packed));
struct gpt_entry {
uint64_t partition_type_guid[2];
uint64_t unique_partition_guid[2];
uint64_t starting_lba;
uint64_t ending_lba;
uint64_t attributes;
uint16_t partition_name[36];
} __attribute__((packed));
// This table from https://web.mit.edu/freebsd/head/sys/libkern/crc32.c
const uint32_t crc32_table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@ -95,7 +108,7 @@ int main(int argc, char *argv[]) {
uint8_t orig_mbr[70], timestamp[6];
if (argc < 3) {
printf("Usage: %s <bootloader image> <device>\n", argv[0]);
printf("Usage: %s <bootloader image> <device> [GPT partition index]\n", argv[0]);
return 1;
}
@ -165,54 +178,105 @@ int main(int argc, char *argv[]) {
// Default split of stage2 for MBR (consecutive in post MBR gap)
uint64_t stage2_loc_a = 512;
uint64_t stage2_loc_b = stage2_loc_a + stage2_size_a;
if (stage2_loc_b & 512)
if (stage2_loc_b & (512 - 1))
stage2_loc_b = (stage2_loc_b + 512) & ~(512 - 1);
if (gpt) {
stage2_loc_a = (gpt_header.partition_entry_lba + 32) * lb_size;
stage2_loc_a -= stage2_size_a;
stage2_loc_a &= ~(lb_size - 1);
stage2_loc_b = (secondary_gpt_header.partition_entry_lba + 32) * lb_size;
stage2_loc_b -= stage2_size_b;
stage2_loc_b &= ~(lb_size - 1);
if (argc > 3) {
uint32_t partition_num;
sscanf(argv[3], "%" SCNu32, &partition_num);
partition_num--;
if (partition_num > gpt_header.number_of_partition_entries) {
fprintf(stderr, "error: Partition number is too large.\n");
abort();
}
size_t partition_entries_per_lb =
lb_size / gpt_header.size_of_partition_entry;
size_t new_partition_array_lba_size =
stage2_loc_a / lb_size - gpt_header.partition_entry_lba;
size_t new_partition_entry_count =
new_partition_array_lba_size * partition_entries_per_lb;
struct gpt_entry gpt_entry;
fseek(device, (gpt_header.partition_entry_lba * lb_size)
+ (partition_num * sizeof(struct gpt_entry)), SEEK_SET);
fread(&gpt_entry, sizeof(struct gpt_entry), 1, device);
uint8_t *partition_array =
malloc(new_partition_entry_count * gpt_header.size_of_partition_entry);
assert(partition_array);
if (gpt_entry.unique_partition_guid[0] == 0 &&
gpt_entry.unique_partition_guid[1] == 0) {
fprintf(stderr, "error: No such partition.\n");
abort();
}
fseek(device, gpt_header.partition_entry_lba * lb_size, SEEK_SET);
fread(partition_array,
new_partition_entry_count * gpt_header.size_of_partition_entry,
1, device);
fprintf(stderr, "GPT partition specified. Installing there instead of embedding.\n");
uint32_t crc32_partition_array =
crc32(partition_array,
new_partition_entry_count * gpt_header.size_of_partition_entry);
stage2_loc_a = gpt_entry.starting_lba * lb_size;
stage2_loc_b = stage2_loc_a + stage2_size_a;
if (stage2_loc_b & (lb_size - 1))
stage2_loc_b = (stage2_loc_b + lb_size) & ~(lb_size - 1);
} else {
fprintf(stderr, "GPT partition NOT specified. Attempting GPT embedding.\n");
free(partition_array);
ssize_t max_partition_entry_used = -1;
for (ssize_t i = 0; i < gpt_header.number_of_partition_entries; i++) {
struct gpt_entry gpt_entry;
fseek(device, (gpt_header.partition_entry_lba * lb_size)
+ (i * sizeof(struct gpt_entry)), SEEK_SET);
fread(&gpt_entry, sizeof(struct gpt_entry), 1, device);
gpt_header.partition_entry_array_crc32 = crc32_partition_array;
gpt_header.number_of_partition_entries = new_partition_entry_count;
gpt_header.crc32 = 0;
gpt_header.crc32 = crc32(&gpt_header, sizeof(struct gpt_table_header));
fseek(device, lb_size, SEEK_SET);
fwrite(&gpt_header, sizeof(struct gpt_table_header), 1, device);
if (gpt_entry.unique_partition_guid[0] != 0 ||
gpt_entry.unique_partition_guid[1] != 0) {
if (i > max_partition_entry_used)
max_partition_entry_used = i;
}
}
secondary_gpt_header.partition_entry_array_crc32 = crc32_partition_array;
secondary_gpt_header.number_of_partition_entries =
new_partition_entry_count;
secondary_gpt_header.crc32 = 0;
secondary_gpt_header.crc32 =
crc32(&secondary_gpt_header, sizeof(struct gpt_table_header));
fseek(device, lb_size * gpt_header.alternate_lba, SEEK_SET);
fwrite(&secondary_gpt_header, sizeof(struct gpt_table_header), 1, device);
stage2_loc_a = (gpt_header.partition_entry_lba + 32) * lb_size;
stage2_loc_a -= stage2_size_a;
stage2_loc_a &= ~(lb_size - 1);
stage2_loc_b = (secondary_gpt_header.partition_entry_lba + 32) * lb_size;
stage2_loc_b -= stage2_size_b;
stage2_loc_b &= ~(lb_size - 1);
size_t partition_entries_per_lb =
lb_size / gpt_header.size_of_partition_entry;
size_t new_partition_array_lba_size =
stage2_loc_a / lb_size - gpt_header.partition_entry_lba;
size_t new_partition_entry_count =
new_partition_array_lba_size * partition_entries_per_lb;
if ((ssize_t)new_partition_array_lba_size <= max_partition_entry_used) {
fprintf(stderr, "error: Cannot embed because there are too many used partition entries.\n");
abort();
}
uint8_t *partition_array =
malloc(new_partition_entry_count * gpt_header.size_of_partition_entry);
assert(partition_array);
fseek(device, gpt_header.partition_entry_lba * lb_size, SEEK_SET);
fread(partition_array,
new_partition_entry_count * gpt_header.size_of_partition_entry,
1, device);
uint32_t crc32_partition_array =
crc32(partition_array,
new_partition_entry_count * gpt_header.size_of_partition_entry);
free(partition_array);
gpt_header.partition_entry_array_crc32 = crc32_partition_array;
gpt_header.number_of_partition_entries = new_partition_entry_count;
gpt_header.crc32 = 0;
gpt_header.crc32 = crc32(&gpt_header, sizeof(struct gpt_table_header));
fseek(device, lb_size, SEEK_SET);
fwrite(&gpt_header, sizeof(struct gpt_table_header), 1, device);
secondary_gpt_header.partition_entry_array_crc32 = crc32_partition_array;
secondary_gpt_header.number_of_partition_entries =
new_partition_entry_count;
secondary_gpt_header.crc32 = 0;
secondary_gpt_header.crc32 =
crc32(&secondary_gpt_header, sizeof(struct gpt_table_header));
fseek(device, lb_size * gpt_header.alternate_lba, SEEK_SET);
fwrite(&secondary_gpt_header, sizeof(struct gpt_table_header), 1, device);
}
} else {
fprintf(stderr, "Installing to MBR.\n");
}
fprintf(stderr, "Stage 2 to be located at 0x%" PRIx64 " and 0x%" PRIx64 ".\n",