limine-install: Add more sanity checks and give the choice to install stage 2 to a partition with GPT
This commit is contained in:
parent
35181c2b54
commit
eeb647d07c
21
README.md
21
README.md
@ -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.
|
||||
|
142
limine-install.c
142
limine-install.c
@ -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",
|
||||
|
Loading…
x
Reference in New Issue
Block a user