gpt: Embed stage2 code within GPT structures so an extra partition is not needed

This commit is contained in:
mintsuki 2020-12-06 16:43:38 +01:00
parent 8d04ab1c30
commit 35181c2b54
14 changed files with 337 additions and 135 deletions

View File

@ -40,19 +40,19 @@ limine-install: limine-install.c
test.hdd:
rm -f test.hdd
dd if=/dev/zero bs=1M count=0 seek=64 of=test.hdd
parted -s test.hdd mklabel msdos
parted -s test.hdd mklabel gpt
parted -s test.hdd mkpart primary 2048s 100%
echfs-test: all limine-install test.hdd
$(MAKE) -C test
echfs-utils -m -p0 test.hdd quick-format 512 > part_guid
echfs-utils -g -p0 test.hdd quick-format 512 > part_guid
sed "s/@GUID@/`cat part_guid`/g" < test/limine.cfg > limine.cfg.tmp
echfs-utils -m -p0 test.hdd import limine.cfg.tmp limine.cfg
echfs-utils -g -p0 test.hdd import limine.cfg.tmp limine.cfg
rm -f limine.cfg.tmp part_guid
echfs-utils -m -p0 test.hdd import stage2.map boot/stage2.map
echfs-utils -m -p0 test.hdd import test/test.elf boot/test.elf
echfs-utils -m -p0 test.hdd import test/bg.bmp boot/bg.bmp
echfs-utils -m -p0 test.hdd import test/font.bin boot/font.bin
echfs-utils -g -p0 test.hdd import stage2.map boot/stage2.map
echfs-utils -g -p0 test.hdd import test/test.elf boot/test.elf
echfs-utils -g -p0 test.hdd import test/bg.bmp boot/bg.bmp
echfs-utils -g -p0 test.hdd import test/font.bin boot/font.bin
./limine-install limine.bin test.hdd
qemu-system-x86_64 -net none -smp 4 -enable-kvm -cpu host -hda test.hdd -debugcon stdio

View File

@ -54,37 +54,31 @@ start:
; make sure that is the case now.
mov esp, 0x7c00
push 0x6fe0
push 0x7000
pop es
mov eax, dword [stage2_sector]
mov di, stage2_locs
mov eax, dword [di]
mov ebp, dword [di+4]
xor bx, bx
mov ecx, 32768
xor ecx, ecx
mov cx, word [di-4]
call read_sectors
jc err
mov eax, dword [di+8]
mov ebp, dword [di+12]
add bx, cx
mov cx, word [di-2]
call read_sectors
jc err
call load_gdt
lgdt [gdt]
cli
mov eax, cr0
bts ax, 0
mov cr0, eax
jmp 0x18:.mode32
bits 32
.mode32:
mov ax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
and edx, 0xff
jmp vector
bits 16
jmp 0x08:vector
err:
hlt
@ -100,6 +94,15 @@ times 6 db 0
bits 32
vector:
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
and edx, 0xff
push 0
push edx
@ -109,10 +112,12 @@ vector:
call 0x70000
bits 16
times 0x1b0-($-$$) db 0
stage2_sector: dd 0
times 0x1a4-($-$$) db 0
stage2_size_a: dw 0
stage2_size_b: dw 0
stage2_locs:
stage2_loc_a: dq 0
stage2_loc_b: dq 0
times 0x1b8-($-$$) db 0
times 510-($-$$) db 0
@ -127,5 +132,3 @@ align 16
stage2:
incbin '../stage2/stage2.bin.gz'
.size: equ $ - stage2
times 32768-($-$$) db 0

View File

@ -1,9 +1,10 @@
; ***********************************************
; Reads a disk sector with an LBA address
; ***********************************************
; *****************************
; Reads bytes from disk
; *****************************
; IN:
; EAX = LBA sector to load
; EAX = Start address to load low 32
; EBP = Start address to load high 32
; DL = Drive number
; ES = Buffer segment
; BX = Buffer offset
@ -15,32 +16,47 @@
read_sectors:
pusha
mov esi, .da_struct
mov si, .da_struct
mov word [si], 16
mov word [si+2], 1
mov word [si+4], bx
mov word [si+6], es
mov dword [si+8], eax
mov dword [si+12], 0
; Get bytes per sector
push dx
push si
push eax
push ebp
; Get bytes per sector
mov ah, 0x48
mov si, .drive_params
mov word [si], 30 ; buf_size
int 0x13
jc .done
mov bp, word [si+24] ; bytes_per_sect
; ECX byte count to CX sector count
mov ax, cx
shr ecx, 16
mov dx, cx
xor cx, cx
div bp
test dx, dx
adc cx, ax
setnz cl
add cx, ax
pop edx
pop eax
pop si
; EBP:EAX address to EAX LBA sector
div ebp
mov dword [si+8], eax
mov dword [si+12], edx
pop dx
.loop:
@ -52,6 +68,7 @@ read_sectors:
add word [si+4], bp
inc dword [si+8]
adc dword [si+12], 0
loop .loop

View File

@ -1,55 +1,8 @@
%define DIV_ROUNDUP(a, b) (((a) + ((b) - 1)) / (b))
%define ALIGN_UP(x, a) (DIV_ROUNDUP((x), (a)) * (a))
; The GDT is copied to <start of EBDA> - gdt.size, which will also serve
; as the upper limit for balloc()
load_gdt:
pusha
push es
mov ax, word [0x40e] ; 0x40e contains the value of a segment pointing to the EBDA
sub ax, ALIGN_UP(gdt.size, 16) / 16
mov es, ax
mov word [gdt.ptr], ax
shl dword [gdt.ptr], 4
xor di, di
mov si, gdt.start
mov cx, gdt.size
rep movsb
lgdt [gdt]
pop es
popa
ret
gdt:
dw .size - 1 ; GDT size
.ptr:
dd 0 ; GDT start address (calculated at runtime)
dw .size - 1 + 8 ; GDT size
dd .start - 8 ; GDT start address
.start:
; Null descriptor (required)
dw 0x0000 ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 00000000b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 16-bit code
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10011010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 16-bit data
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10010010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 32-bit code
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
@ -66,22 +19,6 @@ gdt:
db 11001111b ; Granularity
db 0x00 ; Base (high 8 bits)
; 64-bit code
dw 0x0000 ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10011010b ; Access
db 00100000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 64-bit data
dw 0x0000 ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10010010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
.end:
.size: equ .end - .start

View File

@ -2,16 +2,100 @@
#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <inttypes.h>
struct gpt_table_header {
// the head
char signature[8];
uint32_t revision;
uint32_t header_size;
uint32_t crc32;
uint32_t _reserved0;
// the partitioning info
uint64_t my_lba;
uint64_t alternate_lba;
uint64_t first_usable_lba;
uint64_t last_usable_lba;
// the guid
uint64_t disk_guid[2];
// entries related
uint64_t partition_entry_lba;
uint32_t number_of_partition_entries;
uint32_t size_of_partition_entry;
uint32_t partition_entry_array_crc32;
} __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,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static uint32_t crc32(void *_stream, size_t len) {
uint8_t *stream = _stream;
uint32_t ret = 0xffffffff;
for (size_t i = 0; i < len; i++) {
ret = (ret >> 8) ^ crc32_table[(ret ^ stream[i]) & 0xff];
}
ret ^= 0xffffffff;
return ret;
}
int main(int argc, char *argv[]) {
FILE *bootloader_file, *device;
uint8_t *bootloader_img;
uint8_t orig_mbr[70], timestamp[6];
uint32_t stage2_sect, sect_size;
if (argc < 3) {
printf("Usage: %s <bootloader image> <device> [<stage2 start sector> <sector size>]\n", argv[0]);
printf("Usage: %s <bootloader image> <device>\n", argv[0]);
return 1;
}
@ -21,8 +105,10 @@ int main(int argc, char *argv[]) {
return 1;
}
// The bootloader image is 64 sectors (32k)
bootloader_img = malloc(64 * 512);
fseek(bootloader_file, 0, SEEK_END);
size_t bootloader_file_size = ftell(bootloader_file);
bootloader_img = malloc(bootloader_file_size);
if (bootloader_img == NULL) {
perror("Error: ");
fclose(bootloader_file);
@ -31,7 +117,7 @@ int main(int argc, char *argv[]) {
// Load in bootloader image
fseek(bootloader_file, 0, SEEK_SET);
fread(bootloader_img, 64, 512, bootloader_file);
fread(bootloader_img, 1, bootloader_file_size, bootloader_file);
fclose(bootloader_file);
device = fopen(argv[2], "r+b");
@ -41,12 +127,96 @@ int main(int argc, char *argv[]) {
return 1;
}
stage2_sect = 0;
sect_size = 512;
if (argc >= 4)
sscanf(argv[3], "%" SCNu32, &stage2_sect);
if (argc >= 5)
sscanf(argv[4], "%" SCNu32, &sect_size);
// Probe for GPT and logical block size
int gpt = 0;
struct gpt_table_header gpt_header;
int lb_guesses[] = { 512, 4096 };
int lb_size;
for (size_t i = 0; i < sizeof(lb_guesses) / sizeof(int); i++) {
fseek(device, lb_guesses[i], SEEK_SET);
fread(&gpt_header, sizeof(struct gpt_table_header), 1, device);
if (!strncmp(gpt_header.signature, "EFI PART", 8)) {
gpt = 1;
lb_size = lb_guesses[i];
fprintf(stderr, "Installing to GPT. Logical block size of %d bytes.\n",
lb_guesses[i]);
break;
}
}
struct gpt_table_header secondary_gpt_header;
if (gpt) {
fprintf(stderr, "Secondary header at LBA 0x%" PRIx64 ".\n",
gpt_header.alternate_lba);
fseek(device, lb_size * gpt_header.alternate_lba, SEEK_SET);
fread(&secondary_gpt_header, sizeof(struct gpt_table_header), 1, device);
if (!strncmp(secondary_gpt_header.signature, "EFI PART", 8)) {
fprintf(stderr, "Secondary header valid.\n");
} else {
fprintf(stderr, "Secondary header not valid, aborting.\n");
abort();
}
}
size_t stage2_size = bootloader_file_size - 512;
uint16_t stage2_size_a = stage2_size / 2 + stage2_size % 2;
uint16_t stage2_size_b = stage2_size / 2;
// 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)
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);
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;
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);
}
fprintf(stderr, "Stage 2 to be located at 0x%" PRIx64 " and 0x%" PRIx64 ".\n",
stage2_loc_a, stage2_loc_b);
// Save original timestamp
fseek(device, 218, SEEK_SET);
@ -61,12 +231,17 @@ int main(int argc, char *argv[]) {
fwrite(&bootloader_img[0], 1, 512, device);
// Write the rest of stage 2 to the device
fseek(device, stage2_sect * sect_size, SEEK_SET);
fwrite(&bootloader_img[0], 64, 512, device);
fseek(device, stage2_loc_a, SEEK_SET);
fwrite(&bootloader_img[512], 1, stage2_size_a, device);
fseek(device, stage2_loc_b, SEEK_SET);
fwrite(&bootloader_img[512 + stage2_size_a], 1, stage2_size_b, device);
// Hardcode in the bootsector the location of stage 2
fseek(device, 0x1b0, SEEK_SET);
fwrite(&stage2_sect, 1, sizeof(uint32_t), device);
// Hardcode in the bootsector the location of stage 2 halves
fseek(device, 0x1a4, SEEK_SET);
fwrite(&stage2_size_a, 1, sizeof(uint16_t), device);
fwrite(&stage2_size_b, 1, sizeof(uint16_t), device);
fwrite(&stage2_loc_a, 1, sizeof(uint64_t), device);
fwrite(&stage2_loc_b, 1, sizeof(uint64_t), device);
// Write back timestamp
fseek(device, 218, SEEK_SET);

Binary file not shown.

Binary file not shown.

View File

@ -10,7 +10,7 @@ start:
mov ss, ax
mov sp, 0x7c00
sti
call load_gdt
lgdt [gdt]
cli
@ -18,10 +18,10 @@ start:
bts ax, 0
mov cr0, eax
jmp 0x18:.mode32
jmp 0x08:.mode32
bits 32
.mode32:
mov ax, 0x20
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax

Binary file not shown.

View File

@ -1,6 +1,7 @@
extern bss_begin
extern bss_end
extern entry
extern gdt
section .entry
@ -15,4 +16,14 @@ _start:
sub ecx, bss_begin
rep stosb
lgdt [gdt]
jmp 0x18:.reload_cs
.reload_cs:
mov eax, 0x20
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp entry

View File

@ -199,7 +199,7 @@ load_up:
if (part_index)
return;
part_index = conv_mem_alloc(sizeof(struct part) * part_count);
part_index = ext_mem_alloc(sizeof(struct part) * part_count);
goto load_up;
}
}

View File

@ -33,10 +33,11 @@ void entry(uint8_t _boot_drive, int pxe_boot) {
if (!a20_enable())
panic("Could not enable A20 line");
part_create_index();
init_e820();
init_memmap();
part_create_index();
if (pxe_boot) {
pxe_init();
if(init_config_pxe()) {

View File

@ -293,13 +293,8 @@ void *conv_mem_alloc(size_t count) {
void *conv_mem_alloc_aligned(size_t count, size_t alignment) {
if (!bump_allocator_limit) {
// The balloc limit is the beginning of the GDT
struct {
uint16_t limit;
uint32_t ptr;
} __attribute__((packed)) gdtr;
asm volatile ("sgdt %0" :: "m"(gdtr) : "memory");
bump_allocator_limit = gdtr.ptr;
// The balloc limit is the beginning of the EBDA
bump_allocator_limit = *((uint16_t *)0x40e) << 4;
}
size_t new_base = ALIGN_UP(bump_allocator_base, alignment);

63
stage2/sys/gdt.asm Normal file
View File

@ -0,0 +1,63 @@
section .data
global gdt
gdt:
dw .size - 1 ; GDT size
dd .start ; GDT start address
.start:
; Null desc
dq 0
; 16-bit code
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10011010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 16-bit data
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10010010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 32-bit code
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10011010b ; Access
db 11001111b ; Granularity
db 0x00 ; Base (high 8 bits)
; 32-bit data
dw 0xffff ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10010010b ; Access
db 11001111b ; Granularity
db 0x00 ; Base (high 8 bits)
; 64-bit code
dw 0x0000 ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10011010b ; Access
db 00100000b ; Granularity
db 0x00 ; Base (high 8 bits)
; 64-bit data
dw 0x0000 ; Limit
dw 0x0000 ; Base (low 16 bits)
db 0x00 ; Base (mid 8 bits)
db 10010010b ; Access
db 00000000b ; Granularity
db 0x00 ; Base (high 8 bits)
.end:
.size: equ .end - .start