From cfc731345132737803fdbc884336cef61fb7938c Mon Sep 17 00:00:00 2001 From: Bananymous Date: Thu, 9 Nov 2023 22:42:47 +0200 Subject: [PATCH] Bootloader: Start work on bootloader I wrote a fast first stage bootloader and a installer to put it into a disk image. --- bootloader/.gitignore | 3 + bootloader/arch/x86_64/boot.S | 287 ++++++++++++++++++++++++++++ bootloader/arch/x86_64/linker.ld | 11 ++ bootloader/build-and-run.sh | 45 +++++ bootloader/installer/CMakeLists.txt | 14 ++ bootloader/installer/ELF.cpp | 140 ++++++++++++++ bootloader/installer/ELF.h | 36 ++++ bootloader/installer/GPT.cpp | 171 +++++++++++++++++ bootloader/installer/GPT.h | 88 +++++++++ bootloader/installer/GUID.cpp | 26 +++ bootloader/installer/GUID.h | 33 ++++ bootloader/installer/build.sh | 3 + bootloader/installer/crc32.cpp | 80 ++++++++ bootloader/installer/crc32.h | 6 + bootloader/installer/main.cpp | 41 ++++ 15 files changed, 984 insertions(+) create mode 100644 bootloader/.gitignore create mode 100644 bootloader/arch/x86_64/boot.S create mode 100644 bootloader/arch/x86_64/linker.ld create mode 100755 bootloader/build-and-run.sh create mode 100644 bootloader/installer/CMakeLists.txt create mode 100644 bootloader/installer/ELF.cpp create mode 100644 bootloader/installer/ELF.h create mode 100644 bootloader/installer/GPT.cpp create mode 100644 bootloader/installer/GPT.h create mode 100644 bootloader/installer/GUID.cpp create mode 100644 bootloader/installer/GUID.h create mode 100755 bootloader/installer/build.sh create mode 100644 bootloader/installer/crc32.cpp create mode 100644 bootloader/installer/crc32.h create mode 100644 bootloader/installer/main.cpp diff --git a/bootloader/.gitignore b/bootloader/.gitignore new file mode 100644 index 0000000000..1f20c0deb8 --- /dev/null +++ b/bootloader/.gitignore @@ -0,0 +1,3 @@ +test.img +build/ +installer/build/ diff --git a/bootloader/arch/x86_64/boot.S b/bootloader/arch/x86_64/boot.S new file mode 100644 index 0000000000..9ff1b220c3 --- /dev/null +++ b/bootloader/arch/x86_64/boot.S @@ -0,0 +1,287 @@ +# FIXME: don't assume 512 byte sectors +.set SECTOR_SIZE, 512 + +.set GPT_HEADER_ADDR, free_memory_start +.set GPT_ENTRY_ADDR, free_memory_start + SECTOR_SIZE + + + +.code16 + +######################################### +# +# STAGE 1 BOOTLOADER +# +# its sole purpose is to load stage2 from +# bios boot partition +# +######################################### + +.section .stage1 + +stage1_start: + jmp main + +# al: character to print +putc: + mov $0x0E, %ah + int $0x10 + ret + +# ds:si: null terminated string to print +puts: + push %si + push %ax + +1: + lodsb + + test %al, %al + jz 2f + + call putc + jmp 1b + +2: + mov $'\r', %al + call putc + mov $'\n', %al + call putc + + pop %ax + pop %si + ret + +# si: ptr1 +# di: ptr2 +# cx: count +# return: 1 if equal, 0 otherwise +memcmp: + pushw %si + pushw %di + + cld + repe cmpsb + setzb %al + + popw %di + popw %si + ret + + +# read sectors from disk +# +# bx:eax: lba start +# cx: lba count (has to less than 0x80) +# dl: drive number +# ds:di: physical address +# +# returns only on success +read_from_disk: + push %ax + push %si + + # prepare disk read packet + mov $disk_address_packet, %si + movb $0x10, 0x00(%si) # packet size + movb $0x00, 0x01(%si) # always 0 + movw %cx, 0x02(%si) # lba count + movw %di, 0x04(%si) # offset + movw %ds, 0x06(%si) # segment + movl %eax, 0x08(%si) # 32 bit lower lba + movw %bx, 0x0C(%si) # 16 bit upper lba + movw $0, 0x0E(%si) # zero + + # issue read command + clc + mov $0x42, %ah + int $0x13 + jc .read_failed + + pop %si + pop %ax + ret + + +main: + # setup segments + movw $0, %ax + movw %ax, %ds + movw %ax, %es + + # setup stack + movw %ax, %ss + movw $0x7C00, %sp + + # save boot disk number + movb %dl, (boot_disk_number) + + # confirm that int 13h extensions are available + clc + movb $0x41, %ah + movw $0x55AA, %bx + movb (boot_disk_number), %dl + int $0x13 + jc .no_int13h_ext + + # read gpt header + movl $1, %eax + movw $0, %bx + movw $1, %cx + movb (boot_disk_number), %dl + movw $GPT_HEADER_ADDR, %di + call read_from_disk + + # confirm header (starts with 'EFI PART') + cmpl $0x20494645, (GPT_HEADER_ADDR + 0) + jne .not_gpt_partition + cmpl $0x54524150, (GPT_HEADER_ADDR + 4) + jne .not_gpt_partition + + # eax := entry_count + movl (GPT_HEADER_ADDR + 80), %eax + test %eax, %eax + jz .no_bios_boot_partition + + # edx:eax := eax * entry_size + mull (GPT_HEADER_ADDR + 84) + test %edx, %edx + jnz .too_gpt_big_entries + + # sector count := (arr_size + SECTOR_SIZE - 1) / SECTOR_SIZE + pushl %eax + addl $(SECTOR_SIZE - 1), %eax + movl $SECTOR_SIZE, %ecx + divl %ecx + movl %eax, %ecx + popl %eax + + # start lba + movl (GPT_HEADER_ADDR + 72), %eax + movw (GPT_HEADER_ADDR + 76), %bx + + movb (boot_disk_number), %dl + movw $GPT_ENTRY_ADDR, %di + call read_from_disk + + # NOTE: 'only' 0xFFFF partitions supported + movw (GPT_HEADER_ADDR + 80), %cx + +.loop_entries: + push %cx + movw $16, %cx + movw $bios_boot_guid, %si + call memcmp + pop %cx + + testb %al, %al + jnz .bios_boot_found + + # add entry size to entry pointer + addw (GPT_HEADER_ADDR + 84), %di + + loop .loop_entries + jmp .no_bios_boot_partition + +.bios_boot_found: + # first lba + movl 32(%di), %eax + movw $0, %bx + + # count := last lba - first lba + 1 + movl 40(%di), %ecx + subl %eax, %ecx + addl $1, %ecx + + # calculate stage2 sector count + movw $((stage2_end - stage2_start + SECTOR_SIZE - 1) / SECTOR_SIZE), %cx + + movb (boot_disk_number), %dl + movw $stage2_start, %di + + call read_from_disk + jmp stage2_start + +print_and_halt: + call puts +halt: + hlt + jmp halt + +.no_int13h_ext: + mov $no_int13_ext_msg, %si + jmp print_and_halt + +.read_failed: + mov $read_failed_msg, %si + jmp print_and_halt + +.not_gpt_partition: + mov $not_gpt_partition_msg, %si + jmp print_and_halt + +.no_bios_boot_partition: + mov $no_bios_boot_partition_msg, %si + jmp print_and_halt + +.too_gpt_big_entries: + mov $too_gpt_big_entries_msg, %si + jmp print_and_halt + + + +# 21686148-6449-6E6F-744E-656564454649 +bios_boot_guid: + .long 0x21686148 # little endian + .word 0x6449 # little endian + .word 0x6E6F # little endian + .word 0x4E74 # big endian + .quad 0x494645646565 # big endian + +no_int13_ext_msg: + .asciz "no INT 13h ext" + +read_failed_msg: + .asciz "read error" + +not_gpt_partition_msg: + .asciz "not gpt" + +no_bios_boot_partition_msg: + .asciz "no bios boot partition" + +too_gpt_big_entries_msg: + .asciz "too big GPT array" + +boot_disk_number: + .skip 1 + +disk_address_packet: + .skip 16 + + + +######################################### +# +# STAGE 2 BOOTLOADER +# +######################################### + +.section .stage2 +stage2_start: + # clear screen and enter 80x25 text mode + movb $0x03, %al + movb $0x00, %ah + int $0x10 + + # print hello message + movw $hello_msg, %si + call puts + +1: + jmp 1b + +hello_msg: + .asciz "This is banan-os bootloader" + +stage2_end: diff --git a/bootloader/arch/x86_64/linker.ld b/bootloader/arch/x86_64/linker.ld new file mode 100644 index 0000000000..b23bde4c8c --- /dev/null +++ b/bootloader/arch/x86_64/linker.ld @@ -0,0 +1,11 @@ +SECTIONS +{ + . = 0x7C00; + .stage1 : { *(.stage1*) } + + . = ALIGN(512); + .stage2 : { *(.stage2) } + + . = ALIGN(512); + free_memory_start = .; +} \ No newline at end of file diff --git a/bootloader/build-and-run.sh b/bootloader/build-and-run.sh new file mode 100755 index 0000000000..0f2cce84a8 --- /dev/null +++ b/bootloader/build-and-run.sh @@ -0,0 +1,45 @@ +#!/bin/sh + +set -e + +CURRENT_DIR=$(dirname $(realpath $0)) + +INSTALLER_DIR=$CURRENT_DIR/installer +INSTALLER_BUILD_DIR=$INSTALLER_DIR/build + +BUILD_DIR=$CURRENT_DIR/build +DISK_IMAGE_PATH=$CURRENT_DIR/test.img + +if ! [ -d $INSTALLER_BUILD_DIR ]; then + mkdir -p $INSTALLER_BUILD_DIR + cd $INSTALLER_BUILD_DIR + cmake .. +fi + +cd $INSTALLER_BUILD_DIR +make + +cd $CURRENT_DIR + +echo creating clean disk image +truncate --size 0 $DISK_IMAGE_PATH +truncate --size 50M $DISK_IMAGE_PATH +echo -ne 'g\nn\n\n\n+1M\nt 1\n4\nw\n' | fdisk $DISK_IMAGE_PATH > /dev/null + +mkdir -p $BUILD_DIR + +echo compiling bootloader +x86_64-banan_os-as arch/x86_64/boot.S -o $BUILD_DIR/bootloader.o + +echo linking bootloader +x86_64-banan_os-ld -nostdlib -T arch/x86_64/linker.ld $BUILD_DIR/bootloader.o -o $BUILD_DIR/bootloader + +echo installing bootloader +$INSTALLER_BUILD_DIR/x86_64-banan_os-bootloader-installer $BUILD_DIR/bootloader $DISK_IMAGE_PATH + +if [ "$1" == "debug" ] ; then + QEMU_FLAGS="-s -S" +fi + +echo running qemu +qemu-system-x86_64 $QEMU_FLAGS --drive format=raw,file=test.img diff --git a/bootloader/installer/CMakeLists.txt b/bootloader/installer/CMakeLists.txt new file mode 100644 index 0000000000..65e3dc35f4 --- /dev/null +++ b/bootloader/installer/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.26) + +project(x86_64-banan_os-bootloader-installer CXX) + +set(SOURCES + crc32.cpp + ELF.cpp + GPT.cpp + GUID.cpp + main.cpp +) + +add_executable(x86_64-banan_os-bootloader-installer ${SOURCES}) +target_compile_options(x86_64-banan_os-bootloader-installer PUBLIC -O2 -std=c++20) diff --git a/bootloader/installer/ELF.cpp b/bootloader/installer/ELF.cpp new file mode 100644 index 0000000000..e0a53815bd --- /dev/null +++ b/bootloader/installer/ELF.cpp @@ -0,0 +1,140 @@ +#include "ELF.h" + +#include +#include +#include +#include +#include +#include +#include + +ELFFile::ELFFile(std::string_view path) + : m_path(path) +{ + m_fd = open(m_path.c_str(), O_RDONLY); + if (m_fd == -1) + { + std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + + if (fstat(m_fd, &m_stat) == -1) + { + std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + + void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ, MAP_PRIVATE, m_fd, 0); + if (mmap_addr == MAP_FAILED) + { + std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + m_mmap = reinterpret_cast(mmap_addr); + + if (!validate_elf_header()) + return; + + m_success = true; +} + +ELFFile::~ELFFile() +{ + if (m_mmap) + munmap(m_mmap, m_stat.st_size); + m_mmap = nullptr; + + if (m_fd != -1) + close(m_fd); + m_fd = -1; +} + +const Elf64_Ehdr& ELFFile::elf_header() const +{ + return *reinterpret_cast(m_mmap); +} + +bool ELFFile::validate_elf_header() const +{ + if (m_stat.st_size < sizeof(Elf64_Ehdr)) + { + std::cerr << m_path << " is too small to be a ELF executable" << std::endl; + return false; + } + + const auto& elf_header = this->elf_header(); + + if ( + elf_header.e_ident[EI_MAG0] != ELFMAG0 || + elf_header.e_ident[EI_MAG1] != ELFMAG1 || + elf_header.e_ident[EI_MAG2] != ELFMAG2 || + elf_header.e_ident[EI_MAG3] != ELFMAG3 + ) + { + std::cerr << m_path << " doesn't have an ELF magic number" << std::endl; + return false; + } + + if (elf_header.e_ident[EI_CLASS] != ELFCLASS64) + { + std::cerr << m_path << " is not 64 bit ELF" << std::endl; + return false; + } + + if (elf_header.e_ident[EI_DATA] != ELFDATA2LSB) + { + std::cerr << m_path << " is not in little endian format" << std::endl; + return false; + } + + if (elf_header.e_ident[EI_VERSION] != EV_CURRENT) + { + std::cerr << m_path << " has unsupported version" << std::endl; + return false; + } + + if (elf_header.e_type != ET_EXEC) + { + std::cerr << m_path << " is not an executable ELF file" << std::endl; + return false; + } + + if (elf_header.e_machine != EM_X86_64) + { + std::cerr << m_path << " is not an x86_64 ELF file" << std::endl; + return false; + } + + return true; +} + +const Elf64_Shdr& ELFFile::section_header(std::size_t index) const +{ + const auto& elf_header = this->elf_header(); + assert(index < elf_header.e_shnum); + const uint8_t* section_array_start = m_mmap + elf_header.e_shoff; + return *reinterpret_cast(section_array_start + index * elf_header.e_shentsize); +} + +std::string_view ELFFile::section_name(const Elf64_Shdr& section_header) const +{ + const auto& elf_header = this->elf_header(); + assert(elf_header.e_shstrndx != SHN_UNDEF); + const auto& section_string_table = this->section_header(elf_header.e_shstrndx); + const char* string_table_start = reinterpret_cast(m_mmap + section_string_table.sh_offset); + return string_table_start + section_header.sh_name; +} + +std::optional> ELFFile::find_section(std::string_view name) const +{ + const auto& elf_header = this->elf_header(); + for (std::size_t i = 0; i < elf_header.e_shnum; i++) + { + const auto& section_header = this->section_header(i); + auto section_name = this->section_name(section_header); + if (section_name != name) + continue; + return std::span(m_mmap + section_header.sh_offset, section_header.sh_size); + } + return {}; +} diff --git a/bootloader/installer/ELF.h b/bootloader/installer/ELF.h new file mode 100644 index 0000000000..d04812884e --- /dev/null +++ b/bootloader/installer/ELF.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +class ELFFile +{ +public: + ELFFile(std::string_view path); + ~ELFFile(); + + const Elf64_Ehdr& elf_header() const; + std::optional> find_section(std::string_view name) const; + + bool success() const { return m_success; } + + std::string_view path() const { return m_path; } + +private: + const Elf64_Shdr& section_header(std::size_t index) const; + std::string_view section_name(const Elf64_Shdr&) const; + bool validate_elf_header() const; + +private: + const std::string m_path; + bool m_success { false }; + int m_fd { -1 }; + struct stat m_stat { }; + uint8_t* m_mmap { nullptr }; +}; diff --git a/bootloader/installer/GPT.cpp b/bootloader/installer/GPT.cpp new file mode 100644 index 0000000000..dcc70b628c --- /dev/null +++ b/bootloader/installer/GPT.cpp @@ -0,0 +1,171 @@ +#include "crc32.h" +#include "GPT.h" + +#include +#include +#include +#include +#include +#include +#include + +// FIXME: don't assume 512 byte sectors +#define SECTOR_SIZE 512 + +GPTFile::GPTFile(std::string_view path) + : m_path(path) +{ + m_fd = open(m_path.c_str(), O_RDWR); + if (m_fd == -1) + { + std::cerr << "Could not open '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + + if (fstat(m_fd, &m_stat) == -1) + { + std::cerr << "Could not stat '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + + void* mmap_addr = mmap(nullptr, m_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_fd, 0); + if (mmap_addr == MAP_FAILED) + { + std::cerr << "Could not mmap '" << m_path << "': " << std::strerror(errno) << std::endl; + return; + } + m_mmap = reinterpret_cast(mmap_addr); + + if (!validate_gpt_header()) + return; + + m_success = true; +} + +GPTFile::~GPTFile() +{ + if (m_mmap) + munmap(m_mmap, m_stat.st_size); + m_mmap = nullptr; + + if (m_fd != -1) + close(m_fd); + m_fd = -1; +} + +MBR& GPTFile::mbr() +{ + return *reinterpret_cast(m_mmap); +} + +const GPTHeader& GPTFile::gpt_header() const +{ + return *reinterpret_cast(m_mmap + SECTOR_SIZE); +} + +bool GPTFile::install_bootcode(std::span boot_code) +{ + auto& mbr = this->mbr(); + + if (boot_code.size() > sizeof(mbr.boot_code)) + { + std::cerr << m_path << ": can't fit " << boot_code.size() << " bytes of boot code in mbr (max is " << sizeof(mbr.boot_code) << ")" << std::endl; + return false; + } + + // copy boot code + memcpy(mbr.boot_code, boot_code.data(), boot_code.size()); + + // setup mbr + mbr.unique_mbr_disk_signature = 0xdeadbeef; + mbr.unknown = 0; + mbr.signature = 0xAA55; + + // setup mbr partition records + mbr.partition_records[0].boot_indicator = 0x00; + mbr.partition_records[0].starting_chs[0] = 0x00; + mbr.partition_records[0].starting_chs[1] = 0x02; + mbr.partition_records[0].starting_chs[2] = 0x00; + mbr.partition_records[0].os_type = 0xEE; + mbr.partition_records[0].ending_chs[0] = 0xFF; + mbr.partition_records[0].ending_chs[1] = 0xFF; + mbr.partition_records[0].ending_chs[2] = 0xFF; + mbr.partition_records[0].starting_lba = 1; + mbr.partition_records[0].size_in_lba = 0xFFFFFFFF; + memset(&mbr.partition_records[1], 0x00, sizeof(MBRPartitionRecord)); + memset(&mbr.partition_records[2], 0x00, sizeof(MBRPartitionRecord)); + memset(&mbr.partition_records[3], 0x00, sizeof(MBRPartitionRecord)); + + return true; +} + +bool GPTFile::write_partition(std::span data, GUID type_guid) +{ + auto partition = find_partition(type_guid); + if (!partition.has_value()) + { + std::cerr << m_path << ": could not find partition with type " << type_guid << std::endl; + return false; + } + + const std::size_t partition_size = (partition->ending_lba - partition->starting_lba + 1) * SECTOR_SIZE; + + if (data.size() > partition_size) + { + std::cerr << m_path << ": can't fit " << data.size() << " bytes of data to partition of size " << partition_size << std::endl; + return false; + } + + memcpy(m_mmap + partition->starting_lba * SECTOR_SIZE, data.data(), data.size()); + + return true; +} + +std::optional GPTFile::find_partition(const GUID& type_guid) const +{ + const auto& gpt_header = this->gpt_header(); + const uint8_t* partition_entry_array_start = m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE; + for (std::size_t i = 0; i < gpt_header.number_of_partition_entries; i++) + { + const auto& partition_entry = *reinterpret_cast(partition_entry_array_start + i * gpt_header.size_of_partition_entry); + if (partition_entry.type_guid != type_guid) + continue; + return partition_entry; + } + return {}; +} + +bool GPTFile::validate_gpt_header() const +{ + if (SECTOR_SIZE + m_stat.st_size < sizeof(GPTHeader)) + { + std::cerr << m_path << " is too small to have GPT header" << std::endl; + return false; + } + + auto gpt_header = this->gpt_header(); + + if (std::memcmp(gpt_header.signature, "EFI PART", 8) != 0) + { + std::cerr << m_path << " doesn't contain GPT partition header signature" << std::endl; + return false; + } + + const uint32_t header_crc32 = gpt_header.header_crc32; + + gpt_header.header_crc32 = 0; + if (header_crc32 != crc32_checksum(reinterpret_cast(&gpt_header), gpt_header.header_size)) + { + std::cerr << m_path << " has non-matching header crc32" << std::endl; + return false; + } + + const std::size_t partition_array_size = gpt_header.number_of_partition_entries * gpt_header.size_of_partition_entry; + if (gpt_header.partition_entry_array_crc32 != crc32_checksum(m_mmap + gpt_header.partition_entry_lba * SECTOR_SIZE, partition_array_size)) + { + std::cerr << m_path << " has non-matching partition entry crc32" << std::endl; + return false; + } + + return true; +} diff --git a/bootloader/installer/GPT.h b/bootloader/installer/GPT.h new file mode 100644 index 0000000000..7e07f5f809 --- /dev/null +++ b/bootloader/installer/GPT.h @@ -0,0 +1,88 @@ +#pragma once + +#include "GUID.h" + +#include +#include +#include +#include +#include +#include + +struct MBRPartitionRecord +{ + uint8_t boot_indicator; + uint8_t starting_chs[3]; + uint8_t os_type; + uint8_t ending_chs[3]; + uint32_t starting_lba; + uint32_t size_in_lba; +} __attribute__((packed)); + +struct MBR +{ + uint8_t boot_code[440]; + uint32_t unique_mbr_disk_signature; + uint16_t unknown; + MBRPartitionRecord partition_records[4]; + uint16_t signature; +} __attribute__((packed)); +static_assert(sizeof(MBR) == 512); + +struct GPTPartitionEntry +{ + GUID type_guid; + GUID partition_guid; + uint64_t starting_lba; + uint64_t ending_lba; + uint64_t attributes; + uint16_t name[36]; +}; +static_assert(sizeof(GPTPartitionEntry) == 128); + +struct GPTHeader +{ + char signature[8]; + uint32_t revision; + uint32_t header_size; + uint32_t header_crc32; + uint32_t reserved; + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + GUID disk_guid; + 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)); +static_assert(sizeof(GPTHeader) == 92); + +class GPTFile +{ +public: + GPTFile(std::string_view path); + ~GPTFile(); + + bool install_bootcode(std::span); + bool write_partition(std::span, GUID type_guid); + + const GPTHeader& gpt_header() const; + + bool success() const { return m_success; } + + std::string_view path() const { return m_path; } + +private: + MBR& mbr(); + bool validate_gpt_header() const; + std::optional find_partition(const GUID& type_guid) const; + +private: + const std::string m_path; + bool m_success { false }; + int m_fd { -1 }; + struct stat m_stat { }; + uint8_t* m_mmap { nullptr }; +}; diff --git a/bootloader/installer/GUID.cpp b/bootloader/installer/GUID.cpp new file mode 100644 index 0000000000..fab512626c --- /dev/null +++ b/bootloader/installer/GUID.cpp @@ -0,0 +1,26 @@ +#include "GUID.h" + +#include +#include + +bool GUID::operator==(const GUID& other) const +{ + return std::memcmp(this, &other, sizeof(GUID)) == 0; +} + +std::ostream& operator<<(std::ostream& out, const GUID& guid) +{ + auto flags = out.flags(); + out << std::hex << std::setfill('0'); + out << std::setw(8) << guid.component1 << '-'; + out << std::setw(4) << guid.component2 << '-'; + out << std::setw(4) << guid.component3 << '-'; + + out << std::setw(2); + for (int i = 0; i < 2; i++) out << +guid.component45[i]; + out << '-'; + for (int i = 2; i < 6; i++) out << +guid.component45[i]; + + out.flags(flags); + return out; +} diff --git a/bootloader/installer/GUID.h b/bootloader/installer/GUID.h new file mode 100644 index 0000000000..0f95a4bb5d --- /dev/null +++ b/bootloader/installer/GUID.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +struct GUID +{ + uint32_t component1; + uint16_t component2; + uint16_t component3; + // last 2 components are combined so no packed needed + uint8_t component45[8]; + + bool operator==(const GUID& other) const; +}; + +std::ostream& operator<<(std::ostream& out, const GUID& guid); + +// unused 00000000-0000-0000-0000-000000000000 +static constexpr GUID unused_guid = { + 0x00000000, + 0x0000, + 0x0000, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + +// bios boot 21686148-6449-6E6F-744E-656564454649 +static constexpr GUID bios_boot_guid = { + 0x21686148, + 0x6449, + 0x6E6F, + { 0x74, 0x4E, 0x65, 0x65, 0x64, 0x45, 0x46, 0x49 } +}; diff --git a/bootloader/installer/build.sh b/bootloader/installer/build.sh new file mode 100755 index 0000000000..719fb30ee5 --- /dev/null +++ b/bootloader/installer/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +g++ -O2 -std=c++20 main.cpp crc32.cpp ELF.cpp GPT.cpp GUID.cpp -o install-bootloader diff --git a/bootloader/installer/crc32.cpp b/bootloader/installer/crc32.cpp new file mode 100644 index 0000000000..92c360466d --- /dev/null +++ b/bootloader/installer/crc32.cpp @@ -0,0 +1,80 @@ +#include "crc32.h" + +static constexpr uint32_t crc32_table[256] = +{ + 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, +}; + +uint32_t crc32_checksum(const uint8_t* data, std::size_t count) +{ + uint32_t crc32 = 0xFFFFFFFF; + for (size_t i = 0; i < count; i++) + { + uint8_t index = (crc32 ^ data[i]) & 0xFF; + crc32 = (crc32 >> 8) ^ crc32_table[index]; + } + return crc32 ^ 0xFFFFFFFF; +} diff --git a/bootloader/installer/crc32.h b/bootloader/installer/crc32.h new file mode 100644 index 0000000000..4cc51c2d94 --- /dev/null +++ b/bootloader/installer/crc32.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +uint32_t crc32_checksum(const uint8_t* data, std::size_t count); diff --git a/bootloader/installer/main.cpp b/bootloader/installer/main.cpp new file mode 100644 index 0000000000..696b338c41 --- /dev/null +++ b/bootloader/installer/main.cpp @@ -0,0 +1,41 @@ +#include "ELF.h" +#include "GPT.h" + +#include + +int main(int argc, char** argv) +{ + using namespace std::string_view_literals; + + if (argc != 3) + { + std::fprintf(stderr, "usage: %s BOOTLOADER DISK_IMAGE}\n", argv[0]); + return 1; + } + + ELFFile bootloader(argv[1]); + if (!bootloader.success()) + return 1; + + auto stage1 = bootloader.find_section(".stage1"sv); + auto stage2 = bootloader.find_section(".stage2"sv); + if (!stage1.has_value() || !stage2.has_value()) + { + std::cerr << bootloader.path() << " doesn't contain .stage1 and .stage2 sections" << std::endl; + return 1; + } + + GPTFile disk_image(argv[2]); + if (!disk_image.success()) + return 1; + + if (!disk_image.install_bootcode(*stage1)) + return 1; + std::cout << "wrote stage1 bootloader" << std::endl; + + if (!disk_image.write_partition(*stage2, bios_boot_guid)) + return 1; + std::cout << "wrote stage2 bootloader" << std::endl; + + return 0; +} \ No newline at end of file