commit 52c96a7d887114351dd34075f6cfa48fd8b19fa2 Author: bzt Date: Tue Jun 5 20:34:03 2018 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..402798b --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Debug files +*.dSYM/ +*.su +aarch64-rpi/LIC* +aarch64-rpi/*.bin +aarch64-rpi/*.dat +aarch64-rpi/*.elf diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ac146d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ + Copyright (C) 2017 bzt (bztsrc@gitlab) + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..41cc836 --- /dev/null +++ b/README.md @@ -0,0 +1,390 @@ +BOOTBOOT Reference Implementations +================================== + +I provide pre-compiled images ready for use. + +1. *x86_64-efi* the preferred way of booting on x86_64 architecture. + Standard GNU toolchain and a few files from gnuefi (included). + [bootboot.efi](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.efi?raw=true) (76k), [bootboot.rom](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.rom?raw=true) (76k) + +2. *x86_64-bios* BIOS and Multiboot (GRUB) compatible, OBSOLETE loader. + If you want to recompile this, you'll need fasm (not included). + [boot.bin](https://gitlab.com/bztsrc/bootboot/blob/master/boot.bin?raw=true) (512 bytes, works as MBR and VBR too), [bootboot.bin](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.bin?raw=true) (8k) + +3. *aarch64-rpi* ARMv8 boot loader for Raspberry Pi 3 + [bootboot.img](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.img?raw=true) (27k) + +4. *mykernel* an example BOOTBOOT [compatible kernel](https://gitlab.com/bztsrc/bootboot/blob/master/mykernel) in C which draws lines and boxes + +Please note that the reference implementations do not support the full protocol at level 2, +they only handle static mappings which makes them level 1 loaders. + +BOOTBOOT Protocol +================= + +Rationale +--------- + +The protocol describes how to boot an ELF64 or PE32+ executable inside an initial ram disk image +into clean 64 bit mode, without using any configuration or even knowing the file system of initrd. + +On [BIOS](https://gitlab.com/bztsrc/bootboot/tree/master/x86_64-bios) based systems, the same image can be loaded via +Multiboot, chainload from MBR or VBR (GPT hybrid booting) or run as a BIOS Expansion ROM +(so not only the ramdisk can be in ROM, but the loader as well). + +On [UEFI machines](https://gitlab.com/bztsrc/bootboot/tree/master/x86_64-efi), it is a standard EFI OS Loader application. + +On [Raspberry Pi 3](https://gitlab.com/bztsrc/bootboot/tree/master/aarch64-rpi) board the bootboot.img +is loaded from the boot partition on SD card as kernel8.img by start.elf. + +The difference to other booting protocols is flexibility and portability; +only clean 64 bit support; and that BOOTBOOT expects the kernel to fit inside the +initial ramdisk. This is ideal for hobby OSes and micro-kernels. The advantage it gaves is that your kernel +can be splitted up into several files and yet they will be loaded together +as if it were a monolitic kernel. And you can use your own file system for the initrd. + +Note: BOOTBOOT is not a boot manager, it's a boot loader protocol. If you want an interactive boot menu, you should +integrate that *before* a BOOTBOOT compatible loader is called. Like GRUB chainloading boot.bin (or loading bootboot.bin as a +kernel) or adding bootboot.efi to UEFI Boot Manager's menu for example. + +Licence +------- + +The BOOTBOOT Protocol as well as the reference implementations are free +software and licensed under the terms of MIT licence. + +``` + Copyright (C) 2017 bzt (bztsrc@gitlab) + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, copy, + modify, merge, publish, distribute, sublicense, and/or sell copies + of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +``` + +Authors +------- + +efirom: Michael Brown + +zlib: Mark Adler + +tinflate: Joergen Ibsen + +raspbootcom: (GPL) Goswin von Brederlow + +BOOTBOOT, FS/Z: bzt + +Glossary +-------- + +* _boot partition_: the first bootable partition of the first bootable disk, + a rather small one. Most likely an EFI System Partition with FAT, but can be + any other partition as well if the partition is bootable (bit 2 set in flags). + +* _environment file_: a maximum one page frame long, utf-8 [file](https://gitlab.com/bztsrc/bootboot/blob/master/README.md#environment-file) on boot partition + at `BOOTBOOT\CONFIG` (or when your initrd is on the entire partition, `/sys/config`). It + has "key=value" pairs (separated by newlines). The protocol + only specifies two of the keys: "screen" for screen size, + and "kernel" for the name of the executable inside the initrd. + +* _initrd_: initial [ramdisk image](https://gitlab.com/bztsrc/bootboot/blob/master/README.md#installation) + (probably in ROM or flash, or on a GPT boot partition at BOOTBOOT\INITRD, or it can occupy the whole partition, or can be loaded + over the network). It's format and whereabouts are not specified (the good part :-) ) and can be optionally gzip compressed. + +* _loader_: a native executable on the boot partition or in ROM. For multi-bootable disks + more loader implementations can co-exists. + +* _file system driver_: a separated function that parses initrd for the kernel file. + Without one the first executable found will be loaded. + +* _kernel file_: an ELF64 / PE32+ [executable inside initrd](https://gitlab.com/bztsrc/bootboot/blob/master/mykernel), + optionally with the following symbols: `fb`, `environment`, `bootboot` (see machine state and linker script). + +* _BOOTBOOT structure_: an informational structure defined in [bootboot.h](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.h). + +Boot process +------------ + +1. the firmware locates the loader, loads it and passes control to it. +2. the loader initializes hardware (64 bit mode, screen resolution, memory map etc.) +3. then loads environment file and initrd file (probably from the boot partition or from ROM). +4. iterates on file system drivers, and loads kernel file from initrd. +5. if file system is not recognized, scans for the first executable in the initrd. +6. parses executable header and symbols to get link addresses (only level 2 compatible loaders). +7. maps linear framebuffer, environment and [bootboot structure](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.h) accordingly. +8. sets up stack, registers and jumps to kernel entry point. See [example kernel](https://gitlab.com/bztsrc/bootboot/blob/master/mykernel). + +Machine state +------------- + +When the kernel gains control, the memory mapping looks like this: + +``` + -64M "fb" framebuffer (0xFFFFFFFFFC000000) + -2M "bootboot" structure (0xFFFFFFFFFFE00000) + -2M+1page "environment" string (0xFFFFFFFFFFE01000) + -2M+2page.. code segment v (0xFFFFFFFFFFE02000) + ..0 stack ^ (0x0000000000000000) + 0-16G RAM identity mapped (0x0000000400000000) +``` + +All infomration is passed at linker defined addresses. No API required at all, therefore the BOOTBOOT Protocol is +totally architecture and ABI agnostic. Level 1 expects these symbols at pre-defined addresses, level 2 loaders +parse the symbol table in executable to get the actual addresses. + +The RAM (up to 16G) is identity mapped in the positive address range. Interrups are turned off and code is running +in supervisor mode. + +The screen is properly set up with a 32 bit packed pixel linear framebuffer, mapped at the negative address defined by +the `fb` symbol. Level 1 loaders limit the framebuffer size somewhere around 4096 x 4096 pixels (depends on scanline size +and aspect ratio too). That's more than enough for [Ultra HD 4K](https://en.wikipedia.org/wiki/4K_resolution) +(3840 x 2160). Level 2 loaders can place the fb anywhere in memory therefore they do not have such a limitation. + +The main information [bootboot structure](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot.h) is mapped +at `bootboot` symbol. It consist of a fixed 128 bytes long header followed by various number of fixed +records. Your initrd (with the additional kernel modules and servers) is enitrely in the memory, and you can locate it +using this struct's *initrd_ptr* and *initrd_size* members. The physical address of the framebuffer can be found in +the *fb_ptr* field. The *boot time* and a platform independent *memory map* are also provided. + +The configuration string (or command line if you like) is mapped at `environment` symbol. + +Kernel's code segment is mapped at ELF header's `p_vaddr` or PE header's `code_base` (level 2 only). Level 1 loaders +load kernels at -2M, therefore limiting the kernel's size in 2M, including configuration, data, bss and stack. That +must be more than enough for all micro-kernels. + +The stack is at the top of the memory, starting at zero and growing downwards. + +Environment file +---------------- + +Configuration is passed to your kernel as newline separated, zero terminated UTF-8 string with "key=value" pairs. + +``` +// BOOTBOOT Options + +// --- Loader specific --- +// requested screen dimension. If not given, autodetected +screen=800x600 +// elf or pe binary to load inside initrd +kernel=sys/core + +// --- Kernel specific, you're choosing --- +anythingyouwant=somevalue +otherstuff=enabled +somestuff=100 +someaddress=0xA0000 +``` + +That cannot be larger than a page size (4096 bytes). Temporary variables will be appended at the end (from +UEFI command line). C style single line and multi line comments can be used. BOOTBOOT protocol only uses `screen` and +`kernel` keys, all the others and their values are up to your kernel (or drivers) to parse. Be creative :-) + +To modify the environment, one will need to insert the disk into another machine (or boot a simple OS like DOS) and edit +BOOTBOOT\CONFIG on the boot partition. With UEFI, you can use the `edit` command provided by the EFI Shell or append +"key=value" pairs on the command line (value specified on command line takes precedence over the one in the file). + +File system drivers +------------------- + +The file system of the boot partition and how initrd is loaded from it is out of the scope of this specification: +the BOOTBOOT Protocol only states that a compatible loader must be able to load initrd and the environment file, +but does not describe how or from where. They can be loaded from nvram, ROM or over +network for example, it does not matter. + +On the other hand BOOTBOOT does specify one API function to locate a file (the kernel) +inside the initrd image, but the ABI is also implementation (and architecture) specific. +This function receives a pointer to initrd in memory as well as the kernel's filename, and +returns a pointer to the first byte of the kernel and it's size. On error (if file system is +not recognized or the kernel file is not found) returns {NULL,0}. Plain simple. + +```c +typedef struct { + uint8_t *ptr; + uint64_t size; +} file_t; + +file_t myfs_initrd(uint8_t *initrd, char *filename); +``` + +The protocol expects that a BOOTBOOT compliant loader iterates on a list of drivers until one +returns a valid result. If all file system drivers returned {NULL,0}, the loader will brute-force +scan for the first ELF64 / PE32+ image in the initrd. This feature is quite comfortable when you +want to use your own file system but you don't have written an fs driver for it yet, or when your +"initrd" is a single, statically linked executable, like the Minix kernel. You just copy +your initrd on the boot partition, and you're ready to rock and roll! + +The BOOTBOOT Protocol expects the file system drivers ([here](https://gitlab.com/bztsrc/bootboot/blob/master/x86_64-efi/fs.h), +[here](https://gitlab.com/bztsrc/bootboot/blob/master/x86_64-bios/fs.inc) and [here](https://gitlab.com/bztsrc/bootboot/blob/master/aarch64-rpi/fs.h)) +to be separated from the rest of the loader's source. This is so because it was designed to help the needs of hobby +OS developers, specially for those who want to write their own file systems. + +The reference implementations support [cpio](https://en.wikipedia.org/wiki/Cpio) (all hpodc, newc and crc variants), +[ustar](https://en.wikipedia.org/wiki/Tar_(computing)), osdev.org's [SFS](http://wiki.osdev.org/SFS), +[James Molloy's initrd](http://www.jamesmolloy.co.uk/tutorial_html/8.-The%20VFS%20and%20the%20initrd.html) +format and OS/Z's native [FS/Z](https://gitlab.com/bztsrc/osz/blob/master/etc/include/fsZ.h). +Gzip compressed initrds also supported to save disk space and fasten up load time (not recommended on RPi3). + +Example kernel +-------------- + +An [example kernel](https://gitlab.com/bztsrc/bootboot/blob/master/mykernel/kernel.c) is included with BOOTBOOT Protocol +to demostrate how to access the environment: + +```c +#include + +/* imported virtual addresses, see linker script below */ +extern BOOTBOOT bootboot; // see bootboot.h +extern unsigned char *environment; // configuration, UTF-8 text key=value pairs +extern uint8_t fb; // linear framebuffer mapped + +void _start() +{ + int x, y, s=bootboot.fb_scanline, w=bootboot.fb_width, h=bootboot.fb_height; + + // cross-hair to see screen dimension detected correctly + for(y=0;y ../INITRD +find . | cpio -H crc -o | gzip > ../INITRD +find . | cpio -H hpodc -o | gzip > ../INITRD +tar -czf ../INITRD * +mkfs ../INITRD . +``` + +2. Create FS0:\BOOTBOOT directory on the boot partition, and copy the image you've created + into it. If you want, create a text file named CONFIG there too, and put your + environment variables there. If you use a different name than "sys/core" for your + kernel, specify "kernel=" in it. + +Alternatively you can copy an uncompressed INITRD into the whole partition using your fs only, leaving FAT file system entirely out. +You can also create an Option ROM out of INITRD (on BIOS there's not much space ~64-96k, but on EFI it can be 16M). + +3. copy the BOOTBOOT loader on the boot partition. + +3.1. *UEFI disk*: copy __bootboot.efi__ to **_FS0:\EFI\BOOT\BOOTX64.EFI_**. + +3.2. *BIOS disk*: copy __bootboot.bin__ to **_FS0:\BOOTBOOT\LOADER_**. + +3.3. *Raspberry Pi 3*: copy __bootboot.img__ to **_FS0:\KERNEL8.IMG_**. + +**IMPORTANT**: see the relevant port's README.md for more details. + +Troubleshooting +--------------- + +``` +BOOTBOOT-PANIC: LBA support not found +``` + +Really old hardware. Your BIOS does not support LBA. This message is generated by 1st stage loader (boot.bin). + +``` +BOOTBOOT-PANIC: FS0:\BOOTBOOT\LOADER not found +``` + +The loader (bootboot.bin) is not on the disk or it's starting LBA address is not recorded in the boot sector at dword [0x1B0] +(see [mkboot](https://gitlab.com/bztsrc/bootboot/blob/master/x86_64-bios/mkboot.c)). As the boot sector supports RAID mirror, +it will try to load the loader from other drives as well. This message is generated by 1st stage loader (boot.bin). + +``` +BOOTBOOT-PANIC: Hardware not supported +``` + +Really old hardware. On x86_64, your CPU is older than family 6.0 or PAE, MSR, LME features not supported. +On AArch64 it means the MMU does not support 4k granule size, at least 36 bit address size or the system timer +(0x3F003000) is not available. + + +``` +BOOTBOOT-PANIC: Unable to initialize SDHC card +``` + +The loader was unable to initialize EMMC for SDHC card access, probably hardware error or old card. + +``` +BOOTBOOT-PANIC: No boot partition +``` + +Either the disk does not have a GPT, or there's no EFI System Partition nor any other bootable +partition on it. Or the FAT file system is found but inconsistent, or doesn't have a BOOTBOOT directory. + +``` +BOOTBOOT-PANIC: INITRD not found +``` + +The loader could not find the initial ramdisk image on the boot partition. This message will be shown +even if you specify an alternative initrd file on EFI command line. + +``` +BOOTBOOT-PANIC: Kernel not found in initrd +``` + +Kernel is not included in the initrd, or initrd's fileformat is not recognized by any of the file system +drivers and scanning haven't found a valid executable header in it. + +``` +BOOTBOOT-PANIC: Kernel is not a valid executable +``` + +The file that was specified as kernel could be loaded by fs drivers, but it's not an ELF64 or PE32+, +does not match the architecture, or does not have any program header with a loadable segment (p_vaddr or core_base) +in the negative range (see linker script). This error is also shown by level 2 loaders if the address of `fb`, +`bootboot` and `environment` symbols are not in the negative range. + +``` +BOOTBOOT-PANIC: GOP failed, no framebuffer +BOOTBOOT-PANIC: VESA VBE error, no framebuffer +BOOTBOOT-PANIC: VideoCore error, no framebuffer +``` + +The first part of the message varies on different platforms. It means that the loader was unable to set up linear +framebuffer with packed 32 bit pixels in the requested resolution. Possible solution is to modify screen to +`screen=800x600` or `screen=1024x768` in environment. + +That's all, hope it will be useful! diff --git a/aarch64-rpi/Makefile b/aarch64-rpi/Makefile new file mode 100644 index 0000000..59ad724 --- /dev/null +++ b/aarch64-rpi/Makefile @@ -0,0 +1,27 @@ +all: boot.S bootboot.c fs.h + @echo " src aarch64-rpi (Raspberry Pi 3+)" + @aarch64-elf-gcc -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -c boot.S -o boot.o + @aarch64-elf-gcc -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -I ./ -c tinflate.c -o tinflate.o + @aarch64-elf-gcc -Wall -O2 -ffreestanding -nostdinc -nostdlib -nostartfiles -I ./ -c bootboot.c -o bootboot.o + @aarch64-elf-ld -r -b binary -o font.o font.psf + @aarch64-elf-ld -nostdlib -nostartfiles boot.o bootboot.o tinflate.o font.o -T link.ld -o bootboot.elf + @aarch64-elf-objcopy -O binary bootboot.elf ../bootboot.img + @rm *.o bootboot.elf + +mkboot: mkboot.c + gcc mkboot.c -o mkboot + +raspbootcom: raspbootcom.c + gcc raspbootcom.c -o raspbootcom + +getfw: + wget -q https://raw.githubusercontent.com/raspberrypi/firmware/master/boot/LICENCE.broadcom + wget -q https://raw.githubusercontent.com/raspberrypi/firmware/master/boot/bootcode.bin + wget -q https://raw.githubusercontent.com/raspberrypi/firmware/master/boot/fixup.dat + wget -q https://raw.githubusercontent.com/raspberrypi/firmware/master/boot/start.elf + +cleanfw: + @rm LICENCE.broadcom bootcode.bin fixup.dat start.elf + +clean: + @rm mkboot raspbootcom *.o >/dev/null 2>/dev/null || true diff --git a/aarch64-rpi/README.md b/aarch64-rpi/README.md new file mode 100644 index 0000000..d61c920 --- /dev/null +++ b/aarch64-rpi/README.md @@ -0,0 +1,49 @@ +BOOTBOOT Raspberry Pi 3 Implementation +====================================== + +See [BOOTBOOT Protocol](https://gitlab.com/bztsrc/bootboot) for common details. + +On [Raspberry Pi 3](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/sdcard.md) board the bootboot.img +is loaded from the boot (or firmware) partition on SD card as kernel8.img by start.elf. For separating firmware and boot +partitions see [documentation](https://gitlab.com/bztsrc/bootboot/blob/master/bootboot_spec_1st_ed.pdf). + +Machine state +------------- + +In addition to standard mappings, the MMIO is also mapped in kernel space: + +``` + -128M MMIO (0xFFFFFFFFF8000000) +``` + +Code is running in supervisor mode, at EL1. Dummy exception handlers are installed, but your kernel should use it's own +handlers as soon as possible. + +File system drivers +------------------- + +For boot partition, RPi3 version expects FAT16 or FAT32 file systems (if the +initrd is a file and does not occupy the whole boot partition). The initrd can also be loaded over serial line, +running [raspbootcom](https://gitlab.com/bztsrc/bootboot/blob/master/aarch64-rpi/raspbootcom.c) on a remote machine. + +Gzip compression is not recommended as reading from SD card is considerably faster than uncompressing. + +Installation +------------ + +1. Copy __bootboot.img__ to **_FS0:\KERNEL8.IMG_**. + +2. You'll need other [firmware files](https://gitlab.com/raspberrypi/firmware/tree/master/boot) as well. + +3. If you have used a GPT disk with ESP as boot partition, then you need to map it in MBR so that Raspberry Pi + firmware could find those files. The [mkboot](https://gitlab.com/bztsrc/bootboot/blob/master/aarch64-rpi/mkboot.c) + utility will do that for you. + +Limitations +----------- + + - Initrd in ROM is not possible + - Maps only the first 1G of RAM. + - Cards other than SDHC Class 10 not supported. + - Raspberry Pi does not have an on-board RTC, so always 0000-00-00 00:00:00 returned as bootboot.datetime. + diff --git a/aarch64-rpi/boot.S b/aarch64-rpi/boot.S new file mode 100644 index 0000000..7b88867 --- /dev/null +++ b/aarch64-rpi/boot.S @@ -0,0 +1,154 @@ +/* + * aarch64-rpi/boot.S + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief Boot loader for the Raspberry Pi 3+ ARMv8 + * + */ +.section ".text.boot" + +.global _start +.global jumptokernel + +/********************************************************************* + * Entry point called by start.elf * + *********************************************************************/ +_start: + // magic + b 1f + .ascii "BOOTBOOT" + // read cpu id, stop slave cores +1: mrs x7, mpidr_el1 + and x7, x7, #3 + cbz x7, 2f +1: wfe + b 1b +2: // set stack before our code + ldr x1, =_start + // set up EL1 + mrs x0, CurrentEL + and x0, x0, #12 + // running at EL3? + cmp x0, #12 + bne 1f + mov x2, #0x5b1 + msr scr_el3, x2 + mov x2, #0x3c9 + msr spsr_el3, x2 + adr x2, 1f + msr elr_el3, x2 + eret + // running at EL2? +1: cmp x0, #4 + beq 1f + msr sp_el1, x1 + // set up exception handlers + ldr x1, =_vectors + msr vbar_el2, x1 + // enable CNTP for EL1 + mrs x0, cnthctl_el2 + orr x0, x0, #3 + msr cnthctl_el2, x0 + msr cntvoff_el2, xzr + // initialize virtual MPIDR + mrs x0, midr_el1 + mrs x2, mpidr_el1 + msr vpidr_el2, x0 + msr vmpidr_el2, x2 + // disable coprocessor traps + mov x0, #0x33FF + msr cptr_el2, x0 + msr hstr_el2, xzr + mov x0, #(3 << 20) + msr cpacr_el1, x0 + // enable AArch64 in EL1 + mov x0, #(1 << 31) // AArch64 + orr x0, x0, #(1 << 1) // SWIO hardwired on Pi3 + msr hcr_el2, x0 + mrs x0, hcr_el2 + // Setup SCTLR access + mov x2, #0x0800 + movk x2, #0x30d0, lsl #16 + msr sctlr_el1, x2 + // change exception level to EL1 + mov x2, #0x3c4 + msr spsr_el2, x2 + adr x2, 1f + msr elr_el2, x2 + eret +1: // clear bss + ldr x2, =__bss_start + ldr w3, =__bss_size +1: cbz w3, 2f + str xzr, [x2], #8 + sub w3, w3, #1 + cbnz w3, 1b +2: mov sp, x1 + // set up exception handlers + ldr x1, =_vectors + msr vbar_el1, x1 + // jump to C code + bl bootboot_main +1: wfe + b 1b + + .align 11 +_vectors: + .align 7 + mov x0, #0 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + mrs x5, sctlr_el1 + mrs x6, tcr_el1 + b uart_exc + .align 7 + mov x0, #1 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + mrs x5, sctlr_el1 + mrs x6, tcr_el1 + b uart_exc + .align 7 + mov x0, #2 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + mrs x5, sctlr_el1 + mrs x6, tcr_el1 + b uart_exc + .align 7 + mov x0, #3 + mrs x1, esr_el1 + mrs x2, elr_el1 + mrs x3, spsr_el1 + mrs x4, far_el1 + mrs x5, sctlr_el1 + mrs x6, tcr_el1 + b uart_exc diff --git a/aarch64-rpi/bootboot.c b/aarch64-rpi/bootboot.c new file mode 100644 index 0000000..06b9ae6 --- /dev/null +++ b/aarch64-rpi/bootboot.c @@ -0,0 +1,1574 @@ +/* + * aarch64-rpi/bootboot.c + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief Boot loader for the Raspberry Pi 3+ ARMv8 + * + */ +#define DEBUG 1 +//#define SD_DEBUG DEBUG +//#define INITRD_DEBUG DEBUG +//#define EXEC_DEBUG DEBUG +//#define MEM_DEBUG DEBUG + +#define CONSOLE UART0 + +#define NULL ((void*)0) +#define PAGESIZE 4096 + +#include "tinf.h" + +/* get BOOTBOOT structure */ +#include "../bootboot.h" + +/* aligned buffers */ +volatile uint32_t __attribute__((aligned(16))) mbox[36]; +/* we place these manually in linker script, gcc would otherwise waste lots of memory */ +volatile uint8_t __attribute__((aligned(PAGESIZE))) __bootboot[PAGESIZE]; +volatile uint8_t __attribute__((aligned(PAGESIZE))) __environment[PAGESIZE]; +volatile uint8_t __attribute__((aligned(PAGESIZE))) __paging[23*PAGESIZE]; +volatile uint8_t __attribute__((aligned(PAGESIZE))) __corestack[PAGESIZE]; +#define __diskbuf __paging +extern volatile uint8_t _data; +extern volatile uint8_t _end; + +/* forward definitions */ +uint32_t color=0xC0C0C0; +void putc(char c); +void puts(char *s); + +/*** ELF64 defines and structs ***/ +#define ELFMAG "\177ELF" +#define SELFMAG 4 +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define PT_LOAD 1 /* Loadable program segment */ +#define EM_AARCH64 183 /* ARM aarch64 architecture */ + +typedef struct +{ + unsigned char e_ident[16];/* Magic number and other info */ + uint16_t e_type; /* Object file type */ + uint16_t e_machine; /* Architecture */ + uint32_t e_version; /* Object file version */ + uint64_t e_entry; /* Entry point virtual address */ + uint64_t e_phoff; /* Program header table file offset */ + uint64_t e_shoff; /* Section header table file offset */ + uint32_t e_flags; /* Processor-specific flags */ + uint16_t e_ehsize; /* ELF header size in bytes */ + uint16_t e_phentsize; /* Program header table entry size */ + uint16_t e_phnum; /* Program header table entry count */ + uint16_t e_shentsize; /* Section header table entry size */ + uint16_t e_shnum; /* Section header table entry count */ + uint16_t e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +typedef struct +{ + uint32_t p_type; /* Segment type */ + uint32_t p_flags; /* Segment flags */ + uint64_t p_offset; /* Segment file offset */ + uint64_t p_vaddr; /* Segment virtual address */ + uint64_t p_paddr; /* Segment physical address */ + uint64_t p_filesz; /* Segment size in file */ + uint64_t p_memsz; /* Segment size in memory */ + uint64_t p_align; /* Segment alignment */ +} Elf64_Phdr; + +/*** PE32+ defines and structs ***/ +#define MZ_MAGIC 0x5a4d /* "MZ" */ +#define PE_MAGIC 0x00004550 /* "PE\0\0" */ +#define IMAGE_FILE_MACHINE_ARM64 0xaa64 /* ARM aarch64 architecture */ +#define PE_OPT_MAGIC_PE32PLUS 0x020b /* PE32+ format */ +typedef struct +{ + uint16_t magic; /* MZ magic */ + uint16_t reserved[29]; /* reserved */ + uint32_t peaddr; /* address of pe header */ +} mz_hdr; + +typedef struct { + uint32_t magic; /* PE magic */ + uint16_t machine; /* machine type */ + uint16_t sections; /* number of sections */ + uint32_t timestamp; /* time_t */ + uint32_t sym_table; /* symbol table offset */ + uint32_t symbols; /* number of symbols */ + uint16_t opt_hdr_size; /* size of optional header */ + uint16_t flags; /* flags */ + uint16_t file_type; /* file type, PE32PLUS magic */ + uint8_t ld_major; /* linker major version */ + uint8_t ld_minor; /* linker minor version */ + uint32_t text_size; /* size of text section(s) */ + uint32_t data_size; /* size of data section(s) */ + uint32_t bss_size; /* size of bss section(s) */ + int32_t entry_point; /* file offset of entry point */ + int32_t code_base; /* relative code addr in ram */ +} pe_hdr; + +/*** Raspberry Pi specific defines ***/ +#define MMIO_BASE 0x3F000000 + +#define PM_RTSC ((volatile uint32_t*)(MMIO_BASE+0x0010001c)) +#define PM_WATCHDOG ((volatile uint32_t*)(MMIO_BASE+0x00100024)) +#define PM_WDOG_MAGIC 0x5a000000 +#define PM_RTSC_FULLRST 0x00000020 + +#define GPFSEL0 ((volatile uint32_t*)(MMIO_BASE+0x00200000)) +#define GPFSEL1 ((volatile uint32_t*)(MMIO_BASE+0x00200004)) +#define GPFSEL2 ((volatile uint32_t*)(MMIO_BASE+0x00200008)) +#define GPFSEL3 ((volatile uint32_t*)(MMIO_BASE+0x0020000C)) +#define GPFSEL4 ((volatile uint32_t*)(MMIO_BASE+0x00200010)) +#define GPFSEL5 ((volatile uint32_t*)(MMIO_BASE+0x00200014)) +#define GPSET0 ((volatile uint32_t*)(MMIO_BASE+0x0020001C)) +#define GPSET1 ((volatile uint32_t*)(MMIO_BASE+0x00200020)) +#define GPCLR0 ((volatile uint32_t*)(MMIO_BASE+0x00200028)) +#define GPLEV0 ((volatile uint32_t*)(MMIO_BASE+0x00200034)) +#define GPLEV1 ((volatile uint32_t*)(MMIO_BASE+0x00200038)) +#define GPEDS0 ((volatile uint32_t*)(MMIO_BASE+0x00200040)) +#define GPEDS1 ((volatile uint32_t*)(MMIO_BASE+0x00200044)) +#define GPHEN0 ((volatile uint32_t*)(MMIO_BASE+0x00200064)) +#define GPHEN1 ((volatile uint32_t*)(MMIO_BASE+0x00200068)) +#define GPPUD ((volatile uint32_t*)(MMIO_BASE+0x00200094)) +#define GPPUDCLK0 ((volatile uint32_t*)(MMIO_BASE+0x00200098)) +#define GPPUDCLK1 ((volatile uint32_t*)(MMIO_BASE+0x0020009C)) + +#define UART0 0 +#define UART0_DR ((volatile uint32_t*)(MMIO_BASE+0x00201000)) +#define UART0_FR ((volatile uint32_t*)(MMIO_BASE+0x00201018)) +#define UART0_IBRD ((volatile uint32_t*)(MMIO_BASE+0x00201024)) +#define UART0_FBRD ((volatile uint32_t*)(MMIO_BASE+0x00201028)) +#define UART0_LCRH ((volatile uint32_t*)(MMIO_BASE+0x0020102C)) +#define UART0_CR ((volatile uint32_t*)(MMIO_BASE+0x00201030)) +#define UART0_IMSC ((volatile uint32_t*)(MMIO_BASE+0x00201038)) +#define UART0_ICR ((volatile uint32_t*)(MMIO_BASE+0x00201044)) + +#define UART1 1 +#define AUX_ENABLE ((volatile uint32_t*)(MMIO_BASE+0x00215004)) +#define AUX_MU_IO ((volatile uint32_t*)(MMIO_BASE+0x00215040)) +#define AUX_MU_IER ((volatile uint32_t*)(MMIO_BASE+0x00215044)) +#define AUX_MU_IIR ((volatile uint32_t*)(MMIO_BASE+0x00215048)) +#define AUX_MU_LCR ((volatile uint32_t*)(MMIO_BASE+0x0021504C)) +#define AUX_MU_MCR ((volatile uint32_t*)(MMIO_BASE+0x00215050)) +#define AUX_MU_LSR ((volatile uint32_t*)(MMIO_BASE+0x00215054)) +#define AUX_MU_MSR ((volatile uint32_t*)(MMIO_BASE+0x00215058)) +#define AUX_MU_SCRATCH ((volatile uint32_t*)(MMIO_BASE+0x0021505C)) +#define AUX_MU_CNTL ((volatile uint32_t*)(MMIO_BASE+0x00215060)) +#define AUX_MU_STAT ((volatile uint32_t*)(MMIO_BASE+0x00215064)) +#define AUX_MU_BAUD ((volatile uint32_t*)(MMIO_BASE+0x00215068)) + +/* timing stuff */ +uint64_t cntfrq; +/* delay cnt clockcycles */ +void delay(uint32_t cnt) { while(cnt--) { asm volatile("nop"); } } +/* delay cnt microsec */ +void delaym(uint32_t cnt) {uint64_t t,r;asm volatile ("mrs %0, cntpct_el0" : "=r" (t)); + t+=((cntfrq/1000)*cnt)/1000;do{asm volatile ("mrs %0, cntpct_el0" : "=r" (r));}while(r=0;c-=4){n=(d>>c)&0xF;n+=n>9?0x37:0x30;uart_send(n);} } +void uart_putc(char c) { if(c=='\n') uart_send((uint32_t)'\r'); uart_send((uint32_t)c); } +void uart_puts(char *s) { while(*s) uart_putc(*s++); } +void uart_dump(void *ptr,uint32_t l) { + uint64_t a,b; + unsigned char c; + for(a=(uint64_t)ptr;a<(uint64_t)ptr+l*16;a+=16) { + uart_hex(a,8); uart_puts(": "); + for(b=0;b<16;b++) { + uart_hex(*((unsigned char*)(a+b)),1); + uart_putc(' '); + if(b%4==3) + uart_putc(' '); + } + for(b=0;b<16;b++) { + c=*((unsigned char*)(a+b)); + uart_putc(c<32||c>=127?'.':c); + } + uart_putc('\n'); + } +} +void uart_exc(uint64_t idx, uint64_t esr, uint64_t elr, uint64_t spsr, uint64_t far, uint64_t sctlr, uint64_t tcr) +{ + register uint64_t r; + asm volatile ("msr ttbr0_el1, %0;tlbi vmalle1" : : "r" ((uint64_t)&__paging+1)); + asm volatile ("dsb ish; isb; mrs %0, sctlr_el1" : "=r" (r)); + // set mandatory reserved bits + r&=~((1<<12) | // clear I, no instruction cache + (1<<2)); // clear C, no cache at all + asm volatile ("msr sctlr_el1, %0; isb" : : "r" (r)); + puts("\nBOOTBOOT-EXCEPTION"); + uart_puts(" #"); + uart_hex(idx,1); + uart_puts(":\n ESR_EL1 "); + uart_hex(esr,8); + uart_puts(" ELR_EL1 "); + uart_hex(elr,8); + uart_puts("\n SPSR_EL1 "); + uart_hex(spsr,8); + uart_puts(" FAR_EL1 "); + uart_hex(far,8); + uart_puts("\nSCTLR_EL1 "); + uart_hex(sctlr,8); + uart_puts(" TCR_EL1 "); + uart_hex(tcr,8); + uart_putc('\n'); + r=0; while(r!='\n' && r!=' ') r=uart_getc(); + asm volatile("dsb sy; isb"); + *PM_WATCHDOG = PM_WDOG_MAGIC | 1; + *PM_RTSC = PM_WDOG_MAGIC | PM_RTSC_FULLRST; + while(1); +} +#define VIDEOCORE_MBOX (MMIO_BASE+0x0000B880) +#define MBOX_READ ((volatile uint32_t*)(VIDEOCORE_MBOX+0x0)) +#define MBOX_POLL ((volatile uint32_t*)(VIDEOCORE_MBOX+0x10)) +#define MBOX_SENDER ((volatile uint32_t*)(VIDEOCORE_MBOX+0x14)) +#define MBOX_STATUS ((volatile uint32_t*)(VIDEOCORE_MBOX+0x18)) +#define MBOX_CONFIG ((volatile uint32_t*)(VIDEOCORE_MBOX+0x1C)) +#define MBOX_WRITE ((volatile uint32_t*)(VIDEOCORE_MBOX+0x20)) +#define MBOX_REQUEST 0 +#define MBOX_RESPONSE 0x80000000 +#define MBOX_FULL 0x80000000 +#define MBOX_EMPTY 0x40000000 +#define MBOX_CH_POWER 0 +#define MBOX_CH_FB 1 +#define MBOX_CH_VUART 2 +#define MBOX_CH_VCHIQ 3 +#define MBOX_CH_LEDS 4 +#define MBOX_CH_BTNS 5 +#define MBOX_CH_TOUCH 6 +#define MBOX_CH_COUNT 7 +#define MBOX_CH_PROP 8 + +/* mailbox functions */ +void mbox_write(uint8_t ch, volatile uint32_t *mbox) +{ + do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_FULL); + *MBOX_WRITE = (((uint32_t)((uint64_t)mbox)&~0xF) | (ch&0xF)); +} +uint32_t mbox_read(uint8_t ch) +{ + uint32_t r; + while(1) { + do{asm volatile("nop");}while(*MBOX_STATUS & MBOX_EMPTY); + r=*MBOX_READ; + if((uint8_t)(r&0xF)==ch) + return (r&~0xF); + } +} +uint8_t mbox_call(uint8_t ch, volatile uint32_t *mbox) +{ + mbox_write(ch,mbox); + return mbox_read(ch)==(uint32_t)((uint64_t)mbox) && mbox[1]==MBOX_RESPONSE; +} + +/* string.h */ +uint32_t strlen(unsigned char *s) { uint32_t n=0; while(*s++) n++; return n; } +void memcpy(void *dst, void *src, uint32_t n){uint8_t *a=dst,*b=src;while(n--) *a++=*b++; } +void memset(void *dst, uint8_t c, uint32_t n){uint8_t *a=dst;while(n--) *a++=c; } +int memcmp(void *s1, void *s2, uint32_t n){uint8_t *a=s1,*b=s2;while(n--){if(*a!=*b){return *a-*b;}a++;b++;} return 0; } +/* other string functions */ +int atoi(unsigned char *c) { int r=0;while(*c>='0'&&*c<='9') {r*=10;r+=*c++-'0';} return r; } +int oct2bin(unsigned char *s, int n){ int r=0;while(n-->0){r<<=3;r+=*s++-'0';} return r; } +int hex2bin(unsigned char *s, int n){ int r=0;while(n-->0){r<<=4; + if(*s>='0' && *s<='9')r+=*s-'0';else if(*s>='A'&&*s<='F')r+=*s-'A'+10;s++;} return r; } + +#if DEBUG +#define DBG(s) puts(s) +#else +#define DBG(s) +#endif + +/* sdcard */ +#define EMMC_ARG2 ((volatile uint32_t*)(MMIO_BASE+0x00300000)) +#define EMMC_BLKSIZECNT ((volatile uint32_t*)(MMIO_BASE+0x00300004)) +#define EMMC_ARG1 ((volatile uint32_t*)(MMIO_BASE+0x00300008)) +#define EMMC_CMDTM ((volatile uint32_t*)(MMIO_BASE+0x0030000C)) +#define EMMC_RESP0 ((volatile uint32_t*)(MMIO_BASE+0x00300010)) +#define EMMC_RESP1 ((volatile uint32_t*)(MMIO_BASE+0x00300014)) +#define EMMC_RESP2 ((volatile uint32_t*)(MMIO_BASE+0x00300018)) +#define EMMC_RESP3 ((volatile uint32_t*)(MMIO_BASE+0x0030001C)) +#define EMMC_DATA ((volatile uint32_t*)(MMIO_BASE+0x00300020)) +#define EMMC_STATUS ((volatile uint32_t*)(MMIO_BASE+0x00300024)) +#define EMMC_CONTROL0 ((volatile uint32_t*)(MMIO_BASE+0x00300028)) +#define EMMC_CONTROL1 ((volatile uint32_t*)(MMIO_BASE+0x0030002C)) +#define EMMC_INTERRUPT ((volatile uint32_t*)(MMIO_BASE+0x00300030)) +#define EMMC_INT_MASK ((volatile uint32_t*)(MMIO_BASE+0x00300034)) +#define EMMC_INT_EN ((volatile uint32_t*)(MMIO_BASE+0x00300038)) +#define EMMC_CONTROL2 ((volatile uint32_t*)(MMIO_BASE+0x0030003C)) +#define EMMC_SLOTISR_VER ((volatile uint32_t*)(MMIO_BASE+0x003000FC)) + +// command flags +#define CMD_NEED_APP 0x80000000 +#define CMD_RSPNS_48 0x00020000 +#define CMD_ERRORS_MASK 0xfff9c004 +#define CMD_RCA_MASK 0xffff0000 + +// COMMANDs +#define CMD_GO_IDLE 0x00000000 +#define CMD_ALL_SEND_CID 0x02010000 +#define CMD_SEND_REL_ADDR 0x03020000 +#define CMD_CARD_SELECT 0x07030000 +#define CMD_SEND_IF_COND 0x08020000 +#define CMD_STOP_TRANS 0x0C030000 +#define CMD_READ_SINGLE 0x11220010 +#define CMD_READ_MULTI 0x12220032 +#define CMD_SET_BLOCKCNT 0x17020000 +#define CMD_APP_CMD 0x37000000 +#define CMD_SET_BUS_WIDTH (0x06020000|CMD_NEED_APP) +#define CMD_SEND_OP_COND (0x29020000|CMD_NEED_APP) +#define CMD_SEND_SCR (0x33220010|CMD_NEED_APP) + +// STATUS register settings +#define SR_READ_AVAILABLE 0x00000800 +#define SR_DAT_INHIBIT 0x00000002 +#define SR_CMD_INHIBIT 0x00000001 +#define SR_APP_CMD 0x00000020 + +// INTERRUPT register settings +#define INT_DATA_TIMEOUT 0x00100000 +#define INT_CMD_TIMEOUT 0x00010000 +#define INT_READ_RDY 0x00000020 +#define INT_CMD_DONE 0x00000001 + +#define INT_ERROR_MASK 0x017E8000 + +// CONTROL register settings +#define C0_SPI_MODE_EN 0x00100000 +#define C0_HCTL_HS_EN 0x00000004 +#define C0_HCTL_DWITDH 0x00000002 + +#define C1_SRST_DATA 0x04000000 +#define C1_SRST_CMD 0x02000000 +#define C1_SRST_HC 0x01000000 +#define C1_TOUNIT_DIS 0x000f0000 +#define C1_TOUNIT_MAX 0x000e0000 +#define C1_CLK_GENSEL 0x00000020 +#define C1_CLK_EN 0x00000004 +#define C1_CLK_STABLE 0x00000002 +#define C1_CLK_INTLEN 0x00000001 + +// SLOTISR_VER values +#define HOST_SPEC_NUM 0x00ff0000 +#define HOST_SPEC_NUM_SHIFT 16 +#define HOST_SPEC_V3 2 +#define HOST_SPEC_V2 1 +#define HOST_SPEC_V1 0 + +// SCR flags +#define SCR_SD_BUS_WIDTH_4 0x00000400 +#define SCR_SUPP_SET_BLKCNT 0x02000000 +// added by my driver +#define SCR_SUPP_CCS 0x00000001 + +#define ACMD41_VOLTAGE 0x00ff8000 +#define ACMD41_CMD_COMPLETE 0x80000000 +#define ACMD41_CMD_CCS 0x40000000 +#define ACMD41_ARG_HC 0x51ff8000 + +#define SD_OK 0 +#define SD_TIMEOUT -1 +#define SD_ERROR -2 + +uint32_t sd_scr[2], sd_ocr, sd_rca, sd_err, sd_hv; + +/** + * Wait for data or command ready + */ +int sd_status(uint32_t mask) +{ + int cnt = 500000; while((*EMMC_STATUS & mask) && !(*EMMC_INTERRUPT & INT_ERROR_MASK) && cnt--) delaym(1); + return (cnt <= 0 || (*EMMC_INTERRUPT & INT_ERROR_MASK)) ? SD_ERROR : SD_OK; +} + +/** + * Wait for interrupt + */ +int sd_int(uint32_t mask) +{ + uint32_t r, m=mask | INT_ERROR_MASK; + int cnt = 1000000; while(!(*EMMC_INTERRUPT & m) && cnt--) delaym(1); + r=*EMMC_INTERRUPT; + if(cnt<=0 || (r & INT_CMD_TIMEOUT) || (r & INT_DATA_TIMEOUT) ) { *EMMC_INTERRUPT=r; return SD_TIMEOUT; } else + if(r & INT_ERROR_MASK) { *EMMC_INTERRUPT=r; return SD_ERROR; } + *EMMC_INTERRUPT=mask; + return 0; +} + +/** + * Send a command + */ +int sd_cmd(uint32_t code, uint32_t arg) +{ + int r=0; + sd_err=SD_OK; + if(code&CMD_NEED_APP) { + r=sd_cmd(CMD_APP_CMD|(sd_rca?CMD_RSPNS_48:0),sd_rca); + if(sd_rca && !r) { DBG("BOOTBOOT-ERROR: failed to send SD APP command\n"); sd_err=SD_ERROR;return 0;} + code &= ~CMD_NEED_APP; + } + if(sd_status(SR_CMD_INHIBIT)) { DBG("BOOTBOOT-ERROR: EMMC busy\n"); sd_err= SD_TIMEOUT;return 0;} +#if SD_DEBUG + uart_puts("EMMC: Sending command ");uart_hex(code,4);uart_puts(" arg ");uart_hex(arg,4);uart_putc('\n'); +#endif + *EMMC_INTERRUPT=*EMMC_INTERRUPT; *EMMC_ARG1=arg; *EMMC_CMDTM=code; + if(code==CMD_SEND_OP_COND) delaym(1000); else + if(code==CMD_SEND_IF_COND || code==CMD_APP_CMD) delaym(100); + if((r=sd_int(INT_CMD_DONE))) {DBG("BOOTBOOT-ERROR: failed to send EMMC command\n");sd_err=r;return 0;} + r=*EMMC_RESP0; + if(code==CMD_GO_IDLE || code==CMD_APP_CMD) return 0; else + if(code==(CMD_APP_CMD|CMD_RSPNS_48)) return r&SR_APP_CMD; else + if(code==CMD_SEND_OP_COND) return r; else + if(code==CMD_SEND_IF_COND) return r==arg? SD_OK : SD_ERROR; else + if(code==CMD_ALL_SEND_CID) {r|=*EMMC_RESP3; r|=*EMMC_RESP2; r|=*EMMC_RESP1; return r; } else + if(code==CMD_SEND_REL_ADDR) { + sd_err=(((r&0x1fff))|((r&0x2000)<<6)|((r&0x4000)<<8)|((r&0x8000)<<8))&CMD_ERRORS_MASK; + return r&CMD_RCA_MASK; + } + return r&CMD_ERRORS_MASK; + // make gcc happy + return 0; +} + +/** + * read a block from sd card and return the number of bytes read + * returns 0 on error. + */ +int sd_readblock(uint64_t lba, uint8_t *buffer, uint32_t num) +{ + int r,c=0,d; + if(num<1) num=1; +#if SD_DEBUG + uart_puts("sd_readblock lba ");uart_hex(lba,4);uart_puts(" num ");uart_hex(num,4);uart_putc('\n'); +#endif + if(sd_status(SR_DAT_INHIBIT)) {sd_err=SD_TIMEOUT; return 0;} + uint32_t *buf=(uint32_t *)buffer; + if(sd_scr[0] & SCR_SUPP_CCS) { + if(num > 1 && (sd_scr[0] & SCR_SUPP_SET_BLKCNT)) { + sd_cmd(CMD_SET_BLOCKCNT,num); + if(sd_err) return 0; + } + *EMMC_BLKSIZECNT = (num << 16) | 512; + sd_cmd(num == 1 ? CMD_READ_SINGLE : CMD_READ_MULTI,lba); + if(sd_err) return 0; + } else { + *EMMC_BLKSIZECNT = (1 << 16) | 512; + } + while( c < num ) { + if(!(sd_scr[0] & SCR_SUPP_CCS)) { + sd_cmd(CMD_READ_SINGLE,(lba+c)*512); + if(sd_err) return 0; + } + if((r=sd_int(INT_READ_RDY))){DBG("\rBOOTBOOT-ERROR: Timeout waiting for ready to read\n");sd_err=r;return 0;} + for(d=0;d<128;d++) buf[d] = *EMMC_DATA; + c++; buf+=128; + } +#if SD_DEBUG + uart_dump(buffer,4); +#endif + if( num > 1 && !(sd_scr[0] & SCR_SUPP_SET_BLKCNT) && (sd_scr[0] & SCR_SUPP_CCS)) sd_cmd(CMD_STOP_TRANS,0); + return sd_err!=SD_OK || c!=num? 0 : num*512; +} + +/** + * set SD clock to frequency in Hz + */ +int sd_clk(uint32_t f) +{ + uint32_t d,c=41666666/f,x,s=32,h=0; + int cnt = 100000; + while((*EMMC_STATUS & (SR_CMD_INHIBIT|SR_DAT_INHIBIT)) && cnt--) delaym(1); + if(cnt<=0) { + DBG("BOOTBOOT-ERROR: timeout waiting for inhibit flag\n"); + return SD_ERROR; + } + + *EMMC_CONTROL1 &= ~C1_CLK_EN; delaym(10); + x=c-1; if(!x) s=0; else { + if(!(x & 0xffff0000u)) { x <<= 16; s -= 16; } + if(!(x & 0xff000000u)) { x <<= 8; s -= 8; } + if(!(x & 0xf0000000u)) { x <<= 4; s -= 4; } + if(!(x & 0xc0000000u)) { x <<= 2; s -= 2; } + if(!(x & 0x80000000u)) { x <<= 1; s -= 1; } + if(s>0) s--; + if(s>7) s=7; + } + if(sd_hv>HOST_SPEC_V2) d=c; else d=(1<HOST_SPEC_V2) h=(d&0x300)>>2; + d=(((d&0x0ff)<<8)|h); + *EMMC_CONTROL1=(*EMMC_CONTROL1&0xffff003f)|d; delaym(10); + *EMMC_CONTROL1 |= C1_CLK_EN; delaym(10); + cnt=10000; while(!(*EMMC_CONTROL1 & C1_CLK_STABLE) && cnt--) delaym(10); + if(cnt<=0) { + DBG("BOOTBOOT-ERROR: failed to get stable clock\n"); + return SD_ERROR; + } + return SD_OK; +} + +/** + * initialize EMMC to read SDHC card + */ +int sd_init() +{ + long r,cnt,ccs=0; + // GPIO_CD + r=*GPFSEL4; r&=~(7<<(7*3)); *GPFSEL4=r; + *GPPUD=2; delay(150); *GPPUDCLK1=(1<<15); delay(150); *GPPUD=0; *GPPUDCLK1=0; + r=*GPHEN1; r|=1<<15; *GPHEN1=r; + + // GPIO_CLK, GPIO_CMD + r=*GPFSEL4; r|=(7<<(8*3))|(7<<(9*3)); *GPFSEL4=r; + *GPPUD=2; delay(150); *GPPUDCLK1=(1<<16)|(1<<17); delay(150); *GPPUD=0; *GPPUDCLK1=0; + + // GPIO_DAT0, GPIO_DAT1, GPIO_DAT2, GPIO_DAT3 + r=*GPFSEL5; r|=(7<<(0*3)) | (7<<(1*3)) | (7<<(2*3)) | (7<<(3*3)); *GPFSEL5=r; + *GPPUD=2; delay(150); + *GPPUDCLK1=(1<<18) | (1<<19) | (1<<20) | (1<<21); + delay(150); *GPPUD=0; *GPPUDCLK1=0; + + sd_hv = (*EMMC_SLOTISR_VER & HOST_SPEC_NUM) >> HOST_SPEC_NUM_SHIFT; +#if SD_DEBUG + uart_puts("EMMC: GPIO set up\n"); +#endif + // Reset the card. + *EMMC_CONTROL0 = 0; *EMMC_CONTROL1 |= C1_SRST_HC; + cnt=10000; do{delaym(10);} while( (*EMMC_CONTROL1 & C1_SRST_HC) && cnt-- ); + if(cnt<=0) { + DBG("BOOTBOOT-ERROR: failed to reset EMMC\n"); + return SD_ERROR; + } +#if SD_DEBUG + uart_puts("EMMC: reset OK\n"); +#endif + *EMMC_CONTROL1 |= C1_CLK_INTLEN | C1_TOUNIT_MAX; + delaym(10); + // Set clock to setup frequency. + if((r=sd_clk(400000))) return r; + *EMMC_INT_EN = 0xffffffff; + *EMMC_INT_MASK = 0xffffffff; + sd_scr[0]=sd_scr[1]=sd_rca=sd_err=0; + sd_cmd(CMD_GO_IDLE,0); + if(sd_err) return sd_err; + + sd_cmd(CMD_SEND_IF_COND,0x000001AA); + if(sd_err) return sd_err; + cnt=6; r=0; while(!(r&ACMD41_CMD_COMPLETE) && cnt--) { + delay(400); + r=sd_cmd(CMD_SEND_OP_COND,ACMD41_ARG_HC); +#if SD_DEBUG + uart_puts("EMMC: CMD_SEND_OP_COND returned "); + if(r&ACMD41_CMD_COMPLETE) + uart_puts("COMPLETE "); + if(r&ACMD41_VOLTAGE) + uart_puts("VOLTAGE "); + if(r&ACMD41_CMD_CCS) + uart_puts("CCS "); + uart_hex(r,8); + uart_putc('\n'); +#endif + if(sd_err!=SD_TIMEOUT && sd_err!=SD_OK ) { + DBG("BOOTBOOT-ERROR: EMMC ACMD41 returned error\n"); + return sd_err; + } + } + if(!(r&ACMD41_CMD_COMPLETE) || !cnt ) return SD_TIMEOUT; + if(!(r&ACMD41_VOLTAGE)) return SD_ERROR; + if(r&ACMD41_CMD_CCS) ccs=SCR_SUPP_CCS; + + sd_cmd(CMD_ALL_SEND_CID,0); + + sd_rca = sd_cmd(CMD_SEND_REL_ADDR,0); +#if SD_DEBUG + uart_puts("EMMC: CMD_SEND_REL_ADDR returned "); + uart_hex(sd_rca,8); + uart_putc('\n'); +#endif + if(sd_err) return sd_err; + + if((r=sd_clk(25000000))) return r; + + sd_cmd(CMD_CARD_SELECT,sd_rca); + if(sd_err) return sd_err; + + if(sd_status(SR_DAT_INHIBIT)) return SD_TIMEOUT; + *EMMC_BLKSIZECNT = (1<<16) | 8; + sd_cmd(CMD_SEND_SCR,0); + if(sd_err) return sd_err; + if(sd_int(INT_READ_RDY)) return SD_TIMEOUT; + + r=0; cnt=100000; while(r<2 && cnt) { + if( *EMMC_STATUS & SR_READ_AVAILABLE ) + sd_scr[r++] = *EMMC_DATA; + else + delaym(1); + } + if(r!=2) return SD_TIMEOUT; + if(sd_scr[0] & SCR_SD_BUS_WIDTH_4) { + sd_cmd(CMD_SET_BUS_WIDTH,sd_rca|2); + if(sd_err) return sd_err; + *EMMC_CONTROL0 |= C0_HCTL_DWITDH; + } + // add software flag +#ifdef SD_DEBUG + uart_puts("EMMC: supports "); + if(sd_scr[0] & SCR_SUPP_SET_BLKCNT) + uart_puts("SET_BLKCNT "); + if(ccs) + uart_puts("CCS "); + uart_putc('\n'); +#endif + sd_scr[0]&=~SCR_SUPP_CCS; + sd_scr[0]|=ccs; + return SD_OK; +} + +// comment out this include if you don't want FS/Z support +#include "../../osZ/etc/include/fsZ.h" +// get filesystem drivers for initrd +#include "fs.h" + +/*** other defines and structs ***/ +#define COREMMIO_BASE 0xFFFFFFFFF8000000 + +typedef struct { + uint32_t type[4]; + uint8_t uuid[16]; + uint64_t start; + uint64_t end; + uint64_t flags; + uint8_t name[72]; +} efipart_t; + +typedef struct { + char jmp[3]; + char oem[8]; + uint16_t bps; + uint8_t spc; + uint16_t rsc; + uint8_t nf; + uint16_t nr; + uint16_t ts16; + uint8_t media; + uint16_t spf16; + uint16_t spt; + uint16_t nh; + uint32_t hs; + uint32_t ts32; + uint32_t spf32; + uint32_t flg; + uint32_t rc; + char vol[6]; + char fst[8]; + char dmy[20]; + char fst2[8]; +} __attribute__((packed)) bpb_t; + +typedef struct { + char name[8]; + char ext[3]; + char attr[9]; + uint16_t ch; + uint32_t attr2; + uint16_t cl; + uint32_t size; +} __attribute__((packed)) fatdir_t; + +typedef struct { + uint32_t magic; + uint32_t version; + uint32_t headersize;/* offset of bitmaps in file */ + uint16_t flags; /* original PSF2 has 32 bit flags */ + uint8_t hotspot_x; /* addition to OS/Z */ + uint8_t hotspot_y; + uint32_t numglyph; + uint32_t bytesperglyph; + uint32_t height; + uint32_t width; + uint8_t glyphs; +} __attribute__((packed)) font_t; + +extern volatile unsigned char _binary_font_psf_start; + +/*** common variables ***/ +file_t env; // environment file descriptor +file_t initrd; // initrd file descriptor +file_t core; // kernel file descriptor +BOOTBOOT *bootboot; // the BOOTBOOT structure + +// default environment variables. M$ states that 1024x768 must be supported +int reqwidth = 1024, reqheight = 768; +char *kernelname="sys/core"; +unsigned char *kne; + +// alternative environment name +char *cfgname="sys/config"; + +/* current cursor position */ +int kx, ky; +/* maximum coordinates */ +int maxx, maxy; + +/** + * Get a linear frame buffer + */ +int GetLFB(uint32_t width, uint32_t height) +{ + font_t *font = (font_t*)&_binary_font_psf_start; + + //query natural width, height if not given + if(width==0 && height==0) { + mbox[0] = 8*4; + mbox[1] = MBOX_REQUEST; + mbox[2] = 0x40003; //get phy wh + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = 0; + mbox[6] = 0; + mbox[7] = 0; + if(mbox_call(MBOX_CH_PROP,mbox) && mbox[5]!=0) { + width=mbox[5]; + height=mbox[6]; + } + } + //if we already have a framebuffer, release it + if(bootboot->fb_ptr!=NULL) { + mbox[0] = 8*4; + mbox[1] = MBOX_REQUEST; + mbox[2] = 0x48001; //release buffer + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = (uint32_t)(((uint64_t)bootboot->fb_ptr)); + mbox[6] = 0; + mbox[7] = 0; + mbox_call(MBOX_CH_PROP,mbox); + } + //check minimum resolution + if(width<640) width=640; + if(height<480) height=480; + + mbox[0] = 35*4; + mbox[1] = MBOX_REQUEST; + + mbox[2] = 0x48003; //set phy wh + mbox[3] = 8; + mbox[4] = 8; + mbox[5] = width; //FrameBufferInfo.width + mbox[6] = height; //FrameBufferInfo.height + + mbox[7] = 0x48004; //set virt wh + mbox[8] = 8; + mbox[9] = 8; + mbox[10] = width; //FrameBufferInfo.virtual_width + mbox[11] = height; //FrameBufferInfo.virtual_height + + mbox[12] = 0x48009; //set virt offset + mbox[13] = 8; + mbox[14] = 8; + mbox[15] = 0; //FrameBufferInfo.x_offset + mbox[16] = 0; //FrameBufferInfo.y.offset + + mbox[17] = 0x48005; //set depth + mbox[18] = 4; + mbox[19] = 4; + mbox[20] = 32; //FrameBufferInfo.depth + + mbox[21] = 0x48006; //set pixel order + mbox[22] = 4; + mbox[23] = 4; + mbox[24] = 0; //RGB, not BGR preferably + + mbox[25] = 0x40001; //get framebuffer, gets alignment on request + mbox[26] = 8; + mbox[27] = 8; + mbox[28] = PAGESIZE; //FrameBufferInfo.pointer + mbox[29] = 0; //FrameBufferInfo.size + + mbox[30] = 0x40008; //get pitch + mbox[31] = 4; + mbox[32] = 4; + mbox[33] = 0; //FrameBufferInfo.pitch + + mbox[34] = 0; //Arnold Schwarzenegger + + if(mbox_call(MBOX_CH_PROP,mbox) && mbox[20]==32 && mbox[27]==(MBOX_RESPONSE|8) && mbox[28]!=0) { + mbox[28]&=0x3FFFFFFF; + bootboot->fb_width=mbox[5]; + bootboot->fb_height=mbox[6]; + bootboot->fb_scanline=mbox[33]; + bootboot->fb_ptr=(void*)((uint64_t)mbox[28]); + bootboot->fb_size=mbox[29]; + bootboot->fb_type=mbox[24]?FB_ABGR:FB_ARGB; + kx=ky=0; + maxx=bootboot->fb_width/(font->width+1); + maxy=bootboot->fb_height/font->height; + return 1; + } + return 0; +} + +/** + * display one literal unicode character + */ +void putc(char c) +{ + font_t *font = (font_t*)&_binary_font_psf_start; + unsigned char *glyph = (unsigned char*)&_binary_font_psf_start + + font->headersize + (c>0&&cnumglyph?c:0)*font->bytesperglyph; + int offs = (ky * font->height * bootboot->fb_scanline) + (kx * (font->width+1) * 4); + int x,y, line,mask; + int bytesperline=(font->width+7)/8; + if(c=='\r') { + kx=0; + } else + if(c=='\n') { + kx=0; ky++; + } else { + for(y=0;yheight;y++){ + line=offs; + mask=1<<(font->width-1); + for(x=0;xwidth;x++){ + *((uint32_t*)((uint64_t)bootboot->fb_ptr + line))=((int)*glyph) & (mask)?color:0; + mask>>=1; + line+=4; + } + *((uint32_t*)((uint64_t)bootboot->fb_ptr + line))=0; + glyph+=bytesperline; + offs+=bootboot->fb_scanline; + } + kx++; + if(kx>=maxx) { + kx=0; ky++; + } + } + // send it to serial too + uart_putc(c); +} + +/** + * display a string + */ +void puts(char *s) { while(*s) putc(*s++); } + +void ParseEnvironment(uint8_t *env) +{ + uint8_t *end=env+PAGESIZE; + DBG(" * Environment\n"); + env--; env[PAGESIZE]=0; kne=NULL; + while(env0 && !(*AUX_MU_LSR&0x01)); +#else + r=(char)(*UART0_DR);do{asm volatile("nop");}while(--mp>0 && *UART0_FR&0x10); +#endif + if(mp>0) { + // we got response from raspbootcom + sp=uart_getc(); + sp|=uart_getc()<<8; sp|=uart_getc()<<16; sp|=uart_getc()<<24; + if(sp>0 && sp127) np=127; + // read GPT entries + r=sd_readblock(*((uint32_t*)((char*)&__diskbuf+72)),(unsigned char*)&__diskbuf,(np*sp+511)/512); + if(r==0) goto diskerr; + part=NULL; + // first, look for a partition with bootable flag + for(r=0;rtype[0]==0 && part->type[1]==0 && part->type[2]==0 && part->type[3]==0) || part->start==0) { + r=np; + break; + } + // EFI_PART_USED_BY_OS? + if(part->flags&4) break; + } + // if none, look for specific partition types + if(part==NULL || r>=np) { + for(r=0;rtype[0]==0 && part->type[1]==0 && part->type[2]==0 && part->type[3]==0) || part->start==0) { + r=np; + break; + } + // ESP? + if((part->type[0]==0xC12A7328 && part->type[1]==0x11D2F81F) || + // or OS/Z root partition for this architecture? + (part->type[0]==0x5A2F534F && (part->type[1]&0xFFFF)==0xAA64 && part->type[3]==0x746F6F72)) + break; + } + } + if(part==NULL || r>=np) goto diskerr; + r=sd_readblock(part->start,(unsigned char*)&_end,1); + if(r==0) goto diskerr; + initrd.ptr=NULL; initrd.size=0; + // wait keypress with timeout + mp=50; +#if CONSOLE == UART1 + r=(char)(*AUX_MU_IO);do{delaym(5);}while(--mp>0 && !(*AUX_MU_LSR&0x01)); +#else + r=(char)(*UART0_DR);do{delaym(5);}while(--mp>0 && *UART0_FR&0x10); +#endif + if(mp!=0) { + puts(" * Backup initrd\n"); + bkp=1; + } + //is it a FAT partition? + bpb=(bpb_t*)&_end; + if(!memcmp((void*)bpb->fst,"FAT16",5) || !memcmp((void*)bpb->fst2,"FAT32",5)) { + // locate BOOTBOOT directory + uint32_t data_sec, root_sec, clu=0, s, s2; + fatdir_t *dir; + uint32_t *fat32=(uint32_t*)((uint8_t*)&_end+bpb->rsc*512); + uint16_t *fat16=(uint16_t*)fat32; + uint8_t *ptr; + data_sec=root_sec=((bpb->spf16?bpb->spf16:bpb->spf32)*bpb->nf)+bpb->rsc; + //WARNING gcc generates a code for bpb->nr that cause unaligned exception + s=*((uint32_t*)&bpb->nf); + s>>=8; + s&=0xFFFF; + s<<=5; + if(bpb->spf16>0) { + data_sec+=(s+511)>>9; + } else { + root_sec+=(bpb->rc-2)*bpb->spc; + } + // load fat table + r=sd_readblock(part->start+1,(unsigned char*)&_end+512,(bpb->spf16?bpb->spf16:bpb->spf32)+bpb->rsc); + if(r==0) goto diskerr; + pe=(uint8_t*)&_end+512+r; + // load root directory + r=sd_readblock(part->start+root_sec,(unsigned char*)pe,s/512+1); + dir=(fatdir_t*)pe; + while(dir->name[0]!=0 && memcmp(dir->name,"BOOTBOOT ",11)) dir++; + if(dir->name[0]!='B') goto diskerr; + r=sd_readblock(part->start+(dir->cl+(dir->ch<<16)-2)*bpb->spc+data_sec,(unsigned char*)pe,bpb->spc); + if(r==0) goto diskerr; + dir=(fatdir_t*)pe; + // locate environment and initrd + while(dir->name[0]!=0) { + if(!memcmp(dir->name,"CONFIG ",11)) { + s=dir->sizesize:PAGESIZE-1; + clu=dir->cl+(dir->ch<<16); + ptr=(void*)&__environment; + while(s>0) { + s2=s>bpb->spc*512?bpb->spc*512:s; + r=sd_readblock(part->start+(clu-2)*bpb->spc+data_sec,ptr,s2<512?1:s2/512); + clu=bpb->spf16>0?fat16[clu]:fat32[clu]; + ptr+=s2; + s-=s2; + } + clu=0; + } else + if(!memcmp(dir->name,bkp?"INITRD BAK":"INITRD ",11)) { + clu=dir->cl+(dir->ch<<16); + initrd.size=dir->size; + } + dir++; + } + // if initrd not found, try architecture specific name + if(clu==0) { + dir=(fatdir_t*)pe; + while(dir->name[0]!=0) { + if(!memcmp(dir->name,"AARCH64 ",11)) { + clu=dir->cl+(dir->ch<<16); + initrd.size=dir->size; + break; + } + dir++; + } + } + // walk through cluster chain to load initrd + if(clu!=0 && initrd.size!=0) { + initrd.ptr=ptr=pe; + s=initrd.size; + while(s>0) { + s2=s>bpb->spc*512?bpb->spc*512:s; + r=sd_readblock(part->start+(clu-2)*bpb->spc+data_sec,ptr,s2<512?1:s2/512); + clu=bpb->spf16>0?fat16[clu]:fat32[clu]; + ptr+=s2; + s-=s2; + } + } + } else { + // initrd is on the entire partition + r=sd_readblock(part->start,(unsigned char*)&_end+512,part->end-part->start); + if(r==0) goto diskerr; + initrd.ptr=(uint8_t*)&_end; + initrd.size=r; + } +gotinitrd: + if(initrd.ptr==NULL || initrd.size==0) { + puts("BOOTBOOT-PANIC: INITRD not found\n"); + goto error; + } +#if INITRD_DEBUG + uart_puts("Initrd at ");uart_hex((uint64_t)initrd.ptr,4);uart_putc(' ');uart_hex(initrd.size,4);uart_putc('\n'); +#endif + // uncompress if it's compressed + if(initrd.ptr[0]==0x1F && initrd.ptr[1]==0x8B) { + unsigned char *addr,f; + volatile TINF_DATA d; + DBG(" * Gzip compressed initrd\n"); + // skip gzip header + addr=initrd.ptr+2; + if(*addr++!=8) goto gzerr; + f=*addr++; addr+=6; + if(f&4) { r=*addr++; r+=(*addr++ << 8); addr+=r; } + if(f&8) { while(*addr++ != 0); } + if(f&16) { while(*addr++ != 0); } + if(f&2) addr+=2; + d.source = addr; + memcpy((void*)&d.destSize,initrd.ptr+initrd.size-4,4); + // decompress + d.bitcount = 0; + d.bfinal = 0; + d.btype = -1; + d.curlen = 0; + if((uint8_t*)&_end+d.destSizeinitrd_ptr=(uint64_t)&_end; + // round up to page size + bootboot->initrd_size=(initrd.size+PAGESIZE-1)&~(PAGESIZE-1); + DBG(" * Initrd loaded\n"); +#if INITRD_DEBUG + // dump initrd in memory + uart_dump((void*)bootboot->initrd_ptr,8); +#endif + + // if no config, locate it in uncompressed initrd + if(*((uint8_t*)&__environment)==0) { + r=0; env.ptr=NULL; + while(env.ptr==NULL && fsdrivers[r]!=NULL) { + env=(*fsdrivers[r++])((unsigned char*)bootboot->initrd_ptr,cfgname); + } + if(env.ptr!=NULL) + memcpy((void*)&__environment,(void*)(env.ptr),env.sizeinitrd_ptr,kernelname); + } + if(kne!=NULL) + *kne='\n'; + // scan for the first executable + if(core.ptr==NULL || core.size==0) { + DBG(" * Autodetecting kernel\n"); + core.size=0; + r=bootboot->initrd_size; + core.ptr=(uint8_t*)bootboot->initrd_ptr; + while(r-->0) { + Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); + pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); + if((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4))&& + ehdr->e_ident[EI_CLASS]==ELFCLASS64&& + ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& + ehdr->e_machine==EM_AARCH64&& + ehdr->e_phnum>0){ + core.size=1; + break; + } + if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && pehdr->magic == PE_MAGIC && + pehdr->machine == IMAGE_FILE_MACHINE_ARM64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS) { + core.size=1; + break; + } + core.ptr++; + } + } + if(core.ptr==NULL || core.size==0) { + puts("BOOTBOOT-PANIC: Kernel not found in initrd\n"); + goto error; + } else { + Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); + pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); + if((!memcmp(ehdr->e_ident,ELFMAG,SELFMAG)||!memcmp(ehdr->e_ident,"OS/Z",4))&& + ehdr->e_ident[EI_CLASS]==ELFCLASS64&& + ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& + ehdr->e_machine==EM_AARCH64&& + ehdr->e_phnum>0){ + DBG(" * Parsing ELF64\n"); + Elf64_Phdr *phdr=(Elf64_Phdr *)((uint8_t *)ehdr+ehdr->e_phoff); + for(r=0;re_phnum;r++){ + if(phdr->p_type==PT_LOAD && phdr->p_vaddr>>48==0xffff) { + core.ptr += phdr->p_offset; + // hack to keep symtab and strtab for shared libraries + core.size = phdr->p_filesz + (ehdr->e_type==3?0x4000:0); + bss = phdr->p_memsz - core.size; + entrypoint = ehdr->e_entry; + break; + } + phdr=(Elf64_Phdr *)((uint8_t *)phdr+ehdr->e_phentsize); + } + } else + if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && pehdr->magic == PE_MAGIC && + pehdr->machine == IMAGE_FILE_MACHINE_ARM64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS && + (int64_t)pehdr->code_base>>48==0xffff) { + DBG(" * Parsing PE32+\n"); + core.size = (pehdr->entry_point-pehdr->code_base) + pehdr->text_size + pehdr->data_size; + bss = pehdr->bss_size; + entrypoint = (int64_t)pehdr->entry_point; + } + } +#if EXEC_DEBUG + uart_puts("Executable size "); + uart_hex((uint64_t)core.size,4); + uart_puts(" bss "); + uart_hex((uint64_t)bss,4); + uart_putc('\n'); + uart_dump((void*)core.ptr,4); +#endif + if(core.size<2 || entrypoint==0) { + puts("BOOTBOOT-PANIC: Kernel is not a valid executable\n"); + goto error; + } + // create core segment + memcpy((void*)(bootboot->initrd_ptr+bootboot->initrd_size), core.ptr, core.size); + core.ptr=(uint8_t*)(bootboot->initrd_ptr+bootboot->initrd_size); + if(bss>0) + memset(core.ptr + core.size, 0, bss); + core.size = (core.size+bss+PAGESIZE-1)&~(PAGESIZE-1); +#if EXEC_DEBUG + uart_puts("Core "); + uart_hex((uint64_t)core.ptr,4); + uart_puts(" to "); + uart_hex((uint64_t)core.ptr+core.size,4); + uart_putc('\n'); +#endif + + /* generate memory map to bootboot struct */ + DBG(" * Memory Map\n"); + mmap=(MMapEnt *)&bootboot->mmap; + + // everything before the bootboot struct is free + mmap->ptr=0; mmap->size=(uint64_t)&__bootboot | MMAP_FREE; + mmap++; bootboot->size+=sizeof(MMapEnt); + + // mark bss reserved + mmap->ptr=(uint64_t)&__bootboot; mmap->size=((uint64_t)&_end-(uint64_t)&__bootboot) | MMAP_USED; + mmap++; bootboot->size+=sizeof(MMapEnt); + + r=bootboot->initrd_size + core.size; + // after bss and before initrd is free + if(bootboot->initrd_ptr-(uint64_t)&_end) { + mmap->ptr=(uint64_t)&_end; mmap->size=(bootboot->initrd_ptr-(uint64_t)&_end) | MMAP_FREE; + mmap++; bootboot->size+=sizeof(MMapEnt); + // initrd is reserved (and add core's area to it) + mmap->ptr=bootboot->initrd_ptr; mmap->size=r | MMAP_USED; + mmap++; bootboot->size+=sizeof(MMapEnt); + } else { + mmap--; mmap->size+=r; mmap++; + } + r+=(uint32_t)bootboot->initrd_ptr; + + mbox[0]=8*4; + mbox[1]=0; + mbox[2]=0x10005; // get memory size + mbox[3]=8; + mbox[4]=0; + mbox[5]=0; + mbox[6]=0; + mbox[7]=0; + if(!mbox_call(MBOX_CH_PROP, mbox)) + // on failure (should never happen) assume 64Mb memory max + mbox[6]=64*1024*1024; + + // everything after initrd to the top of memory is free + mp=mbox[6]-r; + mmap->ptr=r; mmap->size=mp | MMAP_FREE; + mmap++; bootboot->size+=sizeof(MMapEnt); + + // MMIO area + mmap->ptr=MMIO_BASE; mmap->size=((uint64_t)0x40000000-MMIO_BASE) | MMAP_MMIO; + mmap++; bootboot->size+=sizeof(MMapEnt); + +#if MEM_DEBUG + /* dump memory map */ + mmap=(MMapEnt *)&bootboot->mmap; + for(r=128;rsize;r+=sizeof(MMapEnt)) { + uart_hex(MMapEnt_Ptr(mmap),8); + uart_putc(' '); + uart_hex(MMapEnt_Ptr(mmap)+MMapEnt_Size(mmap)-1,8); + uart_putc(' '); + uart_hex(MMapEnt_Type(mmap),1); + uart_putc(' '); + switch(MMapEnt_Type(mmap)) { + case MMAP_USED: uart_puts("reserved"); break; + case MMAP_FREE: uart_puts("free"); break; + case MMAP_ACPIFREE: uart_puts("acpifree"); break; + case MMAP_ACPINVS: uart_puts("acpinvs"); break; + case MMAP_MMIO: uart_puts("mmio"); break; + default: uart_puts("unknown"); break; + } + uart_putc('\n'); + mmap++; + } +#endif + + /* get linear framebuffer if requested resolution different than current */ + DBG(" * Screen VideoCore\n"); + if(reqwidth!=bootboot->fb_width || reqheight!=bootboot->fb_height) { + if(!GetLFB(reqwidth, reqheight)) { +viderr: + puts("BOOTBOOT-PANIC: VideoCore error, no framebuffer\n"); + goto error; + } + } + kx=ky=0; color=0xFFDD33; + + /* create MMU translation tables in __paging */ + paging=(uint64_t*)&__paging; + // TTBR0, identity L1 + paging[0]=(uint64_t)((uint8_t*)&__paging+2*PAGESIZE)|0b11|(3<<8)|(1<<10); //AF=1,Block=1,Present=1, SH=3 ISH, RO + // identity L2 + paging[2*512]=(uint64_t)((uint8_t*)&__paging+3*PAGESIZE)|0b11|(3<<8)|(1<<10); //AF=1,Block=1,Present=1 + // identity L2 2M blocks + mp>>=21; + np=MMIO_BASE>>21; + for(r=1;r<512;r++) + paging[2*512+r]=(uint64_t)(((uint64_t)r<<21))|0b01|(1<<10)|(r>=np?(2<<8)|(1<<2)|(1L<<54):(3<<8)); //device SH=2 OSH + // identity L3 + for(r=0;r<512;r++) + paging[3*512+r]=(uint64_t)(r*PAGESIZE)|0b11|(1<<10)|(r<0x80||r>(uint32_t)((uint64_t)&_data/PAGESIZE)?0:(1<<7)); + // TTBR1, core L1 + paging[512+511]=(uint64_t)((uint8_t*)&__paging+4*PAGESIZE)|0b11|(3<<8)|(1<<10); //AF=1,Block=1,Present=1 + // core L2 + // map MMIO in kernel space + for(r=0;r<32;r++) + paging[4*512+448+r]=(uint64_t)(MMIO_BASE+((uint64_t)r<<21))|0b01|(2<<8)|(1<<10)|(1<<2)|(1L<<54); //OSH, Attr=1, NX + // map framebuffer + for(r=0;r<28;r++) + paging[4*512+480+r]=(uint64_t)((uint8_t*)&__paging+(6+r)*PAGESIZE)|0b11|(2<<8)|(1<<10)|(2<<2)|(1L<<54); //OSH, Attr=2 + paging[4*512+511]=(uint64_t)((uint8_t*)&__paging+5*PAGESIZE)|0b11|(3<<8)|(1<<10);// pointer to core L3 + // core L3 + paging[5*512+0]=(uint64_t)((uint8_t*)&__bootboot)|0b11|(3<<8)|(1<<10)|(1L<<54); // p, b, AF, ISH + paging[5*512+1]=(uint64_t)((uint8_t*)&__environment)|0b11|(3<<8)|(1<<10)|(1L<<54); + for(r=0;r<(core.size/PAGESIZE);r++) + paging[5*512+2+r]=(uint64_t)((uint8_t *)core.ptr+(uint64_t)r*PAGESIZE)|0b11|(3<<8)|(1<<10); +#if MEM_DEBUG + reg=r; +#endif + paging[5*512+511]=(uint64_t)((uint8_t*)&__corestack)|0b11|(3<<8)|(1<<10)|(1L<<54); // core stack + // core L3 (lfb) + for(r=0;r<16*512;r++) + paging[6*512+r]=(uint64_t)((uint8_t*)bootboot->fb_ptr+r*PAGESIZE)|0b11|(2<<8)|(1<<10)|(2<<2)|(1L<<54); //map framebuffer + +#if MEM_DEBUG + /* dump page translation tables */ + uart_puts("\nTTBR0\n L1 "); + uart_hex((uint64_t)&__paging,8); + uart_puts("\n "); + uart_hex((uint64_t)paging[0],8); + uart_puts(" ...\n L2 "); + uart_hex((uint64_t)&paging[2*512],8); + uart_puts("\n "); + for(r=0;r<4;r++) { uart_hex(paging[2*512+r],8); uart_putc(' '); } + uart_puts("...\n ... "); + for(r=mp-4;rrootdirfid*FSZ_SECSIZE); + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || memcmp(sb->magic,FSZ_MAGIC,4) || kernel==NULL){ + return ret; + } + DBG(" * FS/Z "); + DBG(kernel); + DBG("\n"); + // Get the inode + int i,ss=1<<(sb->logsec+11); + char *s,*e; + s=e=kernel; + i=0; +again: + while(*e!='/'&&*e!=0){e++;} + if(*e=='/'){e++;} + if(!memcmp(in->magic,FSZ_IN_MAGIC,4)){ + //is it inlined? + if(!memcmp(in->inlinedata,FSZ_DIR_MAGIC,4)){ + ent=(FSZ_DirEnt *)(in->inlinedata); + } else if(!memcmp(initrd_p+in->sec*ss,FSZ_DIR_MAGIC,4)){ + // go, get the sector pointed + ent=(FSZ_DirEnt *)(initrd_p+in->sec*ss); + } else { + return ret; + } + //skip header + FSZ_DirEntHeader *hdr=(FSZ_DirEntHeader *)ent; ent++; + //iterate on directory entries + int j=hdr->numentries; + while(j-->0){ + if(!memcmp(ent->name,s,e-s)) { + if(*e==0) { + i=ent->fid; + break; + } else { + s=e; + in=(FSZ_Inode *)(initrd_p+ent->fid*ss); + goto again; + } + } + ent++; + } + } else { + i=0; + } + if(i!=0) { + // fid -> inode ptr -> data ptr + FSZ_Inode *in=(FSZ_Inode *)(initrd_p+i*ss); + if(!memcmp(in->magic,FSZ_IN_MAGIC,4)){ + ret.size=in->size; + switch(FSZ_FLAG_TRANSLATION(in->flags)) { + case FSZ_IN_FLAG_INLINE: + // inline data + ret.ptr=(uint8_t*)(initrd_p+i*ss+1024); + break; + case FSZ_IN_FLAG_SECLIST: + case FSZ_IN_FLAG_SDINLINE: + // sector directory or list inlined + ret.ptr=(uint8_t*)(initrd_p + *((uint64_t*)&in->inlinedata) * ss); + break; + case FSZ_IN_FLAG_DIRECT: + // direct data + ret.ptr=(uint8_t*)(initrd_p + in->sec * ss); + break; + // sector directory (only one level supported here, and no holes in files) + case FSZ_IN_FLAG_SECLIST0: + case FSZ_IN_FLAG_SD: + ret.ptr=(uint8_t*)(initrd_p + (unsigned int)(((FSZ_SectorList *)(initrd_p+in->sec*ss))->sec) * ss); + break; + default: + ret.size=0; + break; + } + } + } + return ret; +} +#endif + +/** + * cpio archive + */ +file_t cpio_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr=initrd_p; + int k; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || + (memcmp(initrd_p,"070701",6) && memcmp(initrd_p,"070702",6) && memcmp(initrd_p,"070707",6))) + return ret; + DBG(" * cpio "); + DBG(kernel); + DBG("\n"); + k=strlen((unsigned char*)kernel); + // hpodc archive + while(!memcmp(ptr,"070707",6)){ + int ns=oct2bin(ptr+8*6+11,6); + int fs=oct2bin(ptr+8*6+11+6,11); + if(!memcmp(ptr+9*6+2*11,kernel,k+1)){ + ret.size=fs; + ret.ptr=(uint8_t*)(ptr+9*6+2*11+ns); + return ret; + } + ptr+=(76+ns+fs); + } + // newc and crc archive + while(!memcmp(ptr,"07070",5)){ + int fs=hex2bin(ptr+8*6+6,8); + int ns=hex2bin(ptr+8*11+6,8); + if(!memcmp(ptr+110,kernel,k+1)){ + ret.size=fs; + ret.ptr=(uint8_t*)(ptr+((110+ns+3)/4)*4); + return ret; + } + ptr+=((110+ns+3)/4)*4 + ((fs+3)/4)*4; + } + return ret; +} + +/** + * ustar tarball archive + */ +file_t tar_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr=initrd_p; + int k; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || memcmp(initrd_p+257,"ustar",5)) + return ret; + DBG(" * tar "); + DBG(kernel); + DBG("\n"); + k=strlen((unsigned char*)kernel); + while(!memcmp(ptr+257,"ustar",5)){ + int fs=oct2bin(ptr+0x7c,11); + if(!memcmp(ptr,kernel,k+1)){ + ret.size=fs; + ret.ptr=(uint8_t*)(ptr+512); + return ret; + } + ptr+=(((fs+511)/512)+1)*512; + } + return ret; +} + +/** + * Simple File System + */ +file_t sfs_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr, *end; + int k,bs,ver; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || (memcmp(initrd_p+0x1AC,"SFS",3) && memcmp(initrd_p+0x1A6,"SFS",3))) + return ret; + // 1.0 Brendan's version, 1.10 BenLunt's version + ver=!memcmp(initrd_p+0x1A6,"SFS",3)?10:0; + bs=1<<(7+(uint8_t)initrd_p[ver?0x1B6:0x1BC]); + end=initrd_p + *((uint64_t *)&initrd_p[ver?0x1AA:0x1B0]) * bs; // base + total_number_of_blocks * blocksize + // get index area + ptr=end - *((uint64_t *)&initrd_p[ver?0x19E:0x1A4]); // end - size of index area + // got a Starting Marker Entry? + if(ptr[0]!=2) + return ret; + DBG(" * SFS 1."); + DBG(ver?"10":"0"); + DBG(kernel); + DBG("\n"); + k=strlen((unsigned char*)kernel); + // iterate on index until we reach the end or Volume Identifier + while(ptr>3; diff --git a/aarch64-rpi/mkboot.c b/aarch64-rpi/mkboot.c new file mode 100644 index 0000000..237725a --- /dev/null +++ b/aarch64-rpi/mkboot.c @@ -0,0 +1,112 @@ +/* + * aarch64-rpi/mkboot.c + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief Little tool to create a RPi compatible MBR for ESP + * + */ + +#include +#include +#include +#include +#include + +/* entry point */ +int main(int argc, char** argv) +{ + // variables + unsigned char data[65536]; + unsigned int np,sp,i,es,ee; + int f; + + // check arguments + if(argc < 2) { + printf( "BOOTBOOT mkboot utility - bztsrc@gitlab\n\nUsage:\n" + " ./mkboot \n\n" + "Maps GPT EFI System Partition into MBR so that Raspberry Pi 3\n" + "firmware can find it's files and boot from it.\n" + "Examples:\n" + " ./mkboot diskimage.dd - modify a disk image file\n" + " ./mkboot /dev/sda - modify a real disk\n"); + return 1; + } + // open file + f = open(argv[1], O_RDONLY); + if(f < 0) { + fprintf(stderr, "mkboot: file not found\n"); + return 2; + } + // read the partitioning tables + if(read(f, data, 65536)==-1) { + close(f); + fprintf(stderr, "mkboot: unable to read file\n"); + return 2; + } + close(f); + if(memcmp((void*)&data[512], "EFI PART", 8)) { + fprintf(stderr, "mkboot: GPT partitioning table not found\n"); + return 2; + } + // get number of partitions and size of partition entry + np=*((unsigned int*)&data[512+80]); sp=*((unsigned int*)&data[512+84]); + i=*((unsigned int*)&data[512+72])*512; es=ee=0; + // get esp start and esp end sectors + while(np--) { + if(*((unsigned int*)&data[i])==0xC12A7328 && *((unsigned int*)&data[i+4])==0x11D2F81F) { + es=*((unsigned int*)&data[i+32]); ee=*((unsigned int*)&data[i+40]); break; + } + i+=sp; + } + if(es==0 || ee==0 || ee + * minor modifications for BOOTBOOT: 2017 bzt (bztsrc@gitlab) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../bootboot.h" + +#define BUF_SIZE 65536 + +struct termios old_tio, new_tio; + +void do_exit(int fd, int res) { + // close FD + if (fd != -1) close(fd); + // restore settings for STDIN_FILENO + if (isatty(STDIN_FILENO)) { + tcsetattr(STDIN_FILENO,TCSANOW,&old_tio); + } + exit(res); +} + +// open serial connection +int open_serial(const char *dev) { + // The termios structure, to be configured for serial interface. + struct termios termios; + + // Open the device, read/write, not the controlling tty, and non-blocking I/O + int fd = open(dev, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (fd == -1) { + // failed to open + return -1; + } + // must be a tty + if (!isatty(fd)) { + fprintf(stderr, "%s is not a tty\n", dev); + do_exit(fd, EXIT_FAILURE); + } + + // Get the attributes. + if(tcgetattr(fd, &termios) == -1) + { + perror("Failed to get attributes of device"); + do_exit(fd, EXIT_FAILURE); + } + + // So, we poll. + termios.c_cc[VTIME] = 0; + termios.c_cc[VMIN] = 0; + + // 8N1 mode, no input/output/line processing masks. + termios.c_iflag = 0; + termios.c_oflag = 0; + termios.c_cflag = CS8 | CREAD | CLOCAL; + termios.c_lflag = 0; + + // Set the baud rate. + if((cfsetispeed(&termios, B115200) < 0) || + (cfsetospeed(&termios, B115200) < 0)) + { + perror("Failed to set baud-rate"); + do_exit(fd, EXIT_FAILURE); + } + + // Write the attributes. + if (tcsetattr(fd, TCSAFLUSH, &termios) == -1) { + perror("tcsetattr()"); + do_exit(fd, EXIT_FAILURE); + } + return fd; +} + +// send initrd to rpi +void send_initrd(int fd, const char *file) { + int file_fd; + off_t off; + uint32_t size; + ssize_t pos, total=0; + char *p; + int done = 0; + + // Set fd blocking + if (fcntl(fd, F_SETFL, 0) == -1) { + perror("fcntl()"); + do_exit(fd, EXIT_FAILURE); + } + + // Open file. If not found, simply continue with terminal + if (file==NULL || file[0]==0 || (file_fd = open(file, O_RDONLY)) == -1) { + return; + } + + // Get initrd size + off = lseek(file_fd, 0L, SEEK_END); + if (off >= INITRD_MAXSIZE*1024*1024) { + fprintf(stderr, "initrd too big\n"); + close(file_fd); + return; + } + // empty file + if (off == 0) { + close(file_fd); + return; + } + size = htole32(off); + lseek(file_fd, 0L, SEEK_SET); + + fprintf(stderr, "### sending initrd %s [%zu byte]\n", file, (size_t)off); + + // send initrd size to RPi + p = (char*)&size; + pos = 0; + while(pos < 4) { + ssize_t len = write(fd, &p[pos], 4 - pos); + if (len == -1) { + perror("write()"); + do_exit(fd, EXIT_FAILURE); + } + pos += len; + } + // wait for OK + char ok_buf[2]; + p = ok_buf; + pos = 0; + while(pos < 2) { + ssize_t len = read(fd, &p[pos], 2 - pos); + if (len == -1) { + perror("read()"); + do_exit(fd, EXIT_FAILURE); + } + pos += len; + } + if (ok_buf[0] != 'O' || ok_buf[1] != 'K') { + fprintf(stderr, "error after sending size\n"); + close(file_fd); + return; + } + + while(!done) { + char buf[BUF_SIZE]; + ssize_t pos = 0; + ssize_t len = read(file_fd, buf, BUF_SIZE); + switch(len) { + case -1: + perror("read()"); + close(file_fd); + return; + case 0: + done = 1; + } + while(len > 0) { + ssize_t len2 = write(fd, &buf[pos], len); + if (len2 == -1) { + perror("write()"); + do_exit(fd, EXIT_FAILURE); + } + len -= len2; + pos += len2; + total += len2; + fprintf(stderr,"%3d%% %d / %d\r",total*100/off, total, off); + } + } + close(file_fd); + + // Set fd non-blocking + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl()"); + do_exit(fd, EXIT_FAILURE); + } + + fprintf(stderr, "### finished sending\n"); + + return; +} + +int main(int argc, char *argv[]) { + int fd, max_fd = STDIN_FILENO; + fd_set rfds, wfds, efds; + char buf[BUF_SIZE]; + size_t start = 0; + size_t end = 0; + int done = 0, leave = 0; + int breaks = 0; + + printf("Raspbootcom V1.0 - BOOTBOOT version\n\n"); + + if (argc < 2) { + printf("USAGE: %s [file]\n", argv[0]); + printf("Example: %s /dev/ttyUSB0 BOOTBOOT/INITRD\n", argv[0]); + exit(EXIT_FAILURE); + } + + // Set STDIN non-blocking and unbuffered + if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl()"); + exit(EXIT_FAILURE); + } + if (isatty(STDIN_FILENO)) { + // get the terminal settings for stdin + if (tcgetattr(STDIN_FILENO, &old_tio) == -1) { + perror("tcgetattr"); + exit(EXIT_FAILURE); + } + + // we want to keep the old setting to restore them a the end + new_tio=old_tio; + + // disable canonical mode (buffered i/o) and local echo + new_tio.c_lflag &= (~ICANON & ~ECHO); + + // set the new settings immediately + if (tcsetattr(STDIN_FILENO, TCSANOW, &new_tio) == -1) { + perror("tcsetattr()"); + do_exit(-1, EXIT_FAILURE); + } + } + + while(!leave) { + // Open device + if ((fd = open_serial(argv[1])) == -1) { + // udev takes a while to change ownership + // so sometimes one gets EPERM + if (errno == ENOENT || errno == ENODEV || errno == EACCES) { + fprintf(stderr, "\r### Waiting for %s...\r", argv[1]); + sleep(1); + continue; + } + perror(argv[1]); + do_exit(fd, EXIT_FAILURE); + } + fprintf(stderr, "### Listening on %s \n", argv[1]); + + // select needs the largeds FD + 1 + if (fd > STDIN_FILENO) { + max_fd = fd + 1; + } else { + max_fd = STDIN_FILENO + 1; + } + + done = 0; + start = end = 0; + while(!done || start != end) { + // Watch stdin and dev for input. + FD_ZERO(&rfds); + if (!done && end < BUF_SIZE) FD_SET(STDIN_FILENO, &rfds); + FD_SET(fd, &rfds); + + // Watch fd for output if needed. + FD_ZERO(&wfds); + if (start != end) FD_SET(fd, &wfds); + + // Watch stdin and dev for error. + FD_ZERO(&efds); + FD_SET(STDIN_FILENO, &efds); + FD_SET(fd, &efds); + + // Wait for something to happend + if (select(max_fd, &rfds, &wfds, &efds, NULL) == -1) { + perror("select()"); + do_exit(fd, EXIT_FAILURE); + } else { + // check for errors + if (FD_ISSET(STDIN_FILENO, &efds)) { + fprintf(stderr, "error on STDIN\n"); + do_exit(fd, EXIT_FAILURE); + } + if (FD_ISSET(fd, &efds)) { + fprintf(stderr, "error on device\n"); + do_exit(fd, EXIT_FAILURE); + } + // RPi is ready to recieve more data, send more + if (FD_ISSET(fd, &wfds)) { + ssize_t len = write(fd, &buf[start], end - start); + if (len == -1) { + perror("write()"); + do_exit(fd, EXIT_FAILURE); + } + start += len; + if (start == end) start = end = 0; + // shift buffer contents + if (end == BUF_SIZE) { + memmove(buf, &buf[start], end - start); + end -= start; + start = 0; + } + } + // input from the user, copy to RPi + if (FD_ISSET(STDIN_FILENO, &rfds)) { + ssize_t len = read(STDIN_FILENO, &buf[end], BUF_SIZE - end); + switch(len) { + case -1: + perror("read()"); + do_exit(fd, EXIT_FAILURE); + case 0: + done = 1; + leave = 1; + } + end += len; + } + // output from the RPi, copy to STDOUT + if (FD_ISSET(fd, &rfds)) { + char buf2[BUF_SIZE]; + ssize_t len = read(fd, buf2, BUF_SIZE); + switch(len) { + case -1: + perror("read()"); + do_exit(fd, EXIT_FAILURE); + case 0: + done = 1; + } + // scan output for tripple break (^C^C^C) + // send initrd on tripple break, otherwise output text + const char *p = buf2; + while(p < &buf2[len]) { + const char *q = index(p, '\x03'); + if (q == NULL) q = &buf2[len]; + if (p == q) { + ++breaks; + ++p; + if (breaks == 3) { + if (start != end) { + fprintf(stderr, "Discarding input after tripple break\n"); + start = end = 0; + } + if(argv[2]!=NULL) + send_initrd(fd, argv[2]); + breaks = 0; + } + } else { + while (breaks > 0) { + ssize_t len2 = write(STDOUT_FILENO, "\x03\x03\x03", breaks); + if (len2 == -1) { + perror("write()"); + do_exit(fd, EXIT_FAILURE); + } + breaks -= len2; + } + while(p < q) { + ssize_t len2 = write(STDOUT_FILENO, p, q - p); + if (len2 == -1) { + perror("write()"); + do_exit(fd, EXIT_FAILURE); + } + p += len2; + } + } + } + } + } + } + close(fd); + } + + do_exit(-1, EXIT_SUCCESS); +} diff --git a/aarch64-rpi/tinf.h b/aarch64-rpi/tinf.h new file mode 100644 index 0000000..3a1fe9d --- /dev/null +++ b/aarch64-rpi/tinf.h @@ -0,0 +1,120 @@ +/* + * uzlib - tiny deflate/inflate library (deflate, gzip, zlib) + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2016 by Paul Sokolovsky + */ + +#ifndef TINF_H_INCLUDED +#define TINF_H_INCLUDED + +/* we don't have stdint.h */ +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long int int64_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; + +/* calling convention */ +#ifndef TINFCC + #ifdef __WATCOMC__ + #define TINFCC __cdecl + #else + #define TINFCC + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ok status, more data produced */ +#define TINF_OK 0 +/* end of compressed stream reached */ +#define TINF_DONE 1 +#define TINF_DATA_ERROR (-3) +#define TINF_CHKSUM_ERROR (-4) +#define TINF_DICT_ERROR (-5) + +/* checksum types */ +#define TINF_CHKSUM_NONE 0 +#define TINF_CHKSUM_ADLER 1 +#define TINF_CHKSUM_CRC 2 + +/* data structures */ + +typedef struct { + unsigned int table[16]; /* table of code length counts */ + unsigned int trans[288]; /* code -> symbol translation table */ +} TINF_TREE; + +struct TINF_DATA; +typedef struct TINF_DATA { + TINF_TREE ltree; /* dynamic length/symbol tree */ + TINF_TREE dtree; /* dynamic distance tree */ + const unsigned char *source; + /* If source above is NULL, this function will be used to read + next byte from source stream */ + unsigned char (*readSource)(volatile struct TINF_DATA *data); + + unsigned int tag; + unsigned int bitcount; + + /* Buffer start */ + unsigned char *destStart; + /* Buffer total size */ + unsigned int destSize; + /* Current pointer in buffer */ + unsigned char *dest; + /* Remaining bytes in buffer */ + unsigned int destRemaining; + + /* Accumulating checksum */ + unsigned int checksum; + unsigned int checksum_type; + + int btype; + int bfinal; + unsigned int curlen; + int lzOff; +} TINF_DATA; + +#define TINF_PUT(d, c) \ + { \ + *d->dest++ = c; \ + } + +unsigned char TINFCC uzlib_get_byte(volatile TINF_DATA *d); + +/* Decompression API */ + +void TINFCC uzlib_init(void); +void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); +int TINFCC uzlib_uncompress(volatile TINF_DATA *d); +int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); + +int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); +int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); + +/* Compression API */ + +void TINFCC uzlib_compress(void *data, const uint8_t *src, unsigned slen); + +/* Checksum API */ + +/* prev_sum is previous value for incremental computation, 1 initially */ +uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); +/* crc is previous value for incremental computation, 0xffffffff initially */ +uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TINF_H_INCLUDED */ diff --git a/aarch64-rpi/tinflate.c b/aarch64-rpi/tinflate.c new file mode 100644 index 0000000..8508477 --- /dev/null +++ b/aarch64-rpi/tinflate.c @@ -0,0 +1,475 @@ +/* + * tinflate - tiny inflate + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2016 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +uint32_t tinf_get_le_uint32(TINF_DATA *d); +uint32_t tinf_get_be_uint32(TINF_DATA *d); + +/* --------------------------------------------------- * + * -- uninitialized global data (static structures) -- * + * --------------------------------------------------- */ + +#ifdef RUNTIME_BITS_TABLES + +/* extra bits and base tables for length codes */ +unsigned char length_bits[30]; +unsigned short length_base[30]; + +/* extra bits and base tables for distance codes */ +unsigned char dist_bits[30]; +unsigned short dist_base[30]; + +#else + +const unsigned char length_bits[30] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5 +}; +const unsigned short length_base[30] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258 +}; + +const unsigned char dist_bits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; +const unsigned short dist_base[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, + 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577 +}; + +#endif + +/* special ordering of code length codes */ +const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +/* ----------------------- * + * -- utility functions -- * + * ----------------------- */ + +#ifdef RUNTIME_BITS_TABLES +/* build extra bits and base tables */ +static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) +{ + int i, sum; + + /* build bits table */ + for (i = 0; i < delta; ++i) bits[i] = 0; + for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; + + /* build base table */ + for (sum = first, i = 0; i < 30; ++i) + { + base[i] = sum; + sum += 1 << bits[i]; + } +} +#endif + +/* build the fixed huffman trees */ +static void tinf_build_fixed_trees(volatile TINF_TREE *lt, volatile TINF_TREE *dt) +{ + int i; + + /* build fixed length tree */ + for (i = 0; i < 7; ++i) lt->table[i] = 0; + + lt->table[7] = 24; + lt->table[8] = 152; + lt->table[9] = 112; + + for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; + for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; + for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; + + /* build fixed distance tree */ + for (i = 0; i < 5; ++i) dt->table[i] = 0; + + dt->table[5] = 32; + + for (i = 0; i < 32; ++i) dt->trans[i] = i; +} + +/* given an array of code lengths, build a tree */ +static void tinf_build_tree(volatile TINF_TREE *t, const unsigned char *lengths, unsigned int num) +{ + unsigned short offs[16]; + unsigned int i, sum; + + /* clear code length count table */ + for (i = 0; i < 16; ++i) t->table[i] = 0; + + /* scan symbol lengths, and sum code length counts */ + for (i = 0; i < num; ++i) t->table[lengths[i]]++; + + t->table[0] = 0; + + /* compute offset table for distribution sort */ + for (sum = 0, i = 0; i < 16; ++i) + { + offs[i] = sum; + sum += t->table[i]; + } + + /* create code->symbol translation table (symbols sorted by code) */ + for (i = 0; i < num; ++i) + { + if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + } +} + +/* ---------------------- * + * -- decode functions -- * + * ---------------------- */ + +unsigned char uzlib_get_byte(volatile TINF_DATA *d) +{ + if (d->source) { + return *d->source++; + } + return d->readSource(d); +} + +uint32_t tinf_get_le_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val >> 8 | uzlib_get_byte(d) << 24; + } + return val; +} + +uint32_t tinf_get_be_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val << 8 | uzlib_get_byte(d); + } + return val; +} + +/* get one bit from source stream */ +static int tinf_getbit(volatile TINF_DATA *d) +{ + unsigned int bit; + + /* check if tag is empty */ + if (!d->bitcount--) + { + /* load next tag */ + d->tag = uzlib_get_byte(d); + d->bitcount = 7; + } + + /* shift bit out of tag */ + bit = d->tag & 0x01; + d->tag >>= 1; + + return bit; +} + +/* read a num bit value from a stream and add base */ +static unsigned int tinf_read_bits(volatile TINF_DATA *d, int num, int base) +{ + unsigned int val = 0; + + /* read num bits */ + if (num) + { + unsigned int limit = 1 << (num); + unsigned int mask; + + for (mask = 1; mask < limit; mask *= 2) + if (tinf_getbit(d)) val += mask; + } + + return val + base; +} + +/* given a data stream and a tree, decode a symbol */ +static int tinf_decode_symbol(volatile TINF_DATA *d, volatile TINF_TREE *t) +{ + int sum = 0, cur = 0, len = 0; + + /* get more bits while code value is above sum */ + do { + + cur = 2*cur + tinf_getbit(d); + + ++len; + + sum += t->table[len]; + cur -= t->table[len]; + + } while (cur >= 0); + + return t->trans[sum + cur]; +} + +/* given a data stream, decode dynamic trees from it */ +static void tinf_decode_trees(volatile TINF_DATA *d, volatile TINF_TREE *lt, volatile TINF_TREE *dt) +{ + unsigned char lengths[288+32]; + unsigned int hlit, hdist, hclen; + unsigned int i, num, length; + + /* get 5 bits HLIT (257-286) */ + hlit = tinf_read_bits(d, 5, 257); + + /* get 5 bits HDIST (1-32) */ + hdist = tinf_read_bits(d, 5, 1); + + /* get 4 bits HCLEN (4-19) */ + hclen = tinf_read_bits(d, 4, 4); + + for (i = 0; i < 19; ++i) lengths[i] = 0; + + /* read code lengths for code length alphabet */ + for (i = 0; i < hclen; ++i) + { + /* get 3 bits code length (0-7) */ + unsigned int clen = tinf_read_bits(d, 3, 0); + + lengths[clcidx[i]] = clen; + } + + /* build code length tree, temporarily use length tree */ + tinf_build_tree(lt, lengths, 19); + + /* decode code lengths for the dynamic trees */ + for (num = 0; num < hlit + hdist; ) + { + int sym = tinf_decode_symbol(d, lt); + + switch (sym) + { + case 16: + /* copy previous code length 3-6 times (read 2 bits) */ + { + unsigned char prev = lengths[num - 1]; + for (length = tinf_read_bits(d, 2, 3); length; --length) + { + lengths[num++] = prev; + } + } + break; + case 17: + /* repeat code length 0 for 3-10 times (read 3 bits) */ + for (length = tinf_read_bits(d, 3, 3); length; --length) + { + lengths[num++] = 0; + } + break; + case 18: + /* repeat code length 0 for 11-138 times (read 7 bits) */ + for (length = tinf_read_bits(d, 7, 11); length; --length) + { + lengths[num++] = 0; + } + break; + default: + /* values 0-15 represent the actual code lengths */ + lengths[num++] = sym; + break; + } + } + + /* build dynamic trees */ + tinf_build_tree(lt, lengths, hlit); + tinf_build_tree(dt, lengths + hlit, hdist); +} + +/* ----------------------------- * + * -- block inflate functions -- * + * ----------------------------- */ + +/* given a stream and two trees, inflate a block of data */ +static int tinf_inflate_block_data(volatile TINF_DATA *d, volatile TINF_TREE *lt, volatile TINF_TREE *dt) +{ + if (d->curlen == 0) { + unsigned int offs; + int dist; + int sym = tinf_decode_symbol(d, lt); + //printf("huff sym: %02x\n", sym); + + /* literal byte */ + if (sym < 256) { + TINF_PUT(d, sym); + return TINF_OK; + } + + /* end of block */ + if (sym == 256) { + return TINF_DONE; + } + + /* substring from sliding dictionary */ + sym -= 257; + /* possibly get more bits from length code */ + d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); + + dist = tinf_decode_symbol(d, dt); + /* possibly get more bits from distance code */ + offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); + d->lzOff = -offs; + } + + /* copy next byte from dict substring */ + d->dest[0] = d->dest[d->lzOff]; + d->dest++; + d->curlen--; + return TINF_OK; +} + +/* inflate an uncompressed block of data */ +static int tinf_inflate_uncompressed_block(volatile TINF_DATA *d) +{ + if (d->curlen == 0) { + unsigned int length, invlength; + + /* get length */ + length = uzlib_get_byte(d) + 256 * uzlib_get_byte(d); + /* get one's complement of length */ + invlength = uzlib_get_byte(d) + 256 * uzlib_get_byte(d); + /* check length */ + if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; + + /* increment length to properly return TINF_DONE below, without + producing data at the same time */ + d->curlen = length + 1; + + /* make sure we start next block on a byte boundary */ + d->bitcount = 0; + } + + if (--d->curlen == 0) { + return TINF_DONE; + } + + unsigned char c = uzlib_get_byte(d); + TINF_PUT(d, c); + return TINF_OK; +} + +/* ---------------------- * + * -- public functions -- * + * ---------------------- */ + +/* initialize global (static) data */ +void uzlib_init(void) +{ +#ifdef RUNTIME_BITS_TABLES + /* build extra bits and base tables */ + tinf_build_bits_base(length_bits, length_base, 4, 3); + tinf_build_bits_base(dist_bits, dist_base, 2, 1); + + /* fix a special case */ + length_bits[28] = 0; + length_base[28] = 258; +#endif +} + +/* inflate next byte of compressed stream */ +int uzlib_uncompress(volatile TINF_DATA *d) +{ + do { + int res; + + /* start a new block */ + if (d->btype == -1) { +next_blk: + /* read final block flag */ + d->bfinal = tinf_getbit(d); + /* read block type (2 bits) */ + d->btype = tinf_read_bits(d, 2, 0); + + //printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); + + if (d->btype == 1) { + /* build fixed huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + } else if (d->btype == 2) { + /* decode trees from stream */ + tinf_decode_trees(d, &d->ltree, &d->dtree); + } + } + + /* process current block */ + switch (d->btype) + { + case 0: + /* decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(d); + break; + case 1: + case 2: + /* decompress block with fixed/dyanamic huffman trees */ + /* trees were decoded previously, so it's the same routine for both */ + res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); + break; + default: + return TINF_DATA_ERROR; + } + + if (res == TINF_DONE && !d->bfinal) { + /* the block has ended (without producing more data), but we + can't return without data, so start procesing next block */ + goto next_blk; + } + + if (res != TINF_OK) { + return res; + } + + } while (--d->destSize); + + return TINF_OK; +} diff --git a/boot.bin b/boot.bin new file mode 100644 index 0000000..0c0f4b9 Binary files /dev/null and b/boot.bin differ diff --git a/bootboot.bin b/bootboot.bin new file mode 100644 index 0000000..2eb4028 Binary files /dev/null and b/bootboot.bin differ diff --git a/bootboot.efi b/bootboot.efi new file mode 100755 index 0000000..2889479 Binary files /dev/null and b/bootboot.efi differ diff --git a/bootboot.h b/bootboot.h new file mode 100644 index 0000000..452ad12 --- /dev/null +++ b/bootboot.h @@ -0,0 +1,142 @@ +/* + * bootboot.h + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief The BOOTBOOT structure + * + */ + +#ifndef _BOOTBOOT_H_ +#define _BOOTBOOT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOTBOOT_MAGIC "BOOT" + +// minimum protocol level: +// hardcoded kernel name, static kernel memory addresses +#define PROTOCOL_MINIMAL 0 +// static protocol level: +// kernel name parsed from environment, static kernel memory addresses +#define PROTOCOL_STATIC 1 +// dynamic protocol level: +// kernel name parsed, kernel memory addresses from ELF or PE symbols +#define PROTOCOL_DYNAMIC 2 +// big-endian flag +#define PROTOCOL_BIGENDIAN 0x80 + +// loader types, just informational +#define LOADER_BIOS 0 +#define LOADER_UEFI 1 +#define LOADER_RPI 2 + +// framebuffer pixel format, only 32 bits supported +#define FB_ARGB 0 +#define FB_RGBA 1 +#define FB_ABGR 2 +#define FB_BGRA 3 + +// mmap entry, type is stored in least significant tetrad (half byte) of size +// this means size described in 16 byte units (not a problem, most modern +// firmware report memory in pages, 4096 byte units anyway). +typedef struct { + uint64_t ptr; + uint64_t size; +} __attribute__((packed)) MMapEnt; +#define MMapEnt_Ptr(a) (a->ptr) +#define MMapEnt_Size(a) (a->size & 0xFFFFFFFFFFFFFFF0) +#define MMapEnt_Type(a) (a->size & 0xF) +#define MMapEnt_IsFree(a) ((a->size&0xF)==1||(a->size&0xF)==2) + +#define MMAP_USED 0 // don't use. Reserved or unknown regions +#define MMAP_FREE 1 // usable memory +#define MMAP_ACPIFREE 2 // free to use after acpi tables are parsed +#define MMAP_ACPINVS 3 // don't use. Acpi non-volatile +#define MMAP_MMIO 4 // memory mapped IO region + +#define INITRD_MAXSIZE 16 //Mb + +typedef struct { + uint8_t magic[4]; // 'BOOT', first 64 bytes are platform independent + uint32_t size; // length of bootboot structure, minimum 128 + + uint8_t protocol; // 1, static addresses, see PROTOCOL_* above + uint8_t loader_type; // see LOADER_* above + uint8_t pagesize; // in power of two, 12 = 4096 + uint8_t fb_type; // framebuffer type, see FB_* above + + int16_t timezone; // in minutes -1440..1440 + uint16_t bspid; // Bootsrap processor ID (Local APIC Id on x86_64) + + uint8_t datetime[8]; // in BCD yyyymmddhhiiss UTC (independent to timezone) + + uint64_t initrd_ptr; // ramdisk image position and size + uint64_t initrd_size; + + uint8_t *fb_ptr; // framebuffer pointer and dimensions + uint32_t fb_size; + uint32_t fb_width; + uint32_t fb_height; + uint32_t fb_scanline; + + // the rest (64 bytes) is platform specific + union { + struct { + uint64_t acpi_ptr; + uint64_t smbi_ptr; + uint64_t efi_ptr; + uint64_t mp_ptr; + uint64_t unused0; + uint64_t unused1; + uint64_t unused2; + uint64_t unused3; + } x86_64; + struct { + uint64_t acpi_ptr; + uint64_t mmio_ptr; + uint64_t unused0; + uint64_t unused1; + uint64_t unused2; + uint64_t unused3; + uint64_t unused4; + uint64_t unused5; + } aarch64; + }; + + /* from 128th byte, MMapEnt[], more records may follow */ + MMapEnt mmap; + /* use like this: + * MMapEnt *mmap_ent = &bootboot.mmap; mmap_ent++; + * until you reach bootboot->size */ +} __attribute__((packed)) BOOTBOOT; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bootboot.img b/bootboot.img new file mode 100755 index 0000000..4516cf1 Binary files /dev/null and b/bootboot.img differ diff --git a/bootboot.rom b/bootboot.rom new file mode 100644 index 0000000..cb7eded Binary files /dev/null and b/bootboot.rom differ diff --git a/bootboot_spec_1st_ed.pdf b/bootboot_spec_1st_ed.pdf new file mode 100644 index 0000000..6c19aff Binary files /dev/null and b/bootboot_spec_1st_ed.pdf differ diff --git a/mykernel/Makefile b/mykernel/Makefile new file mode 100644 index 0000000..c1f4ff0 --- /dev/null +++ b/mykernel/Makefile @@ -0,0 +1,50 @@ +# +# mykernel/Makefile +# +# Copyright (C) 2017 bzt (bztsrc@gitlab) +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, copy, +# modify, merge, publish, distribute, sublicense, and/or sell copies +# of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +# +# This file is part of the BOOTBOOT Protocol package. +# @brief An example Makefile for sample kernel +# +# + +CFLAGS = -Wall -fpic -ffreestanding -fno-stack-protector -nostdinc -nostdlib -I../ + +all: mykernel.x86_64.elf mykernel.aarch64.elf + +mykernel.x86_64.elf: kernel.c + x86_64-elf-gcc $(CFLAGS) -mno-red-zone -c kernel.c -o kernel.o + x86_64-elf-ld -r -b binary -o font.o font.psf + x86_64-elf-ld -nostdlib -nostartfiles -T link.ld kernel.o font.o -o mykernel.x86_64.elf + x86_64-elf-strip -s -K fb -K bootboot -K environment mykernel.x86_64.elf + x86_64-elf-readelf -hls mykernel.x86_64.elf >mykernel.x86_64.txt + +mykernel.aarch64.elf: kernel.c + aarch64-elf-gcc $(CFLAGS) -c kernel.c -o kernel.o + aarch64-elf-ld -r -b binary -o font.o font.psf + aarch64-elf-ld -nostdlib -nostartfiles -T link.ld kernel.o font.o -o mykernel.aarch64.elf + aarch64-elf-strip -s -K fb -K bootboot -K environment mykernel.aarch64.elf + aarch64-elf-readelf -hls mykernel.aarch64.elf >mykernel.aarch64.txt + +clean: + rm *.o *.elf *.txt diff --git a/mykernel/font.psf b/mykernel/font.psf new file mode 100644 index 0000000..3e67693 Binary files /dev/null and b/mykernel/font.psf differ diff --git a/mykernel/kernel.c b/mykernel/kernel.c new file mode 100644 index 0000000..810c0fb --- /dev/null +++ b/mykernel/kernel.c @@ -0,0 +1,106 @@ +/* + * mykernel/kernel.c + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief A sample BOOTBOOT compatible kernel + * + */ + +/* function to display a string, see below */ +void puts(char *s); + +/* we don't assume stdint.h exists */ +typedef short int int16_t; +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long int uint64_t; + +#include + +/* imported virtual addresses, see linker script */ +extern BOOTBOOT bootboot; // see bootboot.h +extern unsigned char *environment; // configuration, UTF-8 text key=value pairs +extern uint8_t fb; // linear framebuffer mapped + +/****************************************** + * Entry point, called by BOOTBOOT Loader * + ******************************************/ +void _start() +{ + int x, y, s=bootboot.fb_scanline, w=bootboot.fb_width, h=bootboot.fb_height; + + // cross-hair to see screen dimension detected correctly + for(y=0;ywidth+7)/8; + while(*s) { + unsigned char *glyph = (unsigned char*)&_binary_font_psf_start + font->headersize + + (*s>0&&*snumglyph?*s:0)*font->bytesperglyph; + offs = (kx * (font->width+1) * 4); + for(y=0;yheight;y++) { + line=offs; mask=1<<(font->width-1); + for(x=0;xwidth;x++) { + *((uint32_t*)((uint64_t)&fb+line))=((int)*glyph) & (mask)?0xFFFFFF:0; + mask>>=1; line+=4; + } + *((uint32_t*)((uint64_t)&fb+line))=0; glyph+=bpl; offs+=bootboot.fb_scanline; + } + s++; kx++; + } +} diff --git a/mykernel/link.ld b/mykernel/link.ld new file mode 100644 index 0000000..c16c4ea --- /dev/null +++ b/mykernel/link.ld @@ -0,0 +1,48 @@ +/* + * mykernel/link.ld + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief An example linker script for sample kernel + * + */ + +mmio = 0xfffffffffa000000; +fb = 0xfffffffffc000000; +SECTIONS +{ + . = 0xffffffffffe00000; + bootboot = .; . += 4096; + environment = .; . += 4096; + .text : { + KEEP(*(.text.boot)) *(.text .text.*) /* code */ + *(.rodata .rodata.*) /* data */ + *(.data .data.*) + } + .bss (NOLOAD) : { /* bss */ + . = ALIGN(16); + *(.bss .bss.*) + *(COMMON) + } +} diff --git a/mykernel/mykernel.aarch64.elf b/mykernel/mykernel.aarch64.elf new file mode 100755 index 0000000..00a056a Binary files /dev/null and b/mykernel/mykernel.aarch64.elf differ diff --git a/mykernel/mykernel.aarch64.txt b/mykernel/mykernel.aarch64.txt new file mode 100644 index 0000000..5a4c6ab --- /dev/null +++ b/mykernel/mykernel.aarch64.txt @@ -0,0 +1,42 @@ +ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: EXEC (Executable file) + Machine: AArch64 + Version: 0x1 + Entry point address: 0xffffffffffe02000 + Start of program headers: 64 (bytes into file) + Start of section headers: 3760 (bytes into file) + Flags: 0x0 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 1 + Size of section headers: 64 (bytes) + Number of section headers: 9 + Section header string table index: 8 + +Program Headers: + Type Offset VirtAddr PhysAddr + FileSiz MemSiz Flags Align + LOAD 0x0000000000000078 0xffffffffffe02000 0xffffffffffe02000 + 0x0000000000000cf0 0x0000000000000cf0 RWE 0x8 + + Section to Segment mapping: + Segment Sections... + 00 .text .got .got.plt + +Symbol table '.symtab' contains 9 entries: + Num: Value Size Type Bind Vis Ndx Name + 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND + 1: ffffffffffe02000 0 SECTION LOCAL DEFAULT 1 + 2: ffffffffffe02cb8 0 SECTION LOCAL DEFAULT 2 + 3: ffffffffffe02cd8 0 SECTION LOCAL DEFAULT 3 + 4: ffffffffffe02cf0 0 SECTION LOCAL DEFAULT 4 + 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 + 6: ffffffffffe01000 0 NOTYPE GLOBAL DEFAULT 1 environment + 7: fffffffffc000000 0 NOTYPE GLOBAL DEFAULT ABS fb + 8: ffffffffffe00000 0 NOTYPE GLOBAL DEFAULT 1 bootboot diff --git a/mykernel/mykernel.x86_64.elf b/mykernel/mykernel.x86_64.elf new file mode 100755 index 0000000..0f91b47 Binary files /dev/null and b/mykernel/mykernel.x86_64.elf differ diff --git a/mykernel/mykernel.x86_64.txt b/mykernel/mykernel.x86_64.txt new file mode 100644 index 0000000..1dac104 --- /dev/null +++ b/mykernel/mykernel.x86_64.txt @@ -0,0 +1,42 @@ +ELF Header: + Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 + Class: ELF64 + Data: 2's complement, little endian + Version: 1 (current) + OS/ABI: UNIX - System V + ABI Version: 0 + Type: EXEC (Executable file) + Machine: Advanced Micro Devices X86-64 + Version: 0x1 + Entry point address: 0xffffffffffe02000 + Start of program headers: 64 (bytes into file) + Start of section headers: 3512 (bytes into file) + Flags: 0x0 + Size of this header: 64 (bytes) + Size of program headers: 56 (bytes) + Number of program headers: 1 + Size of section headers: 64 (bytes) + Number of section headers: 9 + Section header string table index: 8 + +Program Headers: + Type Offset VirtAddr PhysAddr + FileSiz MemSiz Flags Align + LOAD 0x0000000000000078 0xffffffffffe02000 0xffffffffffe02000 + 0x0000000000000bf0 0x0000000000000bf0 RWE 0x8 + + Section to Segment mapping: + Segment Sections... + 00 .text .eh_frame .got.plt + +Symbol table '.symtab' contains 9 entries: + Num: Value Size Type Bind Vis Ndx Name + 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND + 1: ffffffffffe02000 0 SECTION LOCAL DEFAULT 1 + 2: ffffffffffe02b70 0 SECTION LOCAL DEFAULT 2 + 3: ffffffffffe02bd8 0 SECTION LOCAL DEFAULT 3 + 4: ffffffffffe02bf0 0 SECTION LOCAL DEFAULT 4 + 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 + 6: ffffffffffe01000 0 NOTYPE GLOBAL DEFAULT 1 environment + 7: fffffffffc000000 0 NOTYPE GLOBAL DEFAULT ABS fb + 8: ffffffffffe00000 0 NOTYPE GLOBAL DEFAULT 1 bootboot diff --git a/mykernel/screenshot.png b/mykernel/screenshot.png new file mode 100644 index 0000000..d63e6f9 Binary files /dev/null and b/mykernel/screenshot.png differ diff --git a/x86_64-bios/Makefile b/x86_64-bios/Makefile new file mode 100644 index 0000000..4c3db3d --- /dev/null +++ b/x86_64-bios/Makefile @@ -0,0 +1,15 @@ +all: + @# NOTE: GNU assembler cannot mix 16 bit real mode with 32 bit prot mode instructions + @# as easily as I need it in a boot loader. So I decided to use fasm. + @echo " src x86_64-bios (MultiBoot / BIOS)" + @fasm boot.asm ../boot.bin >/dev/null + @fasm bootboot.asm ../bootboot.bin >/dev/null + +mkboot: ../boot.bin mkboot.c + @echo " src mkboot" + @ld -r -b binary -o boot.o ../boot.bin + @gcc boot.o mkboot.c -o mkboot + @rm boot.o 2>/dev/null || true + +clean: + @rm *.o ../mbr.bin ../bootboot.bin mkboot >/dev/null 2>/dev/null || true diff --git a/x86_64-bios/README.md b/x86_64-bios/README.md new file mode 100644 index 0000000..44fa466 --- /dev/null +++ b/x86_64-bios/README.md @@ -0,0 +1,34 @@ +BOOTBOOT BIOS / Multiboot Implementation +======================================== + +See [BOOTBOOT Protocol](https://gitlab.com/bztsrc/bootboot) for common details. + +On [BIOS](http://www.scs.stanford.edu/05au-cs240c/lab/specsbbs101.pdf) based systems, the same image can be loaded via +[Multiboot](https://www.gnu.org/software/grub/manual/multiboot/multiboot.html), +chainload from MBR or VBR (GPT hybrid booting) or run as a BIOS Expansion ROM +(so not only the ramdisk can be in ROM, but the loader as well). + +Machine state +------------- + +IRQs masked. GDT unspecified, but valid, IDT unset. Code is running in supervisor mode in ring 0. + +Installation +------------ + +1. *BIOS disk*: copy __bootboot.bin__ to **_FS0:\BOOTBOOT\LOADER_**. You can place it inside your INITRD partition + or outside of partition area as well (with `dd conv=notrunc oseek=x`). Finally install __boot.bin__ in the + master boot record (or in volume boot record if you have a boot manager), saving bootboot.bin's first sector's + LBA number in a dword at 0x1B0. The [mkboot](https://gitlab.com/bztsrc/bootboot/blob/master/x86_64-bios/mkboot.c) + utility will do that for you. + +2. *BIOS ROM*: install __bootboot.bin__ in a **_BIOS Expansion ROM_**. + +3. *GRUB*: specify __bootboot.bin__ as "kernel" in grub.cfg, or you can chainload __boot.bin__. + +Limitations +----------- + + - As it boots in protected mode, it only maps the first 4G of RAM. + - Compressed initrd in ROM is limited to ~96k. + - The CMOS nvram does not store timezone, so always GMT+0 returned in bootboot.timezone. diff --git a/x86_64-bios/boot.asm b/x86_64-bios/boot.asm new file mode 100644 index 0000000..57c4d13 --- /dev/null +++ b/x86_64-bios/boot.asm @@ -0,0 +1,218 @@ +;* +;* x86_64-bios/boot.asm +;* +;* Copyright (C) 2017 bzt (bztsrc@gitlab) +;* +;* Permission is hereby granted, free of charge, to any person +;* obtaining a copy of this software and associated documentation +;* files (the "Software"), to deal in the Software without +;* restriction, including without limitation the rights to use, copy, +;* modify, merge, publish, distribute, sublicense, and/or sell copies +;* of the Software, and to permit persons to whom the Software is +;* furnished to do so, subject to the following conditions: +;* +;* The above copyright notice and this permission notice shall be +;* included in all copies or substantial portions of the Software. +;* +;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;* DEALINGS IN THE SOFTWARE. +;* +;* This file is part of the BOOTBOOT Protocol package. +;* @brief 1nd stage loader, compatible with BIOS boot specification +;* + +;********************************************************************* +;* Macros * +;********************************************************************* + +;LBA packet fields +virtual at lba_packet +lbapacket.size: dw ? +lbapacket.count:dw ? +lbapacket.addr0:dw ? +lbapacket.addr1:dw ? +lbapacket.sect0:dw ? +lbapacket.sect1:dw ? +lbapacket.sect2:dw ? +lbapacket.sect3:dw ? +end virtual + +;memory locations +ldr.header equ 800h ;position of 2nd stage loader +ldr.executor equ 804h ;ptr to init code + +;Writes a message on screen. +macro print msg +{ +if ~ msg eq si + push si + mov si, msg +end if + call printfunc +if ~ msg eq si + pop si +end if +} + +;********************************************************************* +;* code * +;********************************************************************* + +;-----------------ENTRY POINT called by BIOS-------------------- + ORG 0600h + USE16 + +bootboot_record: + jmp short .skipid + nop +.system: db "BOOTBOOT",0 + ;skip BPB area so that we can use this + ;boot code on a FAT volume if needed + db 05Ah-($-$$) dup 0 +.skipid: ;relocate our code to offset 600h + cli + xor ax, ax + mov ss, ax + mov sp, 600h + push ax + pop es + push cs + pop ds + ;find our position in memory. + call .getaddr +.getaddr: pop si + sub si, .getaddr-bootboot_record + cld + mov di, sp + ;clear data area 500h-600h + sub di, 100h + mov cx, 80h + repnz stosw + ;and copy ourselves to 600h + mov cx, 100h + repnz movsw + jmp 0:.start + +.start: ;save boot drive code + mov byte [drive], dl + ;initialize lba packet + mov byte [lbapacket.size], 16 + mov byte [lbapacket.count], 58 + mov byte [lbapacket.addr0+1], 08h ;to address 800h + ;check for lba presistance - floppy not supported any more + ;we use pendrive as removable media for a long time + cmp dl, byte 80h + jl .nolba + cmp dl, byte 84h + jae .nostage2err +.notfloppy: mov ah, byte 41h + mov bx, word 55AAh + int 13h + jc .nolba + cmp bx, word 0AA55h + jne .nolba + test cl, byte 1 + jnz .lbaok +.nolba: mov si, lbanotf + jmp diefunc +.lbaok: ;try to load stage2 - it's a continous area on disk + ;started at given sector with maximum size of 7400h bytes + mov si, stage2_addr + mov di, lbapacket.sect0 + push di + movsw + movsw + movsw + movsw + call loadsectorfunc + + ;do we have a 2nd stage loader? +.chk: cmp word [ldr.header], bx + jne .nostage2 + cmp byte [ldr.header+3], 0E9h + jne .nostage2 + ;invoke stage2 real mode code + print okay + mov ax, [ldr.executor] + add ax, ldr.executor+3 + jmp ax + +.nostage2: ;try to load stage2 from a RAID mirror + inc byte [drive] + cmp byte [drive], 84h + jl .lbaok +.nostage2err: + mov si, stage2notf + ;fall into the diefunc code + +;********************************************************************* +;* functions * +;********************************************************************* +;writes the reason, waits for a key and reboots. +diefunc: + print bootboot_record.system + print panic + call printfunc + mov si, found + call printfunc + sti + xor ax, ax + int 16h + mov al, 0FEh + out 64h, al + jmp far 0FFFFh:0 ;invoke BIOS POST routine + +;loads an LBA sector +loadsectorfunc: + push bx + push si + mov ah, byte 42h + mov dl, byte [drive] + mov si, lba_packet + int 13h + pop si + pop bx + ret + +;ds:si zero terminated string to write +printfunc: + lodsb + or al, al + jz .end + mov ah, byte 0Eh + mov bx, word 11 + int 10h + jmp printfunc +.end: ret + +;********************************************************************* +;* data area * +;********************************************************************* + +panic: db "-PANIC: ",0 +lbanotf: db "LBA support",0 +stage2notf: db "FS0:\BOOTBOOT\LOADER",0 +found: db " not found",10,13,0 +okay: db "Booting LOADER...",10,13,0 +drive: db 0 +lba_packet: db 01B0h-($-$$) dup 0 + +;right before the partition table some data +stage2_addr:dd 0FFFFFFFFh,0 ;1B0h 2nd stage loader address + ;this should be set by mkfs + +diskid: dd 0 ;1B8h WinNT expects it here + dw 0 + +;1BEh first partition entry + + ;padding and magic + db 01FEh-($-$$) dup 0 + db 55h,0AAh +bootboot_record_end: diff --git a/x86_64-bios/bootboot.asm b/x86_64-bios/bootboot.asm new file mode 100644 index 0000000..fc89ccd --- /dev/null +++ b/x86_64-bios/bootboot.asm @@ -0,0 +1,1965 @@ +;* +;* x86_64-bios/bootboot.asm +;* +;* Copyright (C) 2017 bzt (bztsrc@gitlab) +;* +;* Permission is hereby granted, free of charge, to any person +;* obtaining a copy of this software and associated documentation +;* files (the "Software"), to deal in the Software without +;* restriction, including without limitation the rights to use, copy, +;* modify, merge, publish, distribute, sublicense, and/or sell copies +;* of the Software, and to permit persons to whom the Software is +;* furnished to do so, subject to the following conditions: +;* +;* The above copyright notice and this permission notice shall be +;* included in all copies or substantial portions of the Software. +;* +;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;* DEALINGS IN THE SOFTWARE. +;* +;* This file is part of the BOOTBOOT Protocol package. +;* @brief Booting code for BIOS and MultiBoot +;* +;* 2nd stage loader, compatible with GRUB and +;* BIOS boot specification 1.0.1 too (even expansion ROM). +;* +;* memory occupied: 800-7C00 +;* +;* Memory map +;* 0h - 600h reserved for the system +;* 600h - 800h stage1 (MBR) +;* 800h - 6C00h stage2 (this) +;* 6C00h - 7C00h stack +;* 8000h - 9000h bootboot structure +;* 9000h - A000h environment +;* A000h - B000h disk buffer / PML4 +;* B000h - C000h PDPE, higher half core 4K slots +;* C000h - D000h PDE 4K +;* D000h - E000h PTE 4K +;* E000h - F000h PDPE, 4G physical RAM identity mapped 2M +;* F000h -10000h PDE 2M +;* 10000h -11000h PDE 2M +;* 11000h -12000h PDE 2M +;* 12000h -13000h PDE 2M +;* 13000h -14000h PTE 4K +;* 14000h -15000h core stack +;* +;* At first big enough free hole, initrd. Usually at 1Mbyte. +;* + +DEBUG equ 1 + +;get Core boot parameter block +include "bootboot.inc" + +;VBE filter (available, has additional info, color, graphic, linear fb) +VBE_MODEFLAGS equ 1+2+8+16+128 + +;********************************************************************* +;* Macros * +;********************************************************************* + +;Writes a message on screen. +macro real_print msg +{ +if ~ msg eq si + push si + mov si, msg +end if + call real_printfunc +if ~ msg eq si + pop si +end if +} + +;protected and real mode are functions, because we have to switch beetween +macro real_protmode +{ + USE16 + call near real_protmodefunc + USE32 +} + +macro prot_realmode +{ + USE32 + call near prot_realmodefunc + USE16 +} + +;edx:eax sector, edi:pointer +macro prot_readsector +{ + call near prot_readsectorfunc +} + +macro DBG msg +{ +if DEBUG eq 1 + real_print msg +end if +} + +macro DBG32 msg +{ +if DEBUG eq 1 + prot_realmode + real_print msg + real_protmode +end if +} + +virtual at 0 + bpb.jmp db 3 dup 0 + bpb.oem db 8 dup 0 + bpb.bps dw 0 + bpb.spc db 0 + bpb.rsc dw 0 + bpb.nf db 0 ;16 + bpb.nr dw 0 + bpb.ts16 dw 0 + bpb.media db 0 + bpb.spf16 dw 0 ;22 + bpb.spt dw 0 + bpb.nh dw 0 + bpb.hs dd 0 + bpb.ts32 dd 0 + bpb.spf32 dd 0 ;36 + bpb.flg dd 0 + bpb.rc dd 0 ;44 + bpb.vol db 6 dup 0 + bpb.fst db 8 dup 0 ;54 + bpb.dmy db 20 dup 0 + bpb.fst2 db 8 dup 0 ;84 +end virtual + +virtual at 0 + fatdir.name db 8 dup 0 + fatdir.ext db 3 dup 0 + fatdir.attr db 9 dup 0 + fatdir.ch dw 0 + fatdir.attr2 dd 0 + fatdir.cl dw 0 + fatdir.size dd 0 +end virtual + +;********************************************************************* +;* header * +;********************************************************************* +;offs len desc +; 0 2 expansion ROM magic (AA55h) +; 2 1 size in blocks (40h) +; 3 1 magic E9h +; 4 2 real mode entry point (relative) +; 6 2 checksum +; 8 8 magic 'BOOTBOOT' +; 16 10 zeros, at least one and a padding +; 26 2 pnp ptr, must be zero +; 28 4 flags, must be zero +; 32 32 MultiBoot header with protected mode entry point +;any format can follow. + + USE16 + ORG 800h +;BOOTBOOT stage2 header (64 bytes) +loader: db 55h,0AAh ;ROM magic + db (loader_end-loader)/512 ;size in 512 blocks +.executor: jmp near realmode_start ;entry point +.checksum: dw 0 ;checksum +.name: db "BOOTBOOT" + dw 0 + dd 0, 0 +.pnpptr: dw 0 +.flags: dd 0 +MB_MAGIC equ 01BADB002h +MB_FLAGS equ 0D43h + align 8 +.mb_header: dd MB_MAGIC ;magic + dd MB_FLAGS ;flags + dd -(MB_MAGIC+MB_FLAGS) ;checksum (0-magic-flags) + dd .mb_header ;our location (GRUB should load us here) + dd 0800h ;the same... load start + dd 07C00h ;load end + dd 0h ;no bss + dd multiboot_start ;entry point + +;no segments or sections, code comes right after the header + +;********************************************************************* +;* code * +;********************************************************************* + +;----------------Multiboot stub----------------- + USE32 +multiboot_start: + cld + cli + xor dl, dl + ;no GRUB environment available? + cmp eax, 2BADB002h + jne @f + ;save drive code for boot device + mov dl, byte [ebx+12] +@@: jmp CODE_BOOT:.real ;load 16 bit mode segment into cs + USE16 +.real: mov eax, CR0 + and al, 0FEh ;switching back to real mode + mov CR0, eax + xor ax, ax + mov ds, ax ;load registers 2nd turn + mov es, ax + mov ss, ax + jmp 0:realmode_start + +;-----------realmode-protmode stub------------- +realmode_start: + cli + cld + mov sp, 7C00h + ;relocate ourself from ROM to RAM if necessary + call .getaddr +.getaddr: pop si + mov ax, cs + or ax, ax + jnz .reloc + cmp si, .getaddr + je .noreloc +.reloc: mov ds, ax + xor ax, ax + mov es, ax + mov di, loader + sub si, .getaddr-loader + mov cx, (loader_end-loader)/2 + repnz movsw + xor ax, ax + mov ds, ax + xor dl, dl + jmp 0:.noreloc +.noreloc: or dl, dl + jnz @f + mov dl, 80h +@@: mov byte [bootdev], dl + mov word [lbapacket.count], 8 + + ;-----initialize serial port COM1,115200,8N1------ +if DEBUG eq 1 + mov ax, 0401h + xor bx, bx + mov cx, 030Bh + xor dx, dx + int 14h +end if + real_print starting + + in al, 060h ; read key + in al, 061h ; ack + out 061h, al + + DBG dbg_cpu + + ;-----check CPU----- + ;at least 286? + pushf + pushf + pop dx + xor dh,40h + push dx + popf + pushf + pop bx + popf + cmp dx, bx + jne .cpuerror + ;check for 386 + ;look for cpuid instruction + pushfd + pop eax + mov ebx, eax + xor eax, 200000h + and ebx, 200000h + push eax + popfd + pushfd + pop eax + and eax, 200000h + xor eax, ebx + shr eax, 21 + and al, 1 + jz .cpuerror + ;ok, now we can get cpu feature flags + mov eax, 1 + cpuid + shr al, 4 + shr ebx, 24 + mov word [bootboot.bspid], bx + ;look for minimum family + cmp ax, 0600h + jb .cpuerror + ;look for minimum feature flags + ;so we have PAE? + bt edx, 6 + jnc .cpuerror + ;what about MSR? + bt edx, 5 + jnc .cpuerror + ;and can we use long mode (LME)? + mov eax, 80000000h + push bx + cpuid + pop bx + cmp eax, 80000001h + jb .cpuerror + mov eax, 80000001h + cpuid + ;long mode + bt edx, 29 + jc .cpuok +.cpuerror: mov si, noarch + jmp real_diefunc +.cpuok: ;okay, we can do 64 bit! + + DBG dbg_A20 + + ;-----enable A20----- + ;no problem even if a20 is already turned on. + stc + mov ax, 2401h ;BIOS enable A20 function + int 15h + jnc .a20ok + ;keyboard nightmare + call .a20wait + mov al, 0ADh + out 64h, al + call .a20wait + mov al, 0D0h + out 64h, al + call .a20wait2 + in al, 60h + push ax + call .a20wait + mov al, 0D1h + out 64h, al + call .a20wait + pop ax + or al, 2 + out 60h, al + call .a20wait + mov al, 0AEh + out 64h, al + jmp .a20ok + + ;all methods failed, report an error + mov si, a20err + jmp real_diefunc + +.a20wait: in al, 64h + test al, 2 + jnz .a20wait + ret +.a20wait2: in al, 64h + test al, 1 + jz .a20wait2 + ret +.a20ok: + ; wait for a key press, if so use backup initrd + mov ecx, dword [046Ch] + add ecx, 5 ; ~250ms, 18.2/4 + sti +.waitkey: pause + in al, 064h + and al, 1 + jz @f + mov eax, ' BAK' + mov dword [bkp], eax + real_print backup + jmp .waitend +@@: cmp dword [046Ch], ecx + jb .waitkey +.waitend: cli + + ;-----detect memory map----- +getmemmap: + DBG dbg_mem + + xor eax, eax + mov dword [bootboot.acpi_ptr], eax + mov dword [bootboot.smbi_ptr], eax + mov dword [bootboot.initrd_ptr], eax + mov eax, bootboot_MAGIC + mov dword [bootboot.magic], eax + mov dword [bootboot.size], 128 + mov dword [bootboot.pagesize], 12 + mov dword [bootboot.protocol_ver], PROTOCOL_STATIC + mov dword [bootboot.loader_type], LOADER_BIOS + mov di, bootboot.mmap + mov cx, 800h + xor ax, ax + repnz stosw + mov di, bootboot.mmap + xor ebx, ebx + clc +.nextmap: cmp word [bootboot.size], 4096 + jae .nomoremap + mov edx, 'PAMS' + xor ecx, ecx + mov cl, 20 + xor eax, eax + mov ax, 0E820h + int 15h + jc .nomoremap + cmp eax, 'PAMS' + jne .nomoremap + ;is this the first memory hole? If so, mark + ;ourself as reserved memory + cmp dword [di+4], 0 + jnz .notfirst + cmp dword [di], 0 + jnz .notfirst + ; "allocate" memory for loader + mov eax, 15000h + add dword [di], eax + sub dword [di+8], eax + ;convert E820 memory type to BOOTBOOT memory type +.notfirst: mov al, byte [di+16] + cmp al, 1 + je .noov + cmp al, 3 + jne @f + mov al, MMAP_ACPIFREE + jmp .noov +@@: cmp al, 4 + jne @f + mov al, MMAP_ACPINVS + jmp .noov + ; everything else reserved +@@: mov al, MMAP_USED +.noov: ;copy memory type to size's least significant byte + mov byte [di+8], al + xor ecx, ecx + ;is it ACPI area? + cmp al, MMAP_ACPIFREE + jne .notacpi + mov dword [bootboot.acpi_ptr], edi + jmp .entryok + ;is it free slot? +.notacpi: cmp al, MMAP_FREE + jne .notmax +.freemem: ;do we have a ramdisk area? + cmp dword [bootboot.initrd_ptr], 0 + jnz .entryok + ;is it big enough for the compressed and the inflated ramdisk? + mov ebp, (INITRD_MAXSIZE+2)*1024*1024 + shr ebp, 20 + shl ebp, 20 + ;is this free memory hole big enough? (first fit) +.sizechk: mov eax, dword [di+8] ;load size + xor al, al + mov edx, dword [di+12] + and edx, 000FFFFFFh + or edx, edx + jnz .bigenough + cmp eax, ebp + jb .entryok +.bigenough: mov eax, dword [di] + ;save ramdisk pointer + mov dword [bootboot.initrd_ptr], eax +.entryok: ;get limit of memory + mov eax, dword [di+8] ;load size + xor al, al + mov edx, dword [di+12] + add eax, dword [di] ;add base + adc edx, dword [di+4] + and edx, 000FFFFFFh +.notmax: add dword [bootboot.size], 16 + ;bubble up entry if necessary + push si + push di +.bubbleup: mov si, di + cmp di, bootboot.mmap + jbe .swapdone + sub di, 16 + ;order by base asc + mov eax, dword [si+4] + cmp eax, dword [di+4] + jb .swapmodes + jne .swapdone + mov eax, dword [si] + cmp eax, dword [di] + jae .swapdone +.swapmodes: push di + mov cx, 16/2 +@@: mov dx, word [di] + lodsw + stosw + mov word [si-2], dx + dec cx + jnz @b + pop di + jmp .bubbleup +.swapdone: pop di + pop si + add di, 16 + cmp di, bootboot.mmap+4096 + jae .nomoremap +.skip: or ebx, ebx + jnz .nextmap +.nomoremap: cmp dword [bootboot.size], 128 + jne .E820ok +.noE820: mov si, memerr + jmp real_diefunc + +.E820ok: ;check if we have memory for the ramdisk + xor ecx, ecx + cmp dword [bootboot.initrd_ptr], ecx + jnz .enoughmem +.nomem: mov si, noenmem + jmp real_diefunc +.enoughmem: + + ;-----detect system structures----- + DBG dbg_systab + + ;do we need that scanning shit? + mov eax, dword [bootboot.acpi_ptr] + or eax, eax + jz @f + shr eax, 4 + mov es, ax + ;no if E820 map was correct + cmp dword [es:0], 'XSDT' + je .detsmbi + cmp dword [es:0], 'RSDT' + je .detsmbi +@@: inc dx + ;get starting address min(EBDA,E0000) + mov ah,0C1h + stc + int 15h + mov bx, es + jnc @f + mov ax, [ebdaptr] +@@: cmp ax, 0E000h + jb .acpinext + mov ax, 0E000h + ;detect ACPI ptr +.acpinext: mov es, ax + cmp dword [es:0], 'RSD ' + jne .acpinotf + cmp dword [es:4], 'PTR ' + jne .acpinotf + ;ptr found + ; do we have XSDT? + cmp dword [es:28], 0 + jne .acpi2 + cmp dword [es:24], 0 + je .acpi1 +.acpi2: mov eax, dword [es:24] + mov dword [bootboot.acpi_ptr], eax + mov eax, dword [es:28] + mov dword [bootboot.acpi_ptr+4], eax + jmp .detsmbi + ; no, fallback to RSDT +.acpi1: mov eax, dword [es:16] +@@: mov dword [bootboot.acpi_ptr], eax + jmp .detsmbi +.acpinotf: xor eax, eax + mov ax, es + inc ax + cmp ax, 0A000h + jne @f + add ax, 03000h +@@: ;end of 1Mb? + or ax, ax + jnz .acpinext + mov si, noacpi + jmp real_diefunc + + ;detect SMBios tables +.detsmbi: xor eax, eax + mov ax, 0E000h + xor dx, dx +.smbnext: mov es, ax + push ax + cmp dword [es:0], '_SM_' + je .smbfound + cmp dword [es:0], '_MP_' + jne .smbnotf + shl eax, 4 + mov ebx, dword [es:4] + mov dword [bootboot.mp_ptr], ebx + bts dx, 2 + jmp .smbnotf +.smbfound: shl eax, 4 + mov dword [bootboot.smbi_ptr], eax + bts dx, 1 +.smbnotf: pop ax + bt ax, 0 + mov bx, ax + and bx, 03h + inc ax + ;end of 1Mb? + or ax, ax + jnz .smbnext + ;restore ruined es +.detend: push ds + pop es + + DBG dbg_time + + ; ------- BIOS date and time ------- + mov ah, 4 + int 1Ah + jc .nobtime + ;ch century + ;cl year + xchg ch, cl + mov word [bootboot.datetime], cx + ;dh month + ;dl day + xchg dh, dl + mov word [bootboot.datetime+2], dx + mov ah, 2 + int 1Ah + jc .nobtime + ;ch hour + ;cl min + xchg ch, cl + mov word [bootboot.datetime+4], cx + ;dh sec + ;dl daylight saving on/off + xchg dh, dl + mov word [bootboot.datetime+6], dx +.nobtime: + + ;---- enable protmode ---- + cli + cld + lgdt [GDT_value] + mov eax, cr0 + or al, 1 + mov cr0, eax + jmp CODE_PROT:protmode_start + +;writes the reason, waits for a key and reboots. + USE32 +prot_diefunc: + prot_realmode + USE16 +real_diefunc: + push si + real_print loader.name + real_print panic + pop si + call real_printfunc + sti + xor ax, ax + int 16h + mov al, 0FEh + out 64h, al + jmp far 0FFFFh:0 ;invoke BIOS POST routine + +;ds:si zero terminated string to write +real_printfunc: + lodsb + or al, al + jz .end + push si + push ax + mov ah, byte 0Eh + mov bx, word 11 + int 10h + pop ax +if DEBUG eq 1 + mov ah, byte 01h + xor dx, dx + int 14h +end if + pop si + jmp real_printfunc +.end: ret + +real_protmodefunc: + cli + ;get return address + xor ebp, ebp + pop bp + mov dword [hw_stack], esp + lgdt [GDT_value] + mov eax, cr0 ;enable protected mode + or al, 1 + mov cr0, eax + jmp CODE_PROT:.init + + USE32 +.init: mov ax, DATA_PROT + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, dword [hw_stack] + jmp ebp + +prot_realmodefunc: + cli + ;get return address + pop ebp + ;save stack pointer + mov dword [hw_stack], esp + jmp CODE_BOOT:.back ;load 16 bit mode segment into cs + USE16 +.back: mov eax, CR0 + and al, 0FEh ;switching back to real mode + mov CR0, eax + xor ax, ax + mov ds, ax ;load registers 2nd turn + mov es, ax + mov ss, ax + jmp 0:.back2 +.back2: mov sp, word [hw_stack] + sti + jmp bp + + USE32 +prot_readsectorfunc: + push eax + push ecx + push edx + push esi + push edi + ;load 8 sectors (1 page) in low memory + mov dword [lbapacket.sect0], eax + mov dword [lbapacket.sect1], edx + mov dword [lbapacket.addr], 0A000h + prot_realmode + mov ah, byte 42h + mov dl, byte [bootdev] + mov esi, lbapacket + int 13h + xor ebx, ebx + mov bl, ah + real_protmode + pop edi + or edi, edi + jz @f + push edi + ;and copy to addr where it wanted to be (maybe in high memory) + mov esi, dword [lbapacket.addr] + xor ecx, ecx + mov cx, word [lbapacket.count] + shl ecx, 7 + repnz movsd + pop edi +@@: pop esi + pop edx + pop ecx + pop eax + ret + +prot_hex2bin: + xor eax, eax + xor ebx, ebx +@@: mov bl, byte [esi] + cmp bl, '0' + jb @f + cmp bl, '9' + jbe .num + cmp bl, 'A' + jb @f + cmp bl, 'F' + ja @f + sub bl, 7 +.num: sub bl, '0' + shl eax, 4 + add eax, ebx + inc esi + dec cx + jnz @b +@@: ret + +prot_dec2bin: + xor eax, eax + xor ebx, ebx + xor edx, edx + mov ecx, 10 +@@: mov bl, byte [esi] + cmp bl, '0' + jb @f + cmp bl, '9' + ja @f + mul ecx + sub bl, '0' + add eax, ebx + inc esi + jmp @b +@@: ret + +;IN: eax=str ptr, ecx=length OUT: eax=num +prot_oct2bin: + push ebx + push edx + mov ebx, eax + xor eax, eax + xor edx, edx +@@: shl eax, 3 + mov dl, byte[ebx] + sub dl, '0' + add eax, edx + inc ebx + dec ecx + jnz @b + pop edx + pop ebx + ret + +protmode_start: + mov ax, DATA_PROT + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, 7C00h + + ; ------- Locate initrd -------- + mov esi, 0C8000h +.nextrom: cmp word [esi], 0AA55h + jne @f + cmp dword [esi+8], 'INIT' + jne @f + cmp word [esi+12], 'RD' + jne @f + mov eax, dword [esi+16] + mov dword [bootboot.initrd_size], eax + add esi, 32 + jmp .initrdrom +@@: add esi, 2048 + cmp esi, 0F4000h + jb .nextrom + + ; read GPT +.getgpt: xor eax, eax + xor edx, edx + xor edi, edi + prot_readsector + mov esi, 0A000h+512 + cmp dword [esi], 'EFI ' + je @f +.nogpt: mov si, nogpt + jmp prot_diefunc +@@: mov edi, 0B000h + mov ebx, edi + mov ecx, 896 + repnz movsd + mov esi, ebx + mov ecx, dword [esi+80] ;number of entries + mov ebx, dword [esi+84] ;size of one entry + add esi, 512 + mov edx, esi ;first entry + mov dword [gpt_ent], ebx + mov dword [gpt_num], ecx + mov dword [gpt_ptr], edx + ; first, look for a partition with bootable flag + mov esi, edx +@@: cmp dword [esi], 0 ;failsafe, jump to parttype search + jne .notz + cmp dword [esi+32], 0 + jz .nextgpt +.notz: bt word [esi+48], 2 ;EFI_PART_USED_BY_OS? + jc .loadesp2 + add esi, ebx + dec ecx + jnz @b + ; if none, look for specific partition types +.nextgpt: mov esi, dword [gpt_ptr] + mov ebx, dword [gpt_ent] + mov ecx, dword [gpt_num] + mov eax, 0C12A7328h + mov edx, 011D2F81Fh +@@: cmp dword [esi], eax ;GUID match? + jne .note + cmp dword [esi+4], edx + je .loadesp +.note: cmp dword [esi], 'OS/Z' ;or OS/Z root partition for this archicture? + jne .noto + cmp word [esi+4], 08664h + jne .noto + cmp dword [esi+12], 'root' + je .loadesp +.noto: add esi, ebx + dec ecx + jnz @b +.noinitrd: mov esi, nord + jz prot_diefunc + + ; load ESP at free memory hole found +.loadesp: mov dword [gpt_ptr], esi + mov dword [gpt_num], ecx +.loadesp2: mov ecx, dword [esi+40] ;last sector + mov eax, dword [esi+32] ;first sector + mov edx, dword [esi+36] + or edx, edx + jnz .nextgpt + or ecx, ecx + jz .nextgpt + or eax, eax + jz .nextgpt + mov dword [bpb_sec], eax + ;load BPB + mov edi, dword [bootboot.initrd_ptr] + mov word [lbapacket.count], 8 + prot_readsector + + ;parse fat on EFI System Partition +@@: cmp dword [edi + bpb.fst2], 'FAT3' + je .isfat + cmp dword [edi + bpb.fst], 'FAT1' + je .isfat + ;no, then it's an initrd on the entire partition + or eax, eax + jz .noinitrd + or ecx, ecx + jz .noinitrd + sub ecx, eax + shl ecx, 9 + mov dword [bootboot.initrd_size], ecx + ; load INITRD from partition + dec ecx + shr ecx, 12 + xor edx, edx + mov edi, dword [bootboot.initrd_ptr] +@@: add edi, 4096 + prot_readsector + add eax, 8 + dec ecx + jnz @b + jmp .initrdloaded + +.isfat: cmp word [edi + bpb.bps], 512 + jne .nextgpt + ;calculations + xor eax, eax + xor ebx, ebx + xor ecx, ecx + xor edx, edx + mov bx, word [edi + bpb.spf16] + or bx, bx + jnz @f + mov ebx, dword [edi + bpb.spf32] +@@: mov al, byte [edi + bpb.nf] + mov cx, word [edi + bpb.rsc] + ;data_sec = numFat*secPerFat + mul ebx + ;data_sec += reservedSec + add eax, ecx + ;data_sec += ESPsec + add eax, dword [bpb_sec] + mov dword [data_sec], eax + mov dword [root_sec], eax + xor eax, eax + mov al, byte [edi + bpb.spc] + mov dword [clu_sec], eax + ;FAT16 + ;data_sec += (numRootEnt*32+511)/512 + cmp word [edi + bpb.spf16], 0 + je .fat32bpb + cmp byte [edi + bpb.fst + 4], '6' + jne .nextgpt + xor eax, eax + mov ax, word [edi + bpb.nr] + shl eax, 5 + add eax, 511 + shr eax, 9 + add dword [data_sec], eax + mov byte [fattype], 0 + xor ecx, ecx + mov cx, word [edi + bpb.spf16] + jmp .loadfat +.fat32bpb: ;FAT32 + ;root_sec += (rootCluster-2)*secPerCluster + mov eax, dword [edi + bpb.rc] + sub eax, 2 + xor edx, edx + mov ebx, dword [clu_sec] + mul ebx + add dword [root_sec], eax + mov byte [fattype], 1 + mov ecx, dword [edi + bpb.spf32] + ;load FAT +.loadfat: xor eax, eax + mov ax, word [edi+bpb.rsc] + add eax, dword [bpb_sec] + shr ecx, 3 + inc ecx + mov edi, 0x10000 + mov word [lbapacket.count], 8 +@@: prot_readsector + add edi, 4096 + add eax, 8 + dec ecx + jnz @b + mov ax, word [clu_sec] + mov word [lbapacket.count], ax + + ;load root directory + mov eax, dword [root_sec] + xor edx, edx + mov edi, dword [bootboot.initrd_ptr] + prot_readsector + + ;look for BOOTBOOT directory + mov esi, edi + mov eax, 'BOOT' + mov ecx, 255 +.nextroot: cmp dword [esi], eax + jne @f + cmp dword [esi+4], eax + jne @f + cmp word [esi+8], ' ' + je .foundroot +@@: add esi, 32 + cmp byte [esi], 0 + jz .nextgpt + dec ecx + jnz .nextroot + jmp .nextgpt +.foundroot: xor eax, eax + cmp byte [fattype], 0 + jz @f + mov ax, word [esi + fatdir.ch] + shl eax, 16 +@@: mov ax, word [esi + fatdir.cl] + ;sec = (cluster-2)*secPerCluster+data_sec + sub eax, 2 + xor edx, edx + mov ebx, dword [clu_sec] + mul ebx + add eax, dword [data_sec] + mov edi, dword [bootboot.initrd_ptr] + prot_readsector + + mov dword [9000h], 0 + + ;look for CONFIG and INITRD + mov esi, edi + mov ecx, 255 + mov edx, dword [bkp] +.nextdir: cmp dword [esi], 'CONF' + jne .notcfg + cmp dword [esi+4], 'IG ' + jne .notcfg + cmp dword [esi+7], ' ' + jne .notcfg + + push edx + push esi + push edi + push ecx + mov ecx, dword [esi + fatdir.size] + cmp ecx, 4095 + jbe @f + mov ecx, 4095 +@@: mov dword [core_len], ecx + xor eax, eax + cmp byte [fattype], 0 + jz @f + mov ax, word [esi + fatdir.ch] + shl eax, 16 +@@: mov ax, word [esi + fatdir.cl] + mov ebx, dword [clu_sec] + cmp bx, 8 + jbe @f + mov word [lbapacket.count], 8 +@@: mov edi, 9000h +.nextcfg: push eax + ;sec = (cluster-2)*secPerCluster+data_sec + sub eax, 2 + xor edx, edx + mov ebx, dword [clu_sec] + mul ebx + shl ebx, 9 + add eax, dword [data_sec] + push ebx + prot_readsector + pop ebx + pop eax + add edi, ebx + sub ecx, ebx + js .cfgloaded + jz .cfgloaded + ;get next cluster from FAT + cmp byte [fattype], 0 + jz @f + shl eax, 2 + add eax, 0x10000 + mov eax, dword [eax] + jmp .nextcfg +@@: shl eax, 1 + add eax, 0x10000 + mov ax, word [eax] + and eax, 0FFFFh + jmp .nextcfg +.cfgloaded: pop ecx + pop edi + pop esi + pop edx + xor eax, eax + mov ecx, 4096 + sub ecx, dword [core_len] + mov edi, 9000h + add edi, dword [core_len] + repnz stosb + mov dword [core_len], eax + mov byte [9FFFh], al + jmp .notinit +.notcfg: + cmp dword [esi], 'X86_' + jne @f + cmp dword [esi+4], '64 ' + je .altinitrd +@@: cmp dword [esi], 'INIT' + jne .notinit + cmp dword [esi+4], 'RD ' + jne .notinit + cmp dword [esi+7], edx + jne .notinit + +.altinitrd: mov ecx, dword [esi + fatdir.size] + mov dword [bootboot.initrd_size], ecx + xor eax, eax + cmp byte [fattype], 0 + jz @f + mov ax, word [esi + fatdir.ch] + shl eax, 16 +@@: mov ax, word [esi + fatdir.cl] + jmp .loadinitrd + +.notinit: add esi, 32 + cmp byte [esi], 0 + jz .loadinitrd + dec ecx + jnz .nextdir + + ;load cluster chain, eax=cluster, ecx=size +.loadinitrd: + mov edi, dword [bootboot.initrd_ptr] +.nextclu: push eax + ;sec = (cluster-2)*secPerCluster+data_sec + sub eax, 2 + xor edx, edx + mov ebx, dword [clu_sec] + mov word [lbapacket.count], bx + mul ebx + shl ebx, 9 + add eax, dword [data_sec] + push ebx + prot_readsector + pop ebx + pop eax + add edi, ebx + sub ecx, ebx + js .initrdloaded + jz .initrdloaded + ;get next cluster from FAT + cmp byte [fattype], 0 + jz @f + shl eax, 2 + add eax, 0x10000 + mov eax, dword [eax] + jmp .nextclu +@@: shl eax, 1 + add eax, 0x10000 + mov ax, word [eax] + and eax, 0FFFFh + jmp .nextclu + +.initrdloaded: + DBG32 dbg_initrd + mov esi, dword [bootboot.initrd_ptr] +.initrdrom: + mov edi, dword [bootboot.initrd_ptr] + cmp word [esi], 08b1fh + jne .noinflate + DBG32 dbg_gzinitrd + mov ebx, esi + mov eax, dword [bootboot.initrd_size] + add ebx, eax + sub ebx, 4 + mov ecx, dword [ebx] + mov dword [bootboot.initrd_size], ecx + add edi, eax + add edi, 4095 + shr edi, 12 + shl edi, 12 + add eax, ecx + cmp eax, (INITRD_MAXSIZE+2)*1024*1024 + jb @f + mov esi, nogzmem + jmp prot_diefunc +@@: mov dword [bootboot.initrd_ptr], edi + ; inflate initrd + xor eax, eax + add esi, 2 + lodsb + cmp al, 8 + jne tinf_err + lodsb + mov bl, al + add esi, 6 + test bl, 4 + jz @f + lodsw + add esi, eax +@@: test bl, 8 + jz .noname +@@: lodsb + or al, al + jnz @b +.noname: test bl, 16 + jz .nocmt +@@: lodsb + or al, al + jnz @b +.nocmt: test bl, 2 + jz @f + add esi, 2 +@@: call tinf_uncompress +.noinflate: + ;round up to page size + mov eax, dword [bootboot.initrd_size] + add eax, 4095 + shr eax, 12 + shl eax, 12 + mov dword [bootboot.initrd_size], eax + + ;do we have an environment configuration? + mov ebx, 9000h + cmp byte [ebx], 0 + jnz .parsecfg + + ;-----load /sys/config------ + mov edx, fsdrivers +.nextfs1: xor ebx, ebx + mov bx, word [edx] + or bx, bx + jz .errfs1 + mov esi, dword [bootboot.initrd_ptr] + mov ecx, dword [bootboot.initrd_size] + add ecx, esi + mov edi, cfgfile + push edx + call ebx + pop edx + or ecx, ecx + jnz .fscfg + add edx, 2 + jmp .nextfs1 +.fscfg: mov edi, 9000h + add ecx, 3 + shr ecx, 2 + repnz movsd +.errfs1: + ;do we have an environment configuration? + mov ebx, 9000h + cmp byte [ebx], 0 + jz .noconf + + ;parse +.parsecfg: push ebx + DBG32 dbg_env + pop esi + jmp .getnext + + ;skip comments +.nextvar: cmp byte[esi], '#' + je .skipcom + cmp word[esi], '//' + jne @f + add esi, 2 +.skipcom: lodsb + cmp al, 10 + je .getnext + cmp al, 13 + je .getnext + or al, al + jz .parseend + cmp esi, 0A000h + ja .parseend + jmp .skipcom +@@: cmp word[esi], '/*' + jne @f +.skipcom2: inc esi + cmp word [esi-2], '*/' + je .getnext + cmp byte [esi], 0 + jz .parseend + cmp esi, 0A000h + ja .parseend + jmp .skipcom2 + ;parse screen dimensions +@@: cmp dword[esi], 'scre' + jne @f + cmp word[esi+4], 'en' + jne @f + cmp byte[esi+6], '=' + jne @f + add esi, 7 + call prot_dec2bin + mov dword [reqwidth], eax + inc esi + call prot_dec2bin + mov dword [reqheight], eax + jmp .getnext + ;get kernel's filename +@@: cmp dword[esi], 'kern' + jne @f + cmp word[esi+4], 'el' + jne @f + cmp byte[esi+6], '=' + jne @f + add esi, 7 + mov edi, kernel +.copy: lodsb + or al, al + jz .copyend + cmp al, ' ' + jz .copyend + cmp al, 13 + jbe .copyend + cmp esi, 0A000h + ja .copyend + cmp edi, loader_end-1 + jae .copyend + stosb + jmp .copy +.copyend: xor al, al + stosb + jmp .getnext +@@: + inc esi + ;failsafe +.getnext: cmp esi, 0A000h + jae .parseend + cmp byte [esi], 0 + je .parseend + ;skip white spaces + cmp byte [esi], ' ' + je @b + cmp byte [esi], 13 + jbe @b + jmp .nextvar +.noconf: mov edi, ebx + mov ecx, 1024 + xor eax, eax + repnz stosd + mov dword [ebx+0], '// N' + mov dword [ebx+4], '/A\n' +.parseend: + + ;-----load /sys/core------ + mov edx, fsdrivers +.nextfs: xor ebx, ebx + mov bx, word [edx] + or bx, bx + jz .errfs + mov esi, dword [bootboot.initrd_ptr] + mov ecx, dword [bootboot.initrd_size] + add ecx, esi + mov edi, kernel + push edx + call ebx + pop edx + or ecx, ecx + jnz .coreok + add edx, 2 + jmp .nextfs +.errfs2: mov esi, nocore + jmp prot_diefunc +.errfs: ; if all drivers failed, search for the first elf executable + DBG32 dbg_scan + mov esi, dword [bootboot.initrd_ptr] + mov ecx, dword [bootboot.initrd_size] + add ecx, esi + dec esi +@@: inc esi + cmp esi, ecx + jae .errfs2 + cmp dword [esi], 5A2F534Fh ; OS/Z magic + je .alt + cmp dword [esi], 464C457Fh ; ELF magic + je .alt + cmp word [esi], 5A4Dh ; MZ magic + jne @b + mov eax, dword [esi+0x3c] + add eax, esi + cmp dword [eax], 00004550h ; PE magic + jne @b + cmp word [eax+4], 8664h ; x86_64 + jne @b + cmp word [eax+20], 20Bh + jne @b +.alt: cmp word [esi+4], 0102h ;lsb 64 bit + jne @b + cmp word [esi+18], 62 ;x86_64 + jne @b + cmp word [esi+0x38], 0 ;e_phnum > 0 + jz @b +.coreok: + ; parse PE + cmp word [esi], 5A4Dh ; MZ magic + jne .tryelf + mov ebx, esi + add esi, dword [esi+0x3c] + cmp dword [esi], 00004550h ; PE magic + jne .badcore + cmp word [esi+4], 8664h ; x86_64 architecture + jne .badcore + cmp word [esi+20], 20Bh ; PE32+ format + jne .badcore + + DBG32 dbg_pe + + mov eax, dword [esi+36] ; entry point + mov dword [entrypoint], eax + mov dword [entrypoint+4], 0FFFFFFFFh + mov ecx, eax + sub ecx, dword [esi+40] ; - code base + add ecx, dword [esi+24] ; + text size + add ecx, dword [esi+28] ; + data size + mov edx, dword [esi+32] ; bss size + shr eax, 31 + jz .badcore + jmp .mkcore + + ; parse ELF +.tryelf: cmp dword [esi], 5A2F534Fh ; OS/Z magic + je @f + cmp dword [esi], 464C457Fh ; ELF magic + jne .badcore +@@: cmp word [esi+4], 0102h ;lsb 64 bit, little endian + jne .badcore + cmp word [esi+18], 62 ;x86_64 architecture + je @f +.badcore: mov esi, badcore + jmp prot_diefunc +@@: + DBG32 dbg_elf + + mov ebx, esi + mov eax, dword [esi+0x18] + mov dword [entrypoint], eax + mov eax, dword [esi+0x18+4] + mov dword [entrypoint+4], eax + ;parse ELF binary + mov cx, word [esi+0x38] ; program header entries phnum + mov eax, dword [esi+0x20] ; program header + add esi, eax + sub esi, 56 + inc cx +.nextph: add esi, 56 + dec cx + jz .badcore + cmp word [esi], 1 ; p_type, loadable + jne .nextph + cmp word [esi+22], 0FFFFh ; p_vaddr == negative address + jne .nextph + ;got it + add ebx, dword [esi+8] ; + P_offset + mov ecx, dword [esi+32] ; p_filesz + ; hack to keep symtab and strtab for shared libraries + cmp byte [ebx+16], 3 + jne @f + add ecx, 04000h +@@: mov edx, dword [esi+40] ; p_memsz + sub edx, ecx + + ;ebx=ptr to core segment, ecx=segment size, edx=bss size +.mkcore: or ecx, ecx + jz .badcore + mov edi, dword [bootboot.initrd_ptr] + add edi, dword [bootboot.initrd_size] + mov dword [core_ptr], edi + mov dword [core_len], ecx + ;copy code from segment after initrd + mov esi, ebx + mov ebx, ecx + and bl, 3 + shr ecx, 2 + repnz movsd + or bl, bl + jz @f + mov cl, bl + repnz movsb + ;zero out bss +@@: or edx, edx + jz @f + add dword [core_len], edx + xor eax, eax + mov ecx, edx + and dl, 3 + shr ecx, 2 + repnz stosd + or dl, dl + jz @f + mov cl, dl + repnz stosb + ;round up to page size +@@: mov eax, dword [core_len] + add eax, 4095 + shr eax, 12 + shl eax, 12 + mov dword [core_len], eax + + ;exclude initrd area and core from free mmap + mov esi, bootboot.mmap + mov ebx, dword [bootboot.initrd_ptr] + mov edx, dword [core_ptr] + add edx, eax + mov cx, 248 + ; initrd+core (ebx..edx) +.nextfree: cmp dword [esi], ebx + ja .excludeok + mov eax, dword [esi+8] + and al, 0F0h + add eax, dword [esi] + cmp edx, eax + ja .notini + ; +--------------+ + ; ####### + cmp dword [esi], ebx + jne .splitmem + ; ptr = initrd_ptr+initrd_size+core_len + mov dword [esi], edx + sub edx, ebx + and dl, 0F0h + ; size -= initrd_size+core_len + sub dword [esi+8], edx + jmp .excludeok + ; +--+ +----------+ + ; ####### +.splitmem: mov edi, bootboot.magic + add edi, dword [bootboot.size] + add dword [bootboot.size], 16 +@@: mov eax, dword [edi-16] + mov dword [edi], eax + mov eax, dword [edi-12] + mov dword [edi+4], eax + mov eax, dword [edi-8] + mov dword [edi+8], eax + mov eax, dword [edi-4] + mov dword [edi+12], eax + sub edi, 16 + cmp edi, esi + ja @b + mov eax, ebx + sub eax, dword [esi] + sub dword [esi+24], eax + inc al + mov dword [esi+8], eax + mov dword [esi+16], edx + sub edx, ebx + sub dword [esi+24], edx + jmp .excludeok +.notini: add esi, 16 + dec cx + jnz .nextfree +.excludeok: + ; ------- set video resolution ------- + prot_realmode + + DBG dbg_vesa + + xor ax, ax + mov es, ax + mov word [vbememsize], ax + ;get VESA VBE2.0 info + mov ax, 4f00h + mov di, 0A000h + mov dword [di], 'VBE2' + ;this call requires a big stack + int 10h + cmp ax, 004fh + je @f +.viderr: mov si, novbe + jmp real_diefunc + ;get video memory size in MiB +@@: mov ax, word [0A000h+12h] + shr ax, 4 + or ax, ax + jnz @f + inc ax +@@: mov word [vbememsize], ax + ;read dword pointer and copy string to 1st 64k + ;available video modes +@@: xor esi, esi + xor edi, edi + mov si, word [0A000h+0Eh] + mov ax, word [0A000h+10h] + mov ds, ax + xor ax, ax + mov es, ax + mov di, 0A000h+400h + mov cx, 64 +@@: lodsw + cmp ax, 0ffffh + je @f + or ax, ax + jz @f + stosw + dec cx + jnz @b +@@: xor ax, ax + stosw + ;iterate on modes + mov si, 0A000h+400h +.nextmode: mov di, 0A000h+800h + xor ax, ax + mov word [0A000h+802h], ax ; vbe mode + lodsw + or ax, ax + jz .viderr + mov cx, ax + mov ax, 4f01h + push bx + push cx + push si + push di + int 10h + pop di + pop si + pop cx + pop bx + cmp ax, 004fh + jne .viderr + bts cx, 13 + bts cx, 14 + mov ax, word [0A000h+800h] ; vbe flags + and ax, VBE_MODEFLAGS + cmp ax, VBE_MODEFLAGS + jne .nextmode + ;check memory model (direct) + cmp byte [0A000h+81bh], 6 + jne .nextmode + ;check bpp + cmp byte [0A000h+819h], 32 + jne .nextmode + ;check min width + mov ax, word [reqwidth] + cmp ax, 640 + ja @f + mov ax, 640 +@@: cmp word [0A000h+812h], ax + jne .nextmode + ;check min height + mov ax, word [reqheight] + cmp ax, 480 + ja @f + mov ax, 480 +@@: cmp word [0A000h+814h], ax + jb .nextmode + ;match? go no further +.match: mov ax, word [0A000h+810h] + mov word [bootboot.fb_scanline], ax + mov ax, word [0A000h+812h] + mov word [bootboot.fb_width], ax + mov ax, word [0A000h+814h] + mov word [bootboot.fb_height], ax + mov eax, dword [0A000h+828h] + mov dword [bootboot.fb_ptr], eax + mov byte [bootboot.fb_type],FB_ARGB ; blue offset + cmp byte [0A000h+824h], 0 + je @f + mov byte [bootboot.fb_type],FB_RGBA + cmp byte [0A000h+824h], 8 + je @f + mov byte [bootboot.fb_type],FB_ABGR + cmp byte [0A000h+824h], 16 + je @f + mov byte [bootboot.fb_type],FB_BGRA +@@: ; set video mode + mov bx, cx + bts bx, 14 ;flat linear + mov ax, 4f02h + int 10h + cmp ax, 004fh + jne .viderr + ;no debug output after this point + + ;inform firmware that we're about to leave it's realm + mov ax, 0EC00h + mov bl, 2 ; 64 bit + int 15h + real_protmode + + ; -------- paging --------- + ;map core at higher half of memory + ;address 0xffffffffffe00000 + xor eax, eax + mov edi, 0A000h + mov ecx, (15000h-0A000h)/4 + repnz stosd + + ;PML4 + mov edi, 0A000h + ;pointer to 2M PDPE (first 4G RAM identity mapped) + mov dword [edi], 0E001h + ;pointer to 4k PDPE (core mapped at -2M) + mov dword [edi+4096-8], 0B001h + + ;4K PDPE + mov edi, 0B000h + mov dword [edi+4096-8], 0C001h + ;4K PDE + mov edi, 0C000h+3840 + mov eax, dword[bootboot.fb_ptr] ;map framebuffer + mov al,81h + mov ecx, 31 +@@: stosd + add edi, 4 + add eax, 2*1024*1024 + dec ecx + jnz @b + mov dword [0C000h+4096-8], 0D001h + + ;4K PT + mov dword[0D000h], 08001h ;map bootboot + mov dword[0D008h], 09001h ;map configuration + mov edi, 0D010h + mov eax, dword[core_ptr] ;map core text segment + inc eax + mov ecx, dword[core_len] + shr ecx, 12 + inc ecx +@@: stosd + add edi, 4 + add eax, 4096 + dec ecx + jnz @b + mov dword[0DFF8h], 014001h ;map core stack + + ;identity mapping + ;2M PDPE + mov edi, 0E000h + mov dword [edi], 0F001h + mov dword [edi+8], 010001h + mov dword [edi+16], 011001h + mov dword [edi+24], 012001h + ;2M PDE + mov edi, 0F000h + xor eax, eax + mov al, 81h + mov ecx, 512* 4;G RAM +@@: stosd + add edi, 4 + add eax, 2*1024*1024 + dec ecx + jnz @b + ;first 2M mapped by page + mov dword [0F000h], 013001h + mov edi, 013000h + mov eax, 1 + mov ecx, 512 +@@: stosd + add edi, 4 + add eax, 4096 + dec ecx + jnz @b + + ;generate new 64 bit gdt + mov edi, GDT_table+8 + ;8h core code + xor eax, eax ;supervisor mode (ring 0) + mov ax, 0FFFFh + stosd + mov eax, 00209800h + stosd + ;10h core data + xor eax, eax ;flat data segment + mov ax, 0FFFFh + stosd + mov eax, 00809200h + stosd + ;18h mandatory tss + xor eax, eax ;required by vt-x + mov al, 068h + stosd + mov eax, 00008900h + stosd + xor eax, eax + stosd + stosd + + ;Enter long mode + cli + mov al, 0FFh ;disable PIC + out 021h, al + out 0A1h, al + in al, 70h ;disable NMI + or al, 80h + out 70h, al + + mov eax, 1101000b ;Set PAE, MCE, PGE + mov cr4, eax + mov eax, 0A000h + mov cr3, eax + mov ecx, 0C0000080h ;EFER MSR + rdmsr + or eax, 100h ;enable long mode + wrmsr + + mov eax, cr0 + or eax, 0C0000001h + mov cr0, eax ;enable paging wich cache disabled + lgdt [GDT_value] ;read 80 bit address + jmp @f + nop +@@: jmp 8:longmode_init + USE64 +longmode_init: + xor eax, eax + mov ax, 10h + mov ds, ax + mov es, ax + mov ss, ax + mov fs, ax + mov gs, ax + + xor rsp, rsp + ;call _start() at qword[entrypoint] + jmp qword[entrypoint] + nop + nop + nop + nop + + USE32 + include "fs.inc" + include "tinf.inc" + +;********************************************************************* +;* Data * +;********************************************************************* + ;global descriptor table + align 16 +GDT_table: dd 0, 0 ;null descriptor +DATA_PROT = $-GDT_table + dd 0000FFFFh,008F9200h ;flat ds +CODE_BOOT = $-GDT_table + dd 0000FFFFh,00009800h ;16 bit legacy real mode cs +CODE_PROT = $-GDT_table + dd 0000FFFFh,00CF9A00h ;32 bit prot mode ring0 cs + dd 00000068h,00CF8900h ;32 bit TSS, not used but required +GDT_value: dw $-GDT_table + dd GDT_table + dd 0,0 + align 16 +entrypoint: dq 0 +core_ptr: dd 0 +core_len: dd 0 +ebdaptr: dd 0 +hw_stack: dd 0 +lastmsg: dd 0 +bpb_sec: dd 0 ;ESP's first sector +root_sec: dd 0 ;root directory's first sector +data_sec: dd 0 ;first data sector +clu_sec: dd 0 ;sector per cluster +gpt_ptr: dd 0 +gpt_num: dd 0 +gpt_ent: dd 0 +lbapacket: ;lba packet for BIOS +.size: dw 10h +.count: dw 8 +.addr: dd 0A000h +.sect0: dd 0 +.sect1: dd 0 +.flataddr: dd 0,0 +reqwidth: dd 1024 +reqheight: dd 768 +bootdev: db 0 +fattype: db 0 +vbememsize: dw 0 +bkp: dd ' ' +if DEBUG eq 1 +dbg_cpu db " * Detecting CPU",10,13,0 +dbg_A20 db " * Enabling A20",10,13,0 +dbg_mem db " * E820 Memory Map",10,13,0 +dbg_systab db " * System tables",10,13,0 +dbg_time db " * System time",10,13,0 +dbg_env db " * Environment",10,13,0 +dbg_initrd db " * Initrd loaded",10,13,0 +dbg_gzinitrd db " * Gzip compressed initrd",10,13,0 +dbg_scan db " * Autodetecting kernel",10,13,0 +dbg_elf db " * Parsing ELF64",10,13,0 +dbg_pe db " * Parsing PE32+",10,13,0 +dbg_vesa db " * Screen VESA VBE",10,13,0 +end if +backup: db " * Backup initrd",10,13,0 +starting: db "Booting OS...",10,13,0 +panic: db "-PANIC: ",0 +noarch: db "Hardware not supported",0 +a20err: db "Failed to enable A20",0 +memerr: db "E820 memory map not found",0 +nogzmem: db "Inflating: " +noenmem: db "Not enough memory",0 +noacpi: db "ACPI not found",0 +nogpt: db "No boot partition",0 +nord: db "INITRD not found",0 +nolib: db "/sys not found in initrd",0 +nocore: db "Kernel not found in initrd",0 +badcore: db "Kernel is not a valid executable",0 +novbe: db "VESA VBE error, no framebuffer",0 +nogzip: db "Unable to uncompress",0 +cfgfile: db "sys/config",0,0,0 +kernel: db "sys/core" + db (128-($-kernel)) dup 0 +;-----------padding to be multiple of 512---------- + db (511-($-loader+511) mod 512) dup 0 +loader_end: + +;-----------BIOS checksum------------ +chksum = 0 +repeat $-loader + load b byte from (loader+%-1) + chksum = (chksum + b) mod 100h +end repeat +store byte (100h-chksum) at (loader.checksum) + +;-----------bss area----------- +tinf_bss_start: +d_end: dd ? +d_lzOff: dd ? +d_dict_ring:dd ? +d_dict_size:dd ? +d_dict_idx: dd ? +d_tag: db ? +d_bitcount: db ? +d_bfinal: db ? +d_curlen: dw ? +d_ltree: +d_ltree_table: + dw 16 dup ? +d_ltree_trans: + dw 288 dup ? +d_dtree: +d_dtree_table: + dw 16 dup ? +d_dtree_trans: + dw 288 dup ? +offs: dw 16 dup ? +num: dw ? +lengths: db 320 dup ? +hlit: dw ? +hdist: dw ? +hclen: dw ? +tinf_bss_end: + +;-----------bound check------------- +;fasm will generate an error if the code +;is bigger than it should be +db 07C00h-4096-($-loader) dup ? diff --git a/x86_64-bios/bootboot.inc b/x86_64-bios/bootboot.inc new file mode 100644 index 0000000..7c5fc3b --- /dev/null +++ b/x86_64-bios/bootboot.inc @@ -0,0 +1,121 @@ +;* +;* x86_64-bios/bootboot.inc +;* +;* Copyright (C) 2017 bzt (bztsrc@gitlab) +;* +;* Permission is hereby granted, free of charge, to any person +;* obtaining a copy of this software and associated documentation +;* files (the "Software"), to deal in the Software without +;* restriction, including without limitation the rights to use, copy, +;* modify, merge, publish, distribute, sublicense, and/or sell copies +;* of the Software, and to permit persons to whom the Software is +;* furnished to do so, subject to the following conditions: +;* +;* The above copyright notice and this permission notice shall be +;* included in all copies or substantial portions of the Software. +;* +;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;* DEALINGS IN THE SOFTWARE. +;* +;* This file is part of the BOOTBOOT Protocol package. +;* @brief The BOOTBOOT structure +;* +; ------ !!! WARNING: MUST MATCH ../bootboot.h !!! ------ + +bootboot = 8000h + + + + + +; this define is in the 18th line of bootboot.h +bootboot_MAGIC equ 'BOOT' + +; minimum protocol level: +; hardcoded kernel name, static kernel memory addresses +PROTOCOL_MINIMAL equ 0 +; static protocol level: +; kernel name parsed from environment, static kernel memory addresses +PROTOCOL_STATIC equ 1 +; dynamic protocol level: +; kernel name parsed from environment, kernel memory addresses parsed from ELF symbols +PROTOCOL_DYNAMIC equ 2 +; big-endian flag +PROTOCOL_BIGENDIAN equ 080h + +; loader types, just informational +LOADER_BIOS equ 0 +LOADER_UEFI equ 1 +LOADER_RPI equ 2 + +; framebuffer pixel format, only 32 bits supported +FB_ARGB equ 0 +FB_RGBA equ 1 +FB_ABGR equ 2 +FB_BGRA equ 3 + + + +; mmap entry, type is stored in least significant tetrad of size +virtual at 0 + mmap_ent.ptr: dq 0 + mmap_ent.size: dq 0 +end virtual +; we don't have entry field macros for asm +; realsize = size & 0xFFFFFFFFFFF0 +; type = size & 0xF + + +MMAP_USED equ 0 +MMAP_FREE equ 1 +MMAP_ACPIFREE equ 2 +MMAP_ACPINVS equ 3 +MMAP_MMIO equ 4 + +INITRD_MAXSIZE equ 16 ; Mb + +virtual at bootboot + bootboot.magic: dd 0 + bootboot.size: dd 0 + + bootboot.protocol_ver:db 1 + bootboot.loader_type: db 0 + bootboot.pagesize: db 0 + bootboot.fb_type: db 0 + + bootboot.timezone: dw 0 + bootboot.bspid: dw 0 + + bootboot.datetime: dq 0 + + bootboot.initrd_ptr: dq 0 + bootboot.initrd_size: dq 0 + + bootboot.fb_ptr: dq 0 + bootboot.fb_size: dd 0 + bootboot.fb_width: dd 0 + bootboot.fb_height: dd 0 + bootboot.fb_scanline: dd 0 + + ; architecture specific pointers + + + bootboot.acpi_ptr: dq 0 + bootboot.smbi_ptr: dq 0 + bootboot.efi_ptr: dq 0 + bootboot.mp_ptr: dq 0 + bootboot.unused: dq 0,0,0,0 + + bootboot.mmap: +end virtual + + + + + diff --git a/x86_64-bios/fs.inc b/x86_64-bios/fs.inc new file mode 100644 index 0000000..b66123c --- /dev/null +++ b/x86_64-bios/fs.inc @@ -0,0 +1,502 @@ +;* +;* x86_64-bios/fs.inc +;* +;* Copyright (C) 2017 bzt (bztsrc@gitlab) +;* +;* Permission is hereby granted, free of charge, to any person +;* obtaining a copy of this software and associated documentation +;* files (the "Software"), to deal in the Software without +;* restriction, including without limitation the rights to use, copy, +;* modify, merge, publish, distribute, sublicense, and/or sell copies +;* of the Software, and to permit persons to whom the Software is +;* furnished to do so, subject to the following conditions: +;* +;* The above copyright notice and this permission notice shall be +;* included in all copies or substantial portions of the Software. +;* +;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;* DEALINGS IN THE SOFTWARE. +;* +;* This file is part of the BOOTBOOT Protocol package. +;* @brief Filesystem drivers for initial ramdisk. +;* + +FSZ_SUPPORT equ 1 + +;********************************************************************* +;* File System Drivers * +;********************************************************************* + + USE32 +fsdrivers: +if FSZ_SUPPORT eq 1 + dw fsz_initrd +end if + dw cpio_initrd + dw tar_initrd + dw sfs_initrd + dw jamesm_initrd + dw 0 + +if FSZ_SUPPORT eq 1 +; ----------- FS/Z ---------- +; Find the kernel on initrd +; IN: esi: initrd pointer, ecx: initrd end, edi: kernel filename +; OUT: On Success +; esi: pointer to the first byte, ecx: size in bytes +; On Error +; ecx: 0 +fsz_initrd: + mov ebx, ecx + xor ecx, ecx + ; FS/Z superblock + ; get root dir inode + mov eax, dword [esi+560] ; FSZ_SuperBlock.rootdirfid + shl eax, 12 + add esi, eax + cmp dword [esi], 'FSIN' + je @f +.nolib: mov esi, nolib +.err: xor ecx, ecx + ret +.nocore: mov esi, nocore + jmp .err +@@: ; it has inlined data? +.again: add esi, 1024 ; FSZ_Inode.inlinedata + cmp dword [esi], 'FSDR' + je .srchdir + ; no, locate the data + mov ecx, dword [esi] + mov eax, dword [esi-1024+448] ; FSZ_Inode.sec + shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + cmp dword [esi], 'FSDR' + je .srchdir + ; inlined sector directory or list? + shl ecx, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, ecx + cmp dword [esi], 'FSDR' + jne .nolib +.srchdir: ; find sys/ + mov ecx, dword [esi+16] ; FSZ_DirEntHeader.numentries + mov eax, dword [edi] +@@: add esi, 128 ; directories than + cmp dword [esi+17], eax + je @f + dec ecx + jnz @b + jmp .nolib + ; found, get it's inode +@@: + mov eax, dword [esi] + shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + cmp dword [esi], 'FSIN' + jne .nolib + + ;this is not bullet proof + add edi, 4 + cmp byte [edi+3], '/' + je .again + + ; it has inlined data? + add esi, 1024 ; FSZ_Inode.inlinedata + cmp dword [esi], 'FSDR' + je .srchcore + ; no, locate the data + mov ecx, dword [esi] + mov eax, dword [esi-1024+448] ; FSZ_Inode.sec + shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + cmp dword [esi], 'FSDR' + je .srchdir + ; inlined sector directory or list? + shl ecx, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, ecx + cmp dword [esi], 'FSDR' + jne .nolib + +.srchcore: ; find filename + mov ecx, dword [esi+16] ; FSZ_DirEntHeader.numentries + ;filename, 8 characters supported + mov eax, dword [edi] + mov edx, dword [edi+4] +@@: add esi, 128 + cmp dword [esi+21], edx + jne .not + cmp dword [esi+17], eax + je @f +.not: dec ecx + jnz @b + jmp .nocore + ; found, get it's inode +@@: mov eax, dword [esi] + shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + cmp dword [esi], 'FSIN' + jne .nocore + ; get data + mov eax, dword [esi+448] ; FSZ_Inode.sec + mov ecx, dword [esi+464] ; FSZ_Inode.size + mov bl, byte [esi+492] ; FSZ_Inode.flags + + ; inline + cmp bl, 0FFh ; FSZ_IN_FLAG_INLINE + jne @f + add esi, 1024 + ret + ; direct data block +@@: or bl, bl ; FSZ_IN_FLAG_DIRECT + je .load + ; inlined sector directory or sector list +@@: cmp bl, 07Fh ; FSZ_IN_FLAG_SDINLINE + je @f + cmp bl, 080h ; FSZ_IN_FLAG_SECLIST + je @f + cmp bl, 1 ; FSZ_IN_FLAG_SD + jne .nocore + shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + mov eax, dword [esi] ; first FSZ_SectorList.sec + jmp .load +@@: add esi, 1024 + ; sector directory at esi, file size in ecx + mov eax, dword [esi] ; first FSZ_SectorList.sec +.load: shl eax, 12 + mov esi, dword [bootboot.initrd_ptr] + add esi, eax + ret +end if + +; ----------- cpio ---------- +; Find the kernel on initrd +; IN: esi: initrd pointer, ecx: initrd end, edi: kernel filename +; OUT: On Success +; esi: pointer to the first byte, ecx: size in bytes +; On Error +; ecx: 0 +cpio_initrd: + ; upper bound + mov ebx, ecx + xor ecx, ecx + ; strlen(kernel) + mov eax, edi + or eax, eax + jz .err + cmp byte [eax], 0 + jz .err + xor ecx, ecx +@@: inc ecx + inc eax + cmp byte [eax], 0 + jnz @b + mov dword [.ks], ecx + ; while(ptr.magic=='070707' && ptr +#include +#include +#include +#include + +/* the BOOTBOOT 1st stage loader code */ +extern unsigned char *_binary____boot_bin_start; + +/* entry point */ +int main(int argc, char** argv) +{ + // variables + unsigned char bootrec[512], data[512]; + int f, lba=0, lsn, bootlsn=-1; + + // check arguments + if(argc < 2) { + printf( "BOOTBOOT mkboot utility - bztsrc@gitlab\n\nUsage:\n" + " ./mkboot [partition lba]\n\n" + "Installs boot record on a disk. Disk can be a local file, a disk or partition\n" + "device. If you want to install it on a partition, you'll have to specify the\n" + "starting LBA of that partition as well. Requires that bootboot.bin is already\n" + "copied on the disk in a contiguous area in order to work.\n\n" + "Examples:\n" + " ./mkboot diskimage.dd - installing on a disk image\n" + " ./mkboot /dev/sda - installing as MBR\n" + " ./mkboot /dev/sda1 123 - installing as VBR\n"); + return 1; + } + if(argc > 2 || argv[2]!=NULL) { + lba = atoi(argv[2]); + } + // open file + f = open(argv[1], O_RDONLY); + if(f < 0) { + fprintf(stderr, "mkboot: file not found\n"); + return 2; + } + // read the boot record + if(read(f, data, 512)==-1) { + close(f); + fprintf(stderr, "mkboot: unable to read file\n"); + return 2; + } + // create the boot record. First copy the code then the data area from original sector on disk + memcpy((void*)&bootrec, (void*)&_binary____boot_bin_start, 512); + memcpy((void*)&bootrec+0xB, (void*)&data+0xB, 0x5A-0xB); // copy BPB (if any) + memcpy((void*)&bootrec+0x1B8, (void*)&data+0x1B8, 510-0x1B8); // copy WNTID and partitioning table (if any) + // now locate the second stage by magic bytes + for(lsn = 1; lsn < 1024*1024; lsn++) { + printf("Checking sector %d\r", lsn); + if(read(f, data, 512) != -1 && + data[0] == 0x55 && data[1] == 0xAA && data[3] == 0xE9 && data[8] == 'B' && data[12] == 'B') { + bootlsn=lsn; + break; + } + } + close(f); + // add bootboot.bin's address to boot record + if(bootlsn == -1) { + fprintf(stderr, "mkboot: unable to locate 2nd stage (bootboot.bin) in the first 512 Mbyte\n"); + return 2; + } + bootlsn += lba; + memcpy((void*)&bootrec+0x1B0, (void*)&bootlsn, 4); + // save boot record + f = open(argv[1], O_WRONLY); + if(f < 0 || write(f, bootrec, 512) <= 0) { + fprintf(stderr, "mkboot: unable to write boot record\n"); + return 3; + } + close(f); + // all went well + printf("mkboot: BOOTBOOT installed, 2nd stage starts at LBA %d\n", bootlsn); +} diff --git a/x86_64-bios/tinf.inc b/x86_64-bios/tinf.inc new file mode 100644 index 0000000..f2d16bf --- /dev/null +++ b/x86_64-bios/tinf.inc @@ -0,0 +1,500 @@ +;* +;* x86_64-bios/tinf.inc +;* +;* Copyright (C) 2017 bzt (bztsrc@gitlab) +;* +;* Permission is hereby granted, free of charge, to any person +;* obtaining a copy of this software and associated documentation +;* files (the "Software"), to deal in the Software without +;* restriction, including without limitation the rights to use, copy, +;* modify, merge, publish, distribute, sublicense, and/or sell copies +;* of the Software, and to permit persons to whom the Software is +;* furnished to do so, subject to the following conditions: +;* +;* The above copyright notice and this permission notice shall be +;* included in all copies or substantial portions of the Software. +;* +;* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +;* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +;* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +;* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +;* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +;* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +;* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +;* DEALINGS IN THE SOFTWARE. +;* +;* This file is part of the BOOTBOOT Protocol package. +;* @brief Tiny inflate, ported after tinflate.c by bzt +;* + +;IN: +; esi: gzipped initrd +; edi: output buffer (guaranteed to be big enough) +; ecx: output buffer size +tinf_uncompress: + push ecx + push edi + mov edi, tinf_bss_start + mov ecx, tinf_bss_end-tinf_bss_start + xor al, al + repnz stosb + pop edi + pop ecx + mov dword [d_end], ecx + add dword [d_end], edi +.again: + ; start a new block + cmp byte [d_btype], 255 + jne .notst + ; read final block flag +.next_blk: call tinf_getbit + mov byte [d_bfinal], al + ; read block type + xor ebx, ebx + mov cl, 2 + call tinf_read_bits + mov byte [d_btype], al + ; build fixed huffman trees + cmp al, 1 + jne @f + call tinf_build_fixed_trees + xor al, al + ; decode trees from stream +@@: cmp al, 2 + jne @f + call tinf_decode_trees +@@: +.notst: + ; process current block + cmp byte [d_btype], 0 + jnz @f + ; decompress uncompressed block + call tinf_inflate_uncompressed_block + JMP .procend +@@: cmp byte [d_btype], 1 + je @f + cmp byte [d_btype], 2 + jne tinf_err + ; decompress block with fixed/dyanamic huffman trees + ; trees were decoded previously, so it's the same routine for both +@@: call tinf_inflate_block_data +.procend: cmp al, 1 + jne @f + cmp byte [d_bfinal], 0 + ; the block has ended (without producing more data), but we + ; can't return without data, so start procesing next block + jz .next_blk + ret +@@: or al, al + jnz tinf_err + cmp edi, dword [d_end] + jbe .again + ret + +; build the fixed huffman trees +tinf_build_fixed_trees: + push edi + xor ecx, ecx + xor eax, eax + ; build fixed length tree + mov cl, 7 + mov edi, d_ltree_table + repnz stosb + mov al, 24 + stosb + mov al, 152 + stosb + mov al, 112 + stosb + mov edi, d_ltree_trans + + mov cl, 24 + mov ax, 256 +@@: stosw + inc ax + dec cl + jnz @b + + mov cl, 144 + xor ax, ax +@@: stosw + inc ax + dec cl + jnz @b + + mov cl, 8 + mov ax, 280 +@@: stosw + inc ax + dec cl + jnz @b + + mov cl, 112 + mov ax, 144 +@@: stosw + inc ax + dec cl + jnz @b + + ; build fixed distance tree + mov cl, 5 + mov edi, d_dtree_table + xor al, al + repnz stosb + mov al, 32 + stosb + + mov edi, d_dtree_trans + mov cl, 32 + xor ax, ax +@@: stosw + inc ax + dec cl + jnz @b + + pop edi + ret + +;IN: +; ebp: TINF_TREE +; ebx: lengths +; ecx: num +tinf_build_tree: + push edi + xor eax, eax + ; clear code length count table + mov edi, ebp + stosd ; for(i=0;i<16;i++) table[i]=0; + stosd + stosd + stosd + + ; scan symbol lengths, and sum code length counts + push ebx + push ecx +@@: mov al, byte [ebx] ;lengths[i] + inc ebx + inc byte [ebp+eax] ;table[lengths[i]]++ + dec cx + jnz @b + mov byte [ebp], 0 + + ; compute offset table for distribution sort + mov cl, 16 + xor edx, edx ;i + xor ebx, ebx ;sum + xor eax, eax +@@: mov word [offs+2*edx], bx + mov al, byte [ebp+edx] + add ebx, eax + inc edx + dec cl + jnz @b + + pop ecx + pop ebx ;lengths + + ; create code->symbol translation table (symbols sorted by code) + xor eax, eax ;i +@@: cmp byte [ebx], 0 + jz .null + xor edx, edx + mov dl, byte [ebx] ;lengths[i] + inc word [offs+2*edx] + mov dx, word [offs+2*edx] + dec dx + mov word [ebp+2*edx+16], ax +.null: inc ebx + inc eax + dec cx + jnz @b + + pop edi + ret + +tinf_decode_trees: + mov word [num], 0 + + ; get 5 bits HLIT (257-286) + xor ecx, ecx + mov cl, 5 + mov ebx, 257 + call tinf_read_bits + mov word [hlit], ax + mov word [num], ax + + ; get 5 bits HDIST (1-32) + mov cl, 5 + xor ebx, ebx + inc ebx + call tinf_read_bits + mov word [hdist], ax + add word [num], ax + + ; get 4 bits HCLEN (4-19) + mov cl, 4 + mov ebx, ecx + call tinf_read_bits + mov word [hclen], ax + + push edi + mov cl, 19 + mov edi, lengths + xor ax, ax + repnz stosw + pop edi + + ; read code lengths for code length alphabet + mov edx, clcidx + ; get 3 bits code length (0-7) +@@: mov cx, 3 + xor ebx, ebx + call tinf_read_bits + xor ebx, ebx + mov bl, byte [edx] ;clcidx[i] + mov byte[ebx+lengths], al + inc edx + dec word [hclen] + jnz @b + + ; build code length tree, temporarily use length tree + mov ebp, d_ltree + mov ebx, lengths + xor ecx, ecx + mov cl, 19 + call tinf_build_tree + + ; decode code lengths for the dynamic trees + mov edx, lengths +.decode: mov ebp, d_ltree + call tinf_decode_symbol + xor ebx, ebx + cmp al, 16 + jne .not16 + ; copy previous code length 3-6 times (read 2 bits) + mov cl, 2 + mov bl, 3 + call tinf_read_bits + mov cx, ax + mov al, byte [edx-1] ;lengths[num-1] +@@: mov byte [edx], al + inc edx + dec word [num] + dec cl + jnz @b + jmp .next + +.not16: cmp al, 17 + jne .not17 + ; repeat code length 0 for 3-10 times (read 3 bits) + mov cl, 3 + mov bl, cl + call tinf_read_bits + mov cx, ax +@@: mov byte [edx], 0 + inc edx + dec word [num] + dec cl + jnz @b + jmp .next + +.not17: cmp al, 18 + jne .not18 + ; repeat code length 0 for 11-138 times (read 7 bits) + mov cl, 7 + mov bl, 11 + call tinf_read_bits + mov cx, ax +@@: mov byte [edx], 0 + inc edx + dec word [num] + dec cl + jnz @b + jmp .next + +.not18: ; values 0-15 represent the actual code lengths + mov byte [edx], al + inc edx + dec word [num] + +.next: cmp word [num], 0 + jnz .decode + + ; build dynamic trees + mov ebp, d_ltree + mov ebx, lengths + xor ecx, ecx + mov cx, word [hlit] + call tinf_build_tree + + mov ebp, d_dtree + mov ebx, lengths + xor ecx, ecx + mov cx, word [hlit] + add ebx, ecx + mov cx, word [hdist] + call tinf_build_tree + ret + +;OUT: +; al: status +tinf_inflate_block_data: + cmp word [d_curlen], 0 + jne .notnull + mov ebp, d_ltree + call tinf_decode_symbol + ; literal byte + cmp ax, 256 + jae @f + stosb + xor al, al + ret +@@: cmp ax, 256 + jne @f + ; end of block + mov al, 1 + ret +@@: ; substring from sliding dictionary + sub eax, 257 + ; possibly get more bits from length code + xor ecx, ecx + mov cl, byte [length_bits+eax] + xor ebx, ebx + mov bx, word [length_base+2*eax] + call tinf_read_bits + mov word [d_curlen], ax + ; possibly get more bits from distance code + mov ebp, d_dtree + call tinf_decode_symbol + xor ecx, ecx + mov cl, byte [dist_bits+eax] + xor ebx, ebx + mov bx, word [dists_base+2*eax] + call tinf_read_bits + cmp dword [d_dict_ring], 0 + neg eax + mov dword [d_lzOff], eax +.notnull: mov eax, edi + add eax, dword [d_lzOff] + mov al, byte [eax] + stosb +@@: dec word [d_curlen] + xor al, al + ret + +;OUT: +; al: status +tinf_inflate_uncompressed_block: + cmp word [d_curlen], 0 + jne @f + ; get length + lodsw + ; get one's complement of length + add esi, 2 + ; increment length to properly return TINF_DONE below, without + ; producing data at the same time + mov word [d_curlen], ax + inc word [d_curlen] + ; make sure we start next block on a byte boundary + mov byte [d_bitcount], 0 +@@: dec byte [d_curlen] + cmp byte [d_curlen], 0 + jnz @f + mov al, 1 + ret +@@: movsb + xor al, al + ret + +;OUT: +; al,zeroflag bit +tinf_getbit: + mov al, byte [d_bitcount] + or al, al + jnz @f + lodsb + mov byte [d_tag], al + mov byte [d_bitcount], 8 +@@: dec byte [d_bitcount] + xor ax, ax + shr byte [d_tag], 1 + adc ax, 0 + ret + +;IN: +; ebx: base +; cl: num +;OUT: +; eax: bits +tinf_read_bits: + push edx + or cl, cl + jz .end + xor eax, eax + xor edx, edx + inc dl +.next: call tinf_getbit + jz @f + add ebx, edx +@@: shl edx, 1 + dec cl + jnz .next +.end: pop edx + mov eax, ebx + ret + +;IN: +; ebp: TINF_TREE +;OUT: +; eax: trans +tinf_decode_symbol: + push edx + xor eax, eax + xor ebx, ebx ;cur + xor ecx, ecx ;len + xor edx, edx ;sum + ; get more bits while code value is above sum +@@: shl ebx, 1 + call tinf_getbit + add ebx, eax + inc ecx + mov al, byte [ebp+ecx] + add edx, eax + sub ebx, eax + jns @b + add edx, ebx + mov ax, word [ebp+2*edx+16] +mov ebp, edx + pop edx + ret + +tinf_err: + mov esi, nogzip + jmp prot_diefunc + +length_bits: + db 0, 0, 0, 0, 0, 0, 0, 0 + db 1, 1, 1, 1, 2, 2, 2, 2 + db 3, 3, 3, 3, 4, 4, 4, 4 + db 5, 5, 5, 5, 0, 0, 0, 0 +length_base: + dw 3, 4, 5, 6, 7, 8, 9, 10 + dw 11, 13, 15, 17, 19, 23, 27, 31 + dw 35, 43, 51, 59, 67, 83, 99, 115 + dw 131, 163, 195, 227, 258, 0, 0 +dist_bits: + db 0, 0, 0, 0, 1, 1, 2, 2 + db 3, 3, 4, 4, 5, 5, 6, 6 + db 7, 7, 8, 8, 9, 9, 10, 10 + db 11, 11, 12, 12, 13, 13, 0, 0 +dists_base: + dw 1, 2, 3, 4, 5, 7, 9, 13 + dw 17, 25, 33, 49, 65, 97, 129, 193 + dw 257, 385, 513, 769, 1025, 1537, 2049, 3073 + dw 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 +clcidx: + db 16, 17, 18, 0, 8, 7, 9, 6 + db 10, 5, 11, 4, 12, 3, 13, 2 + db 14, 1, 15 + +d_btype: db 255 diff --git a/x86_64-efi/Makefile b/x86_64-efi/Makefile new file mode 100644 index 0000000..7053bdf --- /dev/null +++ b/x86_64-efi/Makefile @@ -0,0 +1,35 @@ +ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,) +GNUEFI_INCLUDES = -I/usr/include -I. -I/usr/include/efi -I/usr/include/efi/$(ARCH) -I/usr/include/efi/protocol +GNUEFI_CRT_OBJS = crt0-efi-$(ARCH).o +GNUEFI_LDS = elf_$(ARCH)_efi.lds + +CFLAGS = -mno-red-zone -mno-mmx -mno-sse -O2 -fpic -Wall -Wextra -Werror -fshort-wchar -fno-strict-aliasing -ffreestanding -fno-stack-protector -fno-stack-check -DCONFIG_$(ARCH) -DGNU_EFI_USE_MS_ABI -maccumulate-outgoing-args --std=c11 + +LDFLAGS = -nostdlib +LDFLAGS += -shared -Bsymbolic -L. $(GNUEFI_CRT_OBJS) + +TARGET = bootboot.efi + +all: tinflate.o $(TARGET) + +%.efi: %.so + @echo " src x86_64-efi (UEFI)" + @objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .rel.* -j .rela.* -j .reloc --target efi-app-$(ARCH) --subsystem=13 $^ $@ + @echo -n "BOOTBOOT Loader do not " | dd conv=notrunc of=$(TARGET) bs=1 seek=78 1>/dev/null 2>/dev/null + @gcc $(GNUEFI_INCLUDES) -Wall -fshort-wchar efirom.c -o efirom $(LIBS) + @./efirom $(TARGET) ../bootboot.rom || true + @mv $(TARGET) ../$(TARGET) + @rm tinflate.o efirom + +%.so: %.o + @ld $(LDFLAGS) tinflate.o $^ -o $@ -lefi -lgnuefi -T $(GNUEFI_LDS) + +%.o: %.c + @gcc $(GNUEFI_INCLUDES) $(CFLAGS) -c $< -o $@ + +%.o: %.S + @gcc $(GNUEFI_INCLUDES) $(CFLAGS) -c $< -o $@ + +clean: + @rm bootboot.o $(TARGET) ../$(TARGET) ../bootboot.rom *.so *.efi efirom tinflate.o 2>/dev/null || true + diff --git a/x86_64-efi/README.md b/x86_64-efi/README.md new file mode 100644 index 0000000..52093b8 --- /dev/null +++ b/x86_64-efi/README.md @@ -0,0 +1,35 @@ +BOOTBOOT UEFI Implementation +============================ + +See [BOOTBOOT Protocol](https://gitlab.com/bztsrc/bootboot) for common details. + +On [UEFI machines](http://www.uefi.org/), the PCI Option ROM is created from the standard EFI +OS loader application. + +Machine state +------------- + +IRQs masked. GDT unspecified, but valid, IDT unset. Code is running in supervisor mode in ring 0. + +File system drivers +------------------- + +For boot partition, UEFI version relies on any file system that's supported by EFI Simple File System Protocol. + +Installation +------------ + +1. *UEFI disk*: copy __bootboot.efi__ to **_FS0:\EFI\BOOT\BOOTX64.EFI_**. + +2. *UEFI ROM*: use __bootboot.rom__ which is a standard **_PCI Option ROM image_**. + +3. *GRUB*, *UEFI Boot Manager*: add __bootboot.efi__ to boot options. + +Limitations +----------- + +Known limitations: + + - Maps the first 16G of RAM. + - PCI Option ROM should be signed in order to work. + - Compressed initrd in ROM is limited to 16M. diff --git a/x86_64-efi/bootboot.c b/x86_64-efi/bootboot.c new file mode 100644 index 0000000..18eb071 --- /dev/null +++ b/x86_64-efi/bootboot.c @@ -0,0 +1,1076 @@ +/* + * x86_64-efi/bootboot.c + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief Booting code for EFI + * + */ + +// DEBUG defined in efidebug.h + +#define BBDEBUG 1 +//#define GOP_DEBUG BBDEBUG + +#if BBDEBUG +#define DBG(fmt, ...) do{Print(fmt,__VA_ARGS__); }while(0); +#else +#define DBG(fmt, ...) +#endif + +// get UEFI functions and environment +#include +#include +#include +#include +// get BOOTBOOT specific stuff +#include "../bootboot.h" +#include "tinf.h" +// comment out this include if you don't want FS/Z support +#include "../../osZ/etc/include/fsZ.h" +// get filesystem drivers for initrd +#include "fs.h" + +/*** ELF64 defines and structs ***/ +#define ELFMAG "\177ELF" +#define SELFMAG 4 +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define PT_LOAD 1 /* Loadable program segment */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ + +typedef struct +{ + unsigned char e_ident[16];/* Magic number and other info */ + UINT16 e_type; /* Object file type */ + UINT16 e_machine; /* Architecture */ + UINT32 e_version; /* Object file version */ + UINT64 e_entry; /* Entry point virtual address */ + UINT64 e_phoff; /* Program header table file offset */ + UINT64 e_shoff; /* Section header table file offset */ + UINT32 e_flags; /* Processor-specific flags */ + UINT16 e_ehsize; /* ELF header size in bytes */ + UINT16 e_phentsize; /* Program header table entry size */ + UINT16 e_phnum; /* Program header table entry count */ + UINT16 e_shentsize; /* Section header table entry size */ + UINT16 e_shnum; /* Section header table entry count */ + UINT16 e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +typedef struct +{ + UINT32 p_type; /* Segment type */ + UINT32 p_flags; /* Segment flags */ + UINT64 p_offset; /* Segment file offset */ + UINT64 p_vaddr; /* Segment virtual address */ + UINT64 p_paddr; /* Segment physical address */ + UINT64 p_filesz; /* Segment size in file */ + UINT64 p_memsz; /* Segment size in memory */ + UINT64 p_align; /* Segment alignment */ +} Elf64_Phdr; + +/*** PE32+ defines and structs ***/ +#define MZ_MAGIC 0x5a4d /* "MZ" */ +#define PE_MAGIC 0x00004550 /* "PE\0\0" */ +#define IMAGE_FILE_MACHINE_AMD64 0x8664 /* AMD x86_64 architecture */ +#define PE_OPT_MAGIC_PE32PLUS 0x020b /* PE32+ format */ +typedef struct +{ + UINT16 magic; /* MZ magic */ + UINT16 reserved[29]; /* reserved */ + UINT32 peaddr; /* address of pe header */ +} mz_hdr; + +typedef struct { + UINT32 magic; /* PE magic */ + UINT16 machine; /* machine type */ + UINT16 sections; /* number of sections */ + UINT32 timestamp; /* time_t */ + UINT32 sym_table; /* symbol table offset */ + UINT32 symbols; /* number of symbols */ + UINT16 opt_hdr_size; /* size of optional header */ + UINT16 flags; /* flags */ + UINT16 file_type; /* file type, PE32PLUS magic */ + UINT8 ld_major; /* linker major version */ + UINT8 ld_minor; /* linker minor version */ + UINT32 text_size; /* size of text section(s) */ + UINT32 data_size; /* size of data section(s) */ + UINT32 bss_size; /* size of bss section(s) */ + INT32 entry_point; /* file offset of entry point */ + INT32 code_base; /* relative code addr in ram */ +} pe_hdr; + +/*** EFI defines and structs ***/ +extern EFI_GUID GraphicsOutputProtocol; +extern EFI_GUID LoadedImageProtocol; +struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; +struct EFI_FILE_PROTOCOL; + +typedef +EFI_STATUS +(EFIAPI *EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME)( + IN struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT struct EFI_FILE_PROTOCOL **Root + ); + +/* Intel EFI headers has simple file protocol, but not GNU EFI */ +#ifndef EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION +typedef struct _EFI_SIMPLE_FILE_SYSTEM_PROTOCOL { + UINT64 Revision; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME OpenVolume; +} EFI_SIMPLE_FILE_SYSTEM_PROTOCOL; + +typedef struct _EFI_FILE_PROTOCOL { + UINT64 Revision; + EFI_FILE_OPEN Open; + EFI_FILE_CLOSE Close; + EFI_FILE_DELETE Delete; + EFI_FILE_READ Read; + EFI_FILE_WRITE Write; + EFI_FILE_GET_POSITION GetPosition; + EFI_FILE_SET_POSITION SetPosition; + EFI_FILE_GET_INFO GetInfo; + EFI_FILE_SET_INFO SetInfo; + EFI_FILE_FLUSH Flush; +} EFI_FILE_PROTOCOL; +#endif + +#ifndef EFI_PCI_OPTION_ROM_TABLE_GUID +#define EFI_PCI_OPTION_ROM_TABLE_GUID \ + { 0x7462660f, 0x1cbd, 0x48da, {0xad, 0x11, 0x91, 0x71, 0x79, 0x13, 0x83, 0x1c} } +typedef struct { + EFI_PHYSICAL_ADDRESS RomAddress; + EFI_MEMORY_TYPE MemoryType; + UINT32 RomLength; + UINT32 Seg; + UINT8 Bus; + UINT8 Dev; + UINT8 Func; + BOOLEAN ExecutedLegacyBiosImage; + BOOLEAN DontLoadEfiRom; +} EFI_PCI_OPTION_ROM_DESCRIPTOR; + +typedef struct { + UINT64 PciOptionRomCount; + EFI_PCI_OPTION_ROM_DESCRIPTOR *PciOptionRomDescriptors; +} EFI_PCI_OPTION_ROM_TABLE; +#endif + +/*** other defines and structs ***/ +typedef struct { + UINT8 magic[8]; + UINT8 chksum; + CHAR8 oemid[6]; + UINT8 revision; + UINT32 rsdt; + UINT32 length; + UINT64 xsdt; + UINT32 echksum; +} __attribute__((packed)) ACPI_RSDPTR; + +#define PAGESIZE 4096 + +/*** common variables ***/ +file_t env; // environment file descriptor +file_t initrd; // initrd file descriptor +file_t core; // kernel file descriptor +BOOTBOOT *bootboot; // the BOOTBOOT structure +UINT64 *paging; // paging table for MMU +UINT64 entrypoint; // kernel entry point +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Volume; +EFI_FILE_HANDLE RootDir; +EFI_FILE_PROTOCOL *Root; +unsigned char *kne; + +// default environment variables. M$ states that 1024x768 must be supported +int reqwidth = 1024, reqheight = 768; +char *kernelname="sys/core"; + +// alternative environment name +char *cfgname="sys/config"; + +/** + * function to convert ascii to number + */ +int atoi(unsigned char*c) +{ + int r=0; + while(*c>='0'&&*c<='9') { + r*=10; r+=*c-'0'; + c++; + } + return r; +} + +/** + * convert ascii to unicode characters + */ +CHAR16 * +a2u (char *str) +{ + static CHAR16 mem[PAGESIZE]; + int i; + + for (i = 0; str[i]; ++i) + mem[i] = (CHAR16) str[i]; + mem[i] = 0; + return mem; +} + +/** + * report status with message to standard output + */ +EFI_STATUS +report(EFI_STATUS Status,CHAR16 *Msg) +{ + Print(L"BOOTBOOT-PANIC: %s (EFI %r)\n",Msg,Status); + return Status; +} + +/** + * convert ascii octal number to binary number + */ +int oct2bin(unsigned char *str,int size) +{ + int s=0; + unsigned char *c=str; + while(size-->0){ + s*=8; + s+=*c-'0'; + c++; + } + return s; +} + +/** + * convert ascii hex number to binary number + */ +int hex2bin(unsigned char *str, int size) +{ + int v=0; + while(size-->0){ + v <<= 4; + if(*str>='0' && *str<='9') + v += (int)((unsigned char)(*str)-'0'); + else if(*str >= 'A' && *str <= 'F') + v += (int)((unsigned char)(*str)-'A'+10); + str++; + } + return v; +} + +/** + * Parse FS0:\BOOTBOOT\CONFIG or /sys/config + */ +EFI_STATUS +ParseEnvironment(unsigned char *cfg, int len, INTN argc, CHAR16 **argv) +{ + unsigned char *ptr=cfg-1; + int i; + // failsafe + if(len>PAGESIZE-1) { + len=PAGESIZE-1; + } + // append temporary variables provided on EFI command line + // if a key exists multiple times, the last is used + cfg[len]=0; + if(argc>2){ + ptr=cfg+len; + for(i=3;iHorizontalResolution, info->VerticalResolution, info->PixelFormat,valid?' ':'?'); + DBG(L"r:%x g:%x b:%x\n", + info->PixelFormat==PixelRedGreenBlueReserved8BitPerColor?0xff:( + info->PixelFormat==PixelBlueGreenRedReserved8BitPerColor?0xff0000:( + info->PixelFormat==PixelBitMask?info->PixelInformation.RedMask:0)), + info->PixelFormat==PixelRedGreenBlueReserved8BitPerColor || + info->PixelFormat==PixelBlueGreenRedReserved8BitPerColor?0xff00:( + info->PixelFormat==PixelBitMask?info->PixelInformation.GreenMask:0), + info->PixelFormat==PixelRedGreenBlueReserved8BitPerColor?0xff0000:( + info->PixelFormat==PixelBlueGreenRedReserved8BitPerColor?0xff:( + info->PixelFormat==PixelBitMask?info->PixelInformation.BlueMask:0))); +#endif + } + // if we have found a new, better mode + if(selectedMode != 9999 && selectedMode != nativeMode) { + status = uefi_call_wrapper(gop->SetMode, 2, gop, selectedMode); + if(!EFI_ERROR(status)) + nativeMode = selectedMode; + } + // get framebuffer properties + bootboot->fb_ptr=(void*)gop->Mode->FrameBufferBase; + bootboot->fb_size=gop->Mode->FrameBufferSize; + bootboot->fb_scanline=4*gop->Mode->Info->PixelsPerScanLine; + bootboot->fb_width=gop->Mode->Info->HorizontalResolution; + bootboot->fb_height=gop->Mode->Info->VerticalResolution; + bootboot->fb_type= + gop->Mode->Info->PixelFormat==PixelBlueGreenRedReserved8BitPerColor || + (gop->Mode->Info->PixelFormat==PixelBitMask && gop->Mode->Info->PixelInformation.BlueMask==0)? FB_ARGB : ( + gop->Mode->Info->PixelFormat==PixelRedGreenBlueReserved8BitPerColor || + (gop->Mode->Info->PixelFormat==PixelBitMask && gop->Mode->Info->PixelInformation.RedMask==0)? FB_ABGR : ( + gop->Mode->Info->PixelInformation.BlueMask==0xFF00? FB_RGBA : FB_BGRA + )); + DBG(L" * Screen %d x %d, scanline %d, fb @%lx %d bytes, type %d %s\n", + bootboot->fb_width, bootboot->fb_height, bootboot->fb_scanline, + bootboot->fb_ptr, bootboot->fb_size, gop->Mode->Info->PixelFormat, + bootboot->fb_type==FB_ARGB?L"ARGB":(bootboot->fb_type==FB_ABGR?L"ABGR":( + bootboot->fb_type==FB_RGBA?L"RGBA":L"BGRA"))); + return EFI_SUCCESS; +} + +/** + * Load a file from FS0 into memory + */ +EFI_STATUS +LoadFile(IN CHAR16 *FileName, OUT UINT8 **FileData, OUT UINTN *FileDataLength) +{ + EFI_STATUS status; + EFI_FILE_HANDLE FileHandle; + EFI_FILE_INFO *FileInfo; + UINT64 ReadSize; + UINTN BufferSize; + UINT8 *Buffer; + + if ((RootDir == NULL) || (FileName == NULL)) { + return report(EFI_NOT_FOUND,L"Empty Root or FileName\n"); + } + + status = uefi_call_wrapper(RootDir->Open, 5, RootDir, &FileHandle, FileName, + EFI_FILE_MODE_READ, EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM); + if (EFI_ERROR(status)) { + return status; +// Print(L"%s not found\n",FileName); +// return report(status,L"Open error"); + } + FileInfo = LibFileInfo(FileHandle); + if (FileInfo == NULL) { + uefi_call_wrapper(FileHandle->Close, 1, FileHandle); + Print(L"%s not found\n",FileName); + return report(EFI_NOT_FOUND,L"FileInfo error"); + } + ReadSize = FileInfo->FileSize; + if (ReadSize > 16*1024*1024) + ReadSize = 16*1024*1024; + FreePool(FileInfo); + + BufferSize = (UINTN)((ReadSize+PAGESIZE-1)/PAGESIZE); + status = uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, BufferSize, (EFI_PHYSICAL_ADDRESS*)&Buffer); + if (Buffer == NULL) { + uefi_call_wrapper(FileHandle->Close, 1, FileHandle); + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + } + status = uefi_call_wrapper(FileHandle->Read, 3, FileHandle, &ReadSize, Buffer); + uefi_call_wrapper(FileHandle->Close, 1, FileHandle); + if (EFI_ERROR(status)) { + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)(Buffer), BufferSize); + Print(L"%s not found\n",FileName); + return report(status,L"Read error"); + } + + *FileData = Buffer; + *FileDataLength = ReadSize; + return EFI_SUCCESS; +} + +/** + * Locate and load the kernel in initrd + */ +EFI_STATUS +LoadCore() +{ + int i=0,bss=0; + UINT8 *ptr; + core.ptr=ptr=NULL; + while(core.ptr==NULL && fsdrivers[i]!=NULL) { + core=(*fsdrivers[i++])((unsigned char*)initrd.ptr,kernelname); + } + // if every driver failed, try brute force, scan for the first elf or pe executable + if(core.ptr==NULL) { + DBG(L" * Autodetecting kernel%s\n",""); + i=initrd.size; + core.ptr=initrd.ptr; + while(i-->0) { + Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); + pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); + if((!CompareMem(ehdr->e_ident,ELFMAG,SELFMAG)||!CompareMem(ehdr->e_ident,"OS/Z",4))&& + ehdr->e_ident[EI_CLASS]==ELFCLASS64&& + ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& + ehdr->e_machine==EM_X86_64&& + ehdr->e_phnum>0){ + break; + } + if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && pehdr->magic == PE_MAGIC && + pehdr->machine == IMAGE_FILE_MACHINE_AMD64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS) { + break; + } + core.ptr++; + } + core.ptr=NULL; + } + + if(core.ptr!=NULL) { + Elf64_Ehdr *ehdr=(Elf64_Ehdr *)(core.ptr); + pe_hdr *pehdr=(pe_hdr*)(core.ptr + ((mz_hdr*)(core.ptr))->peaddr); + if((!CompareMem(ehdr->e_ident,ELFMAG,SELFMAG)||!CompareMem(ehdr->e_ident,"OS/Z",4))&& + ehdr->e_ident[EI_CLASS]==ELFCLASS64&&ehdr->e_ident[EI_DATA]==ELFDATA2LSB&& + ehdr->e_machine==EM_X86_64&&ehdr->e_phnum>0){ + // Parse ELF64 + DBG(L" * Parsing ELF64 @%lx\n",core.ptr); + Elf64_Phdr *phdr=(Elf64_Phdr *)((UINT8 *)ehdr+ehdr->e_phoff); + for(i=0;ie_phnum;i++){ + if(phdr->p_type==PT_LOAD && phdr->p_vaddr>>48==0xffff) { + // hack to keep symtab and strtab for shared libraries + core.size = phdr->p_filesz + (ehdr->e_type==3?0x4000:0); + ptr = (UINT8*)ehdr + phdr->p_offset; + bss = phdr->p_memsz - core.size; + entrypoint = ehdr->e_entry; + break; + } + phdr=(Elf64_Phdr *)((UINT8 *)phdr+ehdr->e_phentsize); + } + } else if(((mz_hdr*)(core.ptr))->magic==MZ_MAGIC && pehdr->magic == PE_MAGIC && + pehdr->machine == IMAGE_FILE_MACHINE_AMD64 && pehdr->file_type == PE_OPT_MAGIC_PE32PLUS && + (INT64)pehdr->code_base>>48==0xffff) { + //Parse PE32+ + DBG(L" * Parsing PE32+ @%lx\n",core.ptr); + core.size = (pehdr->entry_point-pehdr->code_base) + pehdr->text_size + pehdr->data_size; + ptr = core.ptr; + bss = pehdr->bss_size; + entrypoint = (INT64)pehdr->entry_point; + } + if(ptr==NULL || core.size<2 || entrypoint==0) + return report(EFI_LOAD_ERROR,L"Kernel is not a valid executable"); + // create core segment + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, + (core.size + bss + PAGESIZE-1)/PAGESIZE, (EFI_PHYSICAL_ADDRESS*)&core.ptr); + if (core.ptr == NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + CopyMem((void*)core.ptr,ptr,core.size); + if(bss>0) + ZeroMem((void*)core.ptr + core.size, bss); + core.size += bss; + DBG(L" * Entry point @%lx, text @%lx %d bytes\n",entrypoint, core.ptr, core.size); + core.size = ((core.size+PAGESIZE-1)/PAGESIZE)*PAGESIZE; + return EFI_SUCCESS; + + } + return report(EFI_LOAD_ERROR,L"Kernel not found in initrd"); +} + +/** + * Main EFI application entry point + */ +EFI_STATUS +efi_main (EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) +{ + EFI_LOADED_IMAGE *loaded_image = NULL; + EFI_GUID lipGuid = LOADED_IMAGE_PROTOCOL; + EFI_GUID RomTableGuid = EFI_PCI_OPTION_ROM_TABLE_GUID; + EFI_PCI_OPTION_ROM_TABLE *RomTable; + EFI_GUID bioGuid = BLOCK_IO_PROTOCOL; + EFI_BLOCK_IO *bio; + EFI_HANDLE *handles = NULL; + EFI_STATUS status=EFI_SUCCESS; + EFI_MEMORY_DESCRIPTOR *memory_map = NULL, *mement; + EFI_PARTITION_TABLE_HEADER *gptHdr; + EFI_PARTITION_ENTRY *gptEnt; + EFI_INPUT_KEY key; + UINTN i, j=0, handle_size=0,memory_map_size=0, map_key=0, desc_size=0; + UINT32 desc_version=0; + UINT64 lba_s=0,lba_e=0; + MMapEnt *mmapent, *last=NULL; + file_t ret={NULL,0}; + CHAR16 **argv, *initrdfile, *configfile, *help= + L"SYNOPSIS\n BOOTBOOT.EFI [ -h | -? | /h | /? ] [ INITRDFILE [ ENVIRONMENTFILE [...] ] ]\n\nDESCRIPTION\n Bootstraps an operating system via the BOOTBOOT Protocol.\n If arguments not given, defaults to\n FS0:\\BOOTBOOT\\INITRD as ramdisk image and\n FS0:\\BOOTBOOT\\CONFIG for boot environment.\n Additional \"key=value\" command line arguments will be appended to the\n environment. If INITRD not found, it will use the first bootable partition\n in GPT. If CONFIG not found, it will look for /sys/config inside the\n INITRD (or partition).\n\n As this is a loader, it is not supposed to return control to the shell.\n\n"; + INTN argc; + + // Initialize UEFI Library + InitializeLib(image, systab); + BS = systab->BootServices; + + // Parse command line arguments + // BOOTBOOT.EFI [-?|-h|/?|/h] [initrd [config [key=value...]] + argc = GetShellArgcArgv(image, &argv); + if(argc>1) { + if((argv[1][0]=='-'||argv[1][0]=='/')&&(argv[1][1]=='?'||argv[1][1]=='h')){ + Print(L"BOOTBOOT LOADER (build %s)\n\n%s",a2u(__DATE__),help); + return EFI_SUCCESS; + } + initrdfile=argv[1]; + } else { + initrdfile=L"\\BOOTBOOT\\INITRD"; + } + if(argc>2) { + configfile=argv[2]; + } else { + configfile=L"\\BOOTBOOT\\CONFIG"; + } + + Print(L"Booting OS...\n"); + + // get memory for bootboot structure + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, 1, (EFI_PHYSICAL_ADDRESS*)&bootboot); + if (bootboot == NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + ZeroMem((void*)bootboot,PAGESIZE); + CopyMem(bootboot->magic,BOOTBOOT_MAGIC,4); + // unlike BIOS+MultiBoot bootboot, no need to check if we have + // PAE + MSR + LME, as we're already in long mode. + __asm__ __volatile__ ( + "mov $1, %%eax;" + "cpuid;" + "shrl $24, %%ebx;" + "mov %%bx,%0" + : "=b"(bootboot->bspid) : : ); + + // locate InitRD in ROM + DBG(L" * Locate initrd in Option ROMs%s\n",L""); + RomTable = NULL; initrd.ptr = NULL; initrd.size = 0; + status=EFI_LOAD_ERROR; + // first, try RomTable + LibGetSystemConfigurationTable(&RomTableGuid,(void *)&(RomTable)); + if(RomTable!=NULL) { + for (i=0;iPciOptionRomCount;i++) { + ret.ptr=(UINT8*)RomTable->PciOptionRomDescriptors[i].RomAddress; + if(ret.ptr[0]==0x55 && ret.ptr[1]==0xAA && !CompareMem(ret.ptr+8,(const CHAR8 *)"INITRD",6)) { + CopyMem(&initrd.size,ret.ptr+16,4); + initrd.ptr=ret.ptr+32; + status=EFI_SUCCESS; + break; + } + } + } + //if not found, scan memory + if(EFI_ERROR(status) || initrd.ptr==NULL){ + status = uefi_call_wrapper(BS->GetMemoryMap, 5, + &memory_map_size, memory_map, NULL, &desc_size, NULL); + if (status!=EFI_BUFFER_TOO_SMALL || memory_map_size==0) { + return report(EFI_OUT_OF_RESOURCES,L"GetMemoryMap getSize"); + } + memory_map_size+=2*desc_size; + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, + (memory_map_size+PAGESIZE-1)/PAGESIZE, + (EFI_PHYSICAL_ADDRESS*)&memory_map); + if (memory_map == NULL) { + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + } + status = uefi_call_wrapper(BS->GetMemoryMap, 5, + &memory_map_size, memory_map, &map_key, &desc_size, &desc_version); + status=EFI_LOAD_ERROR; + for(mement=memory_map; + mementPhysicalStart==0 && mement->NumberOfPages==0)) + break; + // skip free and ACPI memory + if(mement->Type==7||mement->Type==9||mement->Type==10) + continue; + // according to spec, EFI Option ROMs must start on 512 bytes boundary, not 2048 + for(ret.ptr=(UINT8*)mement->PhysicalStart; + ret.ptr<(UINT8*)mement->PhysicalStart+mement->NumberOfPages*PAGESIZE; + ret.ptr+=512) { + if(ret.ptr[0]==0x55 && ret.ptr[1]==0xAA && !CompareMem(ret.ptr+8,(const CHAR8 *)"INITRD",6)) { + CopyMem(&initrd.size,ret.ptr+16,4); + initrd.ptr=ret.ptr+32; + status=EFI_SUCCESS; + goto foundinrom; + } + } + } +foundinrom: + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)memory_map, (memory_map_size+PAGESIZE-1)/PAGESIZE); + } + // fall back to INITRD on filesystem + if(EFI_ERROR(status) || initrd.ptr==NULL){ + // if the user presses any key now, we fallback to backup initrd + for(i=0;i<50;i++) { + // delay 5ms + uefi_call_wrapper(BS->Stall, 1, 5000); + status=uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key); + if(status!=EFI_NOT_READY) { + Print(L" * Backup initrd\n"); + initrdfile=L"\\BOOTBOOT\\INITRD.BAK"; + break; + } + } + DBG(L" * Locate initrd in %s\n",initrdfile); + // Initialize FS with the DeviceHandler from loaded image protocol + status = uefi_call_wrapper(BS->HandleProtocol, + 3, + image, + &lipGuid, + (void **) &loaded_image); + if (!EFI_ERROR(status) && loaded_image!=NULL) { + status=EFI_LOAD_ERROR; + RootDir = LibOpenRoot(loaded_image->DeviceHandle); + // load ramdisk + status=LoadFile(initrdfile,&initrd.ptr, &initrd.size); + } + } + // if not found, try architecture specific initrd file + if(EFI_ERROR(status) || initrd.ptr==NULL){ + initrdfile=L"\\BOOTBOOT\\X86_64"; + DBG(L" * Locate initrd in %s\n",initrdfile); + status=LoadFile(initrdfile,&initrd.ptr, &initrd.size); + } + // if even that failed, look for a partition + if(status!=EFI_SUCCESS || initrd.size==0){ + DBG(L" * Locate initrd in GPT%s\n",L""); + status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &bioGuid, NULL, &handle_size, handles); + if (status!=EFI_BUFFER_TOO_SMALL || handle_size==0) { + return report(EFI_OUT_OF_RESOURCES,L"LocateHandle getSize"); + } + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, (handle_size+PAGESIZE-1)/PAGESIZE, (EFI_PHYSICAL_ADDRESS*)&handles); + if(handles==NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages\n"); + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, 1, (EFI_PHYSICAL_ADDRESS*)&initrd.ptr); + if (initrd.ptr == NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + lba_s=lba_e=0; + status = uefi_call_wrapper(BS->LocateHandle, 5, ByProtocol, &bioGuid, NULL, &handle_size, handles); + for(i=0;iHandleProtocol, 3, handles[i], &bioGuid, (void **) &bio); + if(status!=EFI_SUCCESS || bio==NULL || bio->Media->BlockSize==0) + continue; + status=bio->ReadBlocks(bio, bio->Media->MediaId, 1, PAGESIZE, initrd.ptr); + if(status!=EFI_SUCCESS || CompareMem(initrd.ptr,EFI_PTAB_HEADER_ID,8)) + continue; + gptHdr = (EFI_PARTITION_TABLE_HEADER*)initrd.ptr; + if(gptHdr->NumberOfPartitionEntries>127) gptHdr->NumberOfPartitionEntries=127; + // first, look for a partition with bootable flag + ret.ptr= (UINT8*)(initrd.ptr + (gptHdr->PartitionEntryLBA-1) * bio->Media->BlockSize); + for(j=0;jNumberOfPartitionEntries;j++) { + gptEnt=(EFI_PARTITION_ENTRY*)ret.ptr; + if((ret.ptr[0]==0 && ret.ptr[1]==0 && ret.ptr[2]==0 && ret.ptr[3]==0) || gptEnt->StartingLBA==0) + break; + // use first partition with bootable flag as INITRD + if((gptEnt->Attributes & EFI_PART_USED_BY_OS)) goto partfound; + ret.ptr+=gptHdr->SizeOfPartitionEntry; + } + // if none, look for specific partition types + ret.ptr= (UINT8*)(initrd.ptr + (gptHdr->PartitionEntryLBA-1) * bio->Media->BlockSize); + for(j=0;jNumberOfPartitionEntries;j++) { + gptEnt=(EFI_PARTITION_ENTRY*)ret.ptr; + if((ret.ptr[0]==0 && ret.ptr[1]==0 && ret.ptr[2]==0 && ret.ptr[3]==0) || gptEnt->StartingLBA==0) + break; + // use the first OS/Z root partition for this architecture + if(!CompareMem(&gptEnt->PartitionTypeGUID.Data1,"OS/Z",4) && + gptEnt->PartitionTypeGUID.Data2==0x8664 && + !CompareMem(&gptEnt->PartitionTypeGUID.Data4[4],"root",4)) { +partfound: lba_s=gptEnt->StartingLBA; lba_e=gptEnt->EndingLBA; + initrd.size = (((lba_e-lba_s)*bio->Media->BlockSize + PAGESIZE-1)/PAGESIZE)*PAGESIZE; + status=EFI_SUCCESS; + goto partok; + } + ret.ptr+=gptHdr->SizeOfPartitionEntry; + } + } + return report(EFI_LOAD_ERROR,L"No boot partition"); +partok: + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)initrd.ptr, 1); + if(initrd.size>0 && bio!=NULL) { + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, initrd.size/PAGESIZE, (EFI_PHYSICAL_ADDRESS*)&initrd.ptr); + if (initrd.ptr == NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + status=bio->ReadBlocks(bio, bio->Media->MediaId, lba_s, initrd.size, initrd.ptr); + } else + status=EFI_LOAD_ERROR; + } + if(status==EFI_SUCCESS && initrd.size>0){ + //check if initrd is gzipped + if(initrd.ptr[0]==0x1f && initrd.ptr[1]==0x8b){ + unsigned char *addr,f; + int len=0, r; + TINF_DATA d; + DBG(L" * Gzip compressed initrd @%lx %d bytes\n",initrd.ptr,initrd.size); + // skip gzip header + addr=initrd.ptr+2; + if(*addr++!=8) goto gzerr; + f=*addr++; addr+=6; + if(f&4) { r=*addr++; r+=(*addr++ << 8); addr+=r; } + if(f&8) { while(*addr++ != 0); } + if(f&16) { while(*addr++ != 0); } + if(f&2) addr+=2; + d.source = addr; + // allocate destination buffer + CopyMem(&len,initrd.ptr+initrd.size-4,4); + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, (len+PAGESIZE-1)/PAGESIZE, (EFI_PHYSICAL_ADDRESS*)&addr); + if(addr==NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages\n"); + // decompress + d.bitcount = 0; + d.bfinal = 0; + d.btype = -1; + d.dict_size = 0; + d.dict_ring = NULL; + d.dict_idx = 0; + d.curlen = 0; + d.dest = addr; + d.destSize = len; + do { r = uzlib_uncompress(&d); } while (!r); + if (r != TINF_DONE) { +gzerr: return report(EFI_COMPROMISED_DATA,L"Unable to uncompress"); + } + // swap initrd.ptr with the uncompressed buffer + // if it's not page aligned, we came from ROM, no FreePages + if(((UINT64)initrd.ptr&(PAGESIZE-1))==0) + uefi_call_wrapper(BS->FreePages, 2, (EFI_PHYSICAL_ADDRESS)initrd.ptr, (initrd.size+PAGESIZE-1)/PAGESIZE); + initrd.ptr=addr; + initrd.size=len; + } + DBG(L" * Initrd loaded @%lx %d bytes\n",initrd.ptr,initrd.size); + kne=env.ptr=NULL; + // if there's an environment file, load it + if(loaded_image!=NULL && LoadFile(configfile,&env.ptr,&env.size)!=EFI_SUCCESS) { + env.ptr=NULL; + } + if(env.ptr==NULL) { + // if there were no environment file on boot partition, find it inside the INITRD + j=0; ret.ptr=NULL; ret.size=0; + while(ret.ptr==NULL && fsdrivers[j]!=NULL) { + ret=(*fsdrivers[j++])((unsigned char*)initrd.ptr,cfgname); + } + if(ret.ptr!=NULL) { + if(ret.size>PAGESIZE-1) + ret.size=PAGESIZE-1; + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, 1, (EFI_PHYSICAL_ADDRESS*)&env.ptr); + if(env.ptr==NULL) + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + ZeroMem((void*)env.ptr,PAGESIZE); + CopyMem((void*)env.ptr,ret.ptr,ret.size); + env.size=ret.size; + } + } + if(env.ptr!=NULL) { + ParseEnvironment(env.ptr,env.size, argc, argv); + } else { + // provide an empty environment for the OS + env.size=0; + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, 1, (EFI_PHYSICAL_ADDRESS*)&env.ptr); + if (env.ptr == NULL) { + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + } + ZeroMem((void*)env.ptr,PAGESIZE); + CopyMem((void*)env.ptr,"// N/A",8); + } + + // get linear frame buffer + status = GetLFB(); + if (EFI_ERROR(status) || bootboot->fb_width==0 || bootboot->fb_ptr==0) + return report(status, L"GOP failed, no framebuffer"); + + // collect information on system + bootboot->protocol=PROTOCOL_STATIC; + bootboot->loader_type=LOADER_UEFI; + bootboot->size=128; + bootboot->pagesize=12; + CopyMem((void *)&(bootboot->initrd_ptr),&initrd.ptr,8); + bootboot->initrd_size=((initrd.size+PAGESIZE-1)/PAGESIZE)*PAGESIZE; + CopyMem((void *)&(bootboot->x86_64.efi_ptr),&systab,8); + + // System tables and structures + DBG(L" * System tables%s\n",""); + LibGetSystemConfigurationTable(&AcpiTableGuid,(void *)&(bootboot->x86_64.acpi_ptr)); + LibGetSystemConfigurationTable(&SMBIOSTableGuid,(void *)&(bootboot->x86_64.smbi_ptr)); + LibGetSystemConfigurationTable(&MpsTableGuid,(void *)&(bootboot->x86_64.mp_ptr)); + + // FIX ACPI table pointer on TianoCore... + ret.ptr = (UINT8*)(bootboot->x86_64.acpi_ptr); + if(CompareMem(ret.ptr,(const CHAR8 *)"RSDT", 4) && CompareMem(ret.ptr,(const CHAR8 *)"XSDT", 4)) { + // scan for the real rsd ptr, as AcpiTableGuid returns bad address + for(i=1;i<256;i++) { + if(!CompareMem(ret.ptr+i, (const CHAR8 *)"RSD PTR ", 8)){ + ret.ptr+=i; + break; + } + } + // get ACPI system table + ACPI_RSDPTR *rsd = (ACPI_RSDPTR*)ret.ptr; + if(rsd->xsdt!=0) + bootboot->x86_64.acpi_ptr = rsd->xsdt; + else + bootboot->x86_64.acpi_ptr = (UINT64)((UINT32)rsd->rsdt); + } + + // Date and time + EFI_TIME t; + uefi_call_wrapper(ST->RuntimeServices->GetTime, 2, &t, NULL); + bootboot->datetime[0]=DecimaltoBCD(t.Year/100); + bootboot->datetime[1]=DecimaltoBCD(t.Year%100); + bootboot->datetime[2]=DecimaltoBCD(t.Month); + bootboot->datetime[3]=DecimaltoBCD(t.Day); + bootboot->datetime[4]=DecimaltoBCD(t.Hour); + bootboot->datetime[5]=DecimaltoBCD(t.Minute); + bootboot->datetime[6]=DecimaltoBCD(t.Second); + bootboot->datetime[7]=DecimaltoBCD(t.Daylight); + CopyMem((void *)&bootboot->timezone, &t.TimeZone, 2); + if(bootboot->timezone<-1440||bootboot->timezone>1440) // TZ in mins + bootboot->timezone=0; + DBG(L" * System time %d-%02d-%02d %02d:%02d:%02d GMT%s%d:%02d %s\n", + t.Year,t.Month,t.Day,t.Hour,t.Minute,t.Second, + bootboot->timezone>=0?L"+":L"",bootboot->timezone/60,bootboot->timezone%60, + t.Daylight?L"summertime":L""); + // get sys/core and parse + status=LoadCore(); + if (EFI_ERROR(status)) + return status; + if(kne!=NULL) + *kne='\n'; + + // query size of memory map + status = uefi_call_wrapper(BS->GetMemoryMap, 5, + &memory_map_size, memory_map, NULL, &desc_size, NULL); + if (status!=EFI_BUFFER_TOO_SMALL || memory_map_size==0) { + return report(EFI_OUT_OF_RESOURCES,L"GetMemoryMap getSize"); + } + // allocate memory for memory descriptors. We assume that one or two new memory + // descriptor may created by our next allocate calls and we round up to page size + memory_map_size+=2*desc_size; + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, + (memory_map_size+PAGESIZE-1)/PAGESIZE, + (EFI_PHYSICAL_ADDRESS*)&memory_map); + if (memory_map == NULL) { + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + } + + // create page tables + uefi_call_wrapper(BS->AllocatePages, 4, 0, 2, 24, (EFI_PHYSICAL_ADDRESS*)&paging); + if (paging == NULL) { + return report(EFI_OUT_OF_RESOURCES,L"AllocatePages"); + } + ZeroMem((void*)paging,23*PAGESIZE); + DBG(L" * Pagetables PML4 @%lx\n",paging); + //PML4 + paging[0]=(UINT64)((UINT8 *)paging+4*PAGESIZE)+1; // pointer to 2M PDPE (16G RAM identity mapped) + paging[511]=(UINT64)((UINT8 *)paging+PAGESIZE)+1; // pointer to 4k PDPE (core mapped at -2M) + //4k PDPE + paging[512+511]=(UINT64)((UINT8 *)paging+2*PAGESIZE+1); + //4k PDE + for(i=0;i<31;i++) + paging[2*512+480+i]=(UINT64)(((UINT8 *)(bootboot->fb_ptr)+(i<<21))+0x81); //map framebuffer + paging[2*512+511]=(UINT64)((UINT8 *)paging+3*PAGESIZE+1); + //4k PT + paging[3*512+0]=(UINT64)(bootboot)+1; + paging[3*512+1]=(UINT64)(env.ptr)+1; + for(i=0;i<(core.size/PAGESIZE);i++) + paging[3*512+2+i]=(UINT64)((UINT8 *)core.ptr+i*PAGESIZE+1); + paging[3*512+511]=(UINT64)((UINT8 *)paging+23*PAGESIZE+1); // core stack + //identity mapping + //2M PDPE + for(i=0;i<16;i++) + paging[4*512+i]=(UINT64)((UINT8 *)paging+(7+i)*PAGESIZE+1); + //first 2M mapped per page + paging[7*512]=(UINT64)((UINT8 *)paging+5*PAGESIZE+1); + for(i=0;i<512;i++) + paging[5*512+i]=(UINT64)(i*PAGESIZE+1); + //2M PDE + for(i=1;i<512*16;i++) + paging[7*512+i]=(UINT64)((i<<21)+0x81); + + // Get memory map + int cnt=3; +get_memory_map: + DBG(L" * Memory Map @%lx %d bytes #%d\n",memory_map, memory_map_size, 4-cnt); + mmapent=(MMapEnt *)&(bootboot->mmap); + status = uefi_call_wrapper(BS->GetMemoryMap, 5, + &memory_map_size, memory_map, &map_key, &desc_size, &desc_version); + if (EFI_ERROR(status)) { + return report(status,L"GetMemoryMap"); + } + last=NULL; + for(mement=memory_map; + mementsize>=PAGESIZE-128 || + (mement->PhysicalStart==0 && mement->NumberOfPages==0)) + break; + // failsafe, don't report our own structures as free + if( mement->NumberOfPages==0 || + ((mement->PhysicalStart <= (UINT64)bootboot && + mement->PhysicalStart+(mement->NumberOfPages*PAGESIZE) > (UINT64)bootboot) || + (mement->PhysicalStart <= (UINT64)env.ptr && + mement->PhysicalStart+(mement->NumberOfPages*PAGESIZE) > (UINT64)env.ptr) || + (mement->PhysicalStart <= (UINT64)initrd.ptr && + mement->PhysicalStart+(mement->NumberOfPages*PAGESIZE) > (UINT64)initrd.ptr) || + (mement->PhysicalStart <= (UINT64)core.ptr && + mement->PhysicalStart+(mement->NumberOfPages*PAGESIZE) > (UINT64)core.ptr) || + (mement->PhysicalStart <= (UINT64)paging && + mement->PhysicalStart+(mement->NumberOfPages*PAGESIZE) > (UINT64)paging) + )) { + continue; + } + mmapent->ptr=mement->PhysicalStart; + mmapent->size=(mement->NumberOfPages*PAGESIZE)+ + ((mement->Type>0&&mement->Type<5)||mement->Type==7?MMAP_FREE: + (mement->Type==9?MMAP_ACPIFREE: + (mement->Type==10?MMAP_ACPINVS: + (mement->Type==11||mement->Type==12?MMAP_MMIO: + MMAP_USED)))); + // merge continous areas of the same type + if(last!=NULL && + MMapEnt_Type(last) == MMapEnt_Type(mmapent) && + MMapEnt_Ptr(last)+MMapEnt_Size(last) == MMapEnt_Ptr(mmapent)) { + last->size+=MMapEnt_Size(mmapent); + mmapent->ptr=mmapent->size=0; + } else { + last=mmapent; + bootboot->size+=16; + mmapent++; + } + } + // --- NO PRINT AFTER THIS POINT --- + + //inform firmware that we're about to leave it's realm + status = uefi_call_wrapper(BS->ExitBootServices, 2, image, map_key); + if(EFI_ERROR(status)){ + cnt--; + if(cnt>0) goto get_memory_map; + return report(status,L"ExitBootServices"); + } + + //set up paging + __asm__ __volatile__ ( + "mov %0,%%rax;" + "mov %%rax,%%cr3" + : : "b"(paging) : "memory" ); + + //call _start() in sys/core + __asm__ __volatile__ ( + "xorq %%rsp, %%rsp;" + "pushq %0;" + "retq" + : : "a"(entrypoint): "memory" ); + } + return report(status,L"INITRD not found"); +} + diff --git a/x86_64-efi/crt0-efi-x86_64.o b/x86_64-efi/crt0-efi-x86_64.o new file mode 100644 index 0000000..82ed97d Binary files /dev/null and b/x86_64-efi/crt0-efi-x86_64.o differ diff --git a/x86_64-efi/efirom.c b/x86_64-efi/efirom.c new file mode 100644 index 0000000..56fd712 --- /dev/null +++ b/x86_64-efi/efirom.c @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2009 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * modifications for gnuefi by bzt (bztsrc@gitlab) + */ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define eprintf(...) fprintf ( stderr, __VA_ARGS__ ) + +/** Command-line options */ +struct options { + uint16_t vendor; + uint16_t device; +}; + +/** + * Allocate memory + * + * @v len Length of memory to allocate + * @ret ptr Pointer to allocated memory + */ +static void * xmalloc ( size_t len ) { + void *ptr; + + ptr = malloc ( len ); + if ( ! ptr ) { + eprintf ( "Could not allocate %zd bytes\n", len ); + exit ( 1 ); + } + + return ptr; +} + +/** + * Get file size + * + * @v file File + * @v len File size + */ +/* +static size_t file_size ( FILE *file ) { + ssize_t len; + + return len; +} +*/ +/** + * Read information from PE headers + * + * @v pe PE file + * @ret machine Machine type + * @ret subsystem EFI subsystem + */ +static void read_pe_info ( void *pe, uint16_t *machine, + uint16_t *subsystem ) { + IMAGE_DOS_HEADER *dos; + union { + IMAGE_NT_HEADERS nt64; + } *nt; + + /* Locate NT header */ + dos = pe; + nt = ( pe + dos->e_lfanew ); + + /* Parse out PE information */ + *machine = nt->nt64.FileHeader.Machine; + *subsystem = nt->nt64.OptionalHeader.Subsystem; +} + +/** + * Convert EFI image to ROM image + * + * @v pe EFI file + * @v rom ROM file + */ +static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) { + struct { + EFI_PCI_EXPANSION_ROM_HEADER rom; + PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) )); + uint8_t checksum; + } *headers; + struct stat pe_stat; + size_t pe_size; + size_t rom_size; + void *buf; + void *payload; + unsigned int i; + uint8_t checksum; + + /* Determine PE file size */ + if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) { + eprintf ( "Could not stat PE file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } + pe_size = pe_stat.st_size; + + /* Determine ROM file size */ + rom_size = ( ( pe_size + sizeof ( *headers ) + 511 ) & ~511 ); + + /* Allocate ROM buffer and read in PE file */ + buf = xmalloc ( rom_size ); + memset ( buf, 0, rom_size ); + headers = buf; + payload = ( buf + sizeof ( *headers ) ); + if ( fread ( payload, pe_size, 1, pe ) != 1 ) { + eprintf ( "Could not read PE file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } + + /* Construct ROM header */ + headers->rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE; + headers->rom.InitializationSize = ( rom_size / 512 ); + headers->rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE; + read_pe_info ( payload, &headers->rom.EfiMachineType, + &headers->rom.EfiSubsystem ); + headers->rom.EfiImageHeaderOffset = sizeof ( *headers ); + headers->rom.PcirOffset = + offsetof ( typeof ( *headers ), pci ); + headers->pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE; + headers->pci.VendorId = opts->vendor ? opts->vendor : 0x8086; + headers->pci.DeviceId = opts->device ? opts->device : 0x100E; + headers->pci.Length = sizeof ( headers->pci ); + headers->pci.ClassCode[0] = PCI_CLASS_NETWORK; + headers->pci.ImageLength = ( rom_size / 512 ); + headers->pci.CodeType = 0x03; /* No constant in EFI headers? */ + headers->pci.Indicator = 0x80; /* No constant in EFI headers? */ + + /* Fix image checksum */ + for ( i = 0, checksum = 0 ; i < rom_size ; i++ ) + checksum += *( ( uint8_t * ) buf + i ); + headers->checksum -= checksum; + + /* Write out ROM */ + if ( fwrite ( buf, rom_size, 1, rom ) != 1 ) { + eprintf ( "Could not write ROM file: %s\n", + strerror ( errno ) ); + exit ( 1 ); + } +} + +/** + * Print help + * + * @v program_name Program name + */ +static void print_help ( const char *program_name ) { + eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] " + "infile outfile\n", program_name ); +} + +/** + * Parse command-line options + * + * @v argc Argument count + * @v argv Argument list + * @v opts Options structure to populate + */ +static int parse_options ( const int argc, char **argv, + struct options *opts ) { + char *end; + int c; + + while (1) { + int option_index = 0; + static struct option long_options[] = { + { "vendor", required_argument, NULL, 'v' }, + { "device", required_argument, NULL, 'd' }, + { "help", 0, NULL, 'h' }, + { 0, 0, 0, 0 } + }; + + if ( ( c = getopt_long ( argc, argv, "v:d:h", + long_options, + &option_index ) ) == -1 ) { + break; + } + + switch ( c ) { + case 'v': + opts->vendor = strtoul ( optarg, &end, 16 ); + if ( *end ) { + eprintf ( "Invalid vendor \"%s\"\n", optarg ); + exit ( 2 ); + } + break; + case 'd': + opts->device = strtoul ( optarg, &end, 16 ); + if ( *end ) { + eprintf ( "Invalid device \"%s\"\n", optarg ); + exit ( 2 ); + } + break; + case 'h': + print_help ( argv[0] ); + exit ( 0 ); + case '?': + default: + exit ( 2 ); + } + } + return optind; +} + +int main ( int argc, char **argv ) { + struct options opts = { + }; + unsigned int infile_index; + const char *infile_name; + const char *outfile_name; + FILE *infile; + FILE *outfile; + + /* Parse command-line arguments */ + infile_index = parse_options ( argc, argv, &opts ); + if ( argc != ( infile_index + 2 ) ) { + print_help ( argv[0] ); + exit ( 2 ); + } + infile_name = argv[infile_index]; + outfile_name = argv[infile_index + 1]; + + /* Open input and output files */ + infile = fopen ( infile_name, "r" ); + if ( ! infile ) { + eprintf ( "Could not open %s for reading: %s\n", + infile_name, strerror ( errno ) ); + exit ( 1 ); + } + outfile = fopen ( outfile_name, "w" ); + if ( ! outfile ) { + eprintf ( "Could not open %s for writing: %s\n", + outfile_name, strerror ( errno ) ); + exit ( 1 ); + } + + /* Convert file */ + make_efi_rom ( infile, outfile, &opts ); + + fclose ( outfile ); + fclose ( infile ); + + return 0; +} diff --git a/x86_64-efi/elf_x86_64_efi.lds b/x86_64-efi/elf_x86_64_efi.lds new file mode 100644 index 0000000..942d1f3 --- /dev/null +++ b/x86_64-efi/elf_x86_64_efi.lds @@ -0,0 +1,74 @@ +/* Same as elf_x86_64_fbsd_efi.lds, except for OUTPUT_FORMAT below - KEEP IN SYNC */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .eh_frame : + { + *(.eh_frame) + } + . = ALIGN(4096); + .text : + { + _text = .; + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + . = ALIGN(16); + } + _etext = .; + _text_size = . - _text; + . = ALIGN(4096); + .reloc : + { + *(.reloc) + } + . = ALIGN(4096); + .data : + { + _data = .; + *(.rodata*) + *(.got.plt) + *(.got) + *(.data*) + *(.sdata) + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + *(.rel.local) + } + .note.gnu.build-id : { *(.note.gnu.build-id) } + + _edata = .; + _data_size = . - _etext; + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.data*) + *(.rela.got) + *(.rela.stab) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + .ignored.reloc : + { + *(.rela.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} diff --git a/x86_64-efi/fs.h b/x86_64-efi/fs.h new file mode 100644 index 0000000..8b9ab57 --- /dev/null +++ b/x86_64-efi/fs.h @@ -0,0 +1,264 @@ +/* + * x86_64-efi/fs.h + * + * Copyright (C) 2017 bzt (bztsrc@gitlab) + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * This file is part of the BOOTBOOT Protocol package. + * @brief Filesystem drivers for initial ramdisk. + * + */ + +/** + * return type for fs drivers + */ +typedef struct { + UINT8 *ptr; + UINTN size; +} file_t; + +extern int oct2bin(unsigned char *str,int size); +extern int hex2bin(unsigned char *str,int size); +extern CHAR16 *a2u (char *str); + +#ifdef _FS_Z_H_ +/** + * FS/Z initrd (OS/Z's native file system) + */ +file_t fsz_initrd(unsigned char *initrd_p, char *kernel) +{ + FSZ_SuperBlock *sb = (FSZ_SuperBlock *)initrd_p; + FSZ_DirEnt *ent; + FSZ_Inode *in=(FSZ_Inode *)(initrd_p+sb->rootdirfid*FSZ_SECSIZE); + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || CompareMem(sb->magic,FSZ_MAGIC,4) || kernel==NULL){ + return ret; + } + DBG(L" * FS/Z %s\n",a2u(kernel)); + // Get the inode + int i,ss=1<<(sb->logsec+11); + char *s,*e; + s=e=kernel; + i=0; +again: + while(*e!='/'&&*e!=0){e++;} + if(*e=='/'){e++;} + if(!CompareMem(in->magic,FSZ_IN_MAGIC,4)){ + //is it inlined? + if(!CompareMem(in->inlinedata,FSZ_DIR_MAGIC,4)){ + ent=(FSZ_DirEnt *)(in->inlinedata); + } else if(!CompareMem(initrd_p+in->sec*ss,FSZ_DIR_MAGIC,4)){ + // go, get the sector pointed + ent=(FSZ_DirEnt *)(initrd_p+in->sec*ss); + } else { + return ret; + } + //skip header + FSZ_DirEntHeader *hdr=(FSZ_DirEntHeader *)ent; ent++; + //iterate on directory entries + int j=hdr->numentries; + while(j-->0){ + if(!CompareMem(ent->name,s,e-s)) { + if(*e==0) { + i=ent->fid; + break; + } else { + s=e; + in=(FSZ_Inode *)(initrd_p+ent->fid*ss); + goto again; + } + } + ent++; + } + } else { + i=0; + } + if(i!=0) { + // fid -> inode ptr -> data ptr + FSZ_Inode *in=(FSZ_Inode *)(initrd_p+i*ss); + if(!CompareMem(in->magic,FSZ_IN_MAGIC,4)){ + ret.size=in->size; + switch(FSZ_FLAG_TRANSLATION(in->flags)) { + case FSZ_IN_FLAG_INLINE: + // inline data + ret.ptr=(UINT8*)(initrd_p+i*ss+1024); + break; + case FSZ_IN_FLAG_SECLIST: + case FSZ_IN_FLAG_SDINLINE: + // sector directory or list inlined + ret.ptr=(UINT8*)(initrd_p + *((UINT64*)&in->inlinedata) * ss); + break; + case FSZ_IN_FLAG_DIRECT: + // direct data + ret.ptr=(UINT8*)(initrd_p + in->sec * ss); + break; + // sector directory (only one level supported here, and no holes in files) + case FSZ_IN_FLAG_SECLIST0: + case FSZ_IN_FLAG_SD: + ret.ptr=(UINT8*)(initrd_p + (unsigned int)(((FSZ_SectorList *)(initrd_p+in->sec*ss))->sec) * ss); + break; + default: + ret.size=0; + break; + } + } + } + return ret; +} +#endif + +/** + * cpio archive + */ +file_t cpio_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr=initrd_p; + int k; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || + (CompareMem(initrd_p,"070701",6) && CompareMem(initrd_p,"070702",6) && CompareMem(initrd_p,"070707",6))) + return ret; + DBG(L" * cpio %s\n",a2u(kernel)); + k=strlena((unsigned char*)kernel); + // hpodc archive + while(!CompareMem(ptr,"070707",6)){ + int ns=oct2bin(ptr+8*6+11,6); + int fs=oct2bin(ptr+8*6+11+6,11); + if(!CompareMem(ptr+9*6+2*11,kernel,k+1)){ + ret.size=fs; + ret.ptr=(UINT8*)(ptr+9*6+2*11+ns); + return ret; + } + ptr+=(76+ns+fs); + } + // newc and crc archive + while(!CompareMem(ptr,"07070",5)){ + int fs=hex2bin(ptr+8*6+6,8); + int ns=hex2bin(ptr+8*11+6,8); + if(!CompareMem(ptr+110,kernel,k+1)){ + ret.size=fs; + ret.ptr=(UINT8*)(ptr+((110+ns+3)/4)*4); + return ret; + } + ptr+=((110+ns+3)/4)*4 + ((fs+3)/4)*4; + } + return ret; +} + +/** + * ustar tarball archive + */ +file_t tar_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr=initrd_p; + int k; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || CompareMem(initrd_p+257,"ustar",5)) + return ret; + DBG(L" * tar %s\n",a2u(kernel)); + k=strlena((unsigned char*)kernel); + while(!CompareMem(ptr+257,"ustar",5)){ + int fs=oct2bin(ptr+0x7c,11); + if(!CompareMem(ptr,kernel,k+1)){ + ret.size=fs; + ret.ptr=(UINT8*)(ptr+512); + return ret; + } + ptr+=(((fs+511)/512)+1)*512; + } + return ret; +} + +/** + * Simple File System + */ +file_t sfs_initrd(unsigned char *initrd_p, char *kernel) +{ + unsigned char *ptr, *end; + int k,bs,ver; + file_t ret = { NULL, 0 }; + if(initrd_p==NULL || kernel==NULL || (CompareMem(initrd_p+0x1AC,"SFS",3) && CompareMem(initrd_p+0x1A6,"SFS",3))) + return ret; + // 1.0 Brendan's version, 1.10 BenLunt's version + ver=!CompareMem(initrd_p+0x1A6,"SFS",3)?10:0; + bs=1<<(7+(UINT8)initrd_p[ver?0x1B6:0x1BC]); + end=initrd_p + *((UINT64 *)&initrd_p[ver?0x1AA:0x1B0]) * bs; // base + total_number_of_blocks * blocksize + // get index area + ptr=end - *((UINT64 *)&initrd_p[ver?0x19E:0x1A4]); // end - size of index area + // got a Starting Marker Entry? + if(ptr[0]!=2) + return ret; + DBG(L" * SFS 1.%d %s\n",ver,a2u(kernel)); + k=strlena((unsigned char*)kernel); + // iterate on index until we reach the end or Volume Identifier + while(ptr + +/* calling convention */ +#ifndef TINFCC + #ifdef __WATCOMC__ + #define TINFCC __cdecl + #else + #define TINFCC + #endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* ok status, more data produced */ +#define TINF_OK 0 +/* end of compressed stream reached */ +#define TINF_DONE 1 +#define TINF_DATA_ERROR (-3) +#define TINF_CHKSUM_ERROR (-4) +#define TINF_DICT_ERROR (-5) + +/* checksum types */ +#define TINF_CHKSUM_NONE 0 +#define TINF_CHKSUM_ADLER 1 +#define TINF_CHKSUM_CRC 2 + +/* data structures */ + +typedef struct { + unsigned short table[16]; /* table of code length counts */ + unsigned short trans[288]; /* code -> symbol translation table */ +} TINF_TREE; + +struct TINF_DATA; +typedef struct TINF_DATA { + const unsigned char *source; + /* If source above is NULL, this function will be used to read + next byte from source stream */ + unsigned char (*readSource)(struct TINF_DATA *data); + + unsigned int tag; + unsigned int bitcount; + + /* Buffer start */ + unsigned char *destStart; + /* Buffer total size */ + unsigned int destSize; + /* Current pointer in buffer */ + unsigned char *dest; + /* Remaining bytes in buffer */ + unsigned int destRemaining; + + /* Accumulating checksum */ + unsigned int checksum; + char checksum_type; + + int btype; + int bfinal; + unsigned int curlen; + int lzOff; + unsigned char *dict_ring; + unsigned int dict_size; + unsigned int dict_idx; + + TINF_TREE ltree; /* dynamic length/symbol tree */ + TINF_TREE dtree; /* dynamic distance tree */ +} TINF_DATA; + +#define TINF_PUT(d, c) \ + { \ + *d->dest++ = c; \ + if (d->dict_ring) { d->dict_ring[d->dict_idx++] = c; if (d->dict_idx == d->dict_size) d->dict_idx = 0; } \ + } + +unsigned char TINFCC uzlib_get_byte(TINF_DATA *d); + +/* Decompression API */ + +void TINFCC uzlib_init(void); +void TINFCC uzlib_uncompress_init(TINF_DATA *d, void *dict, unsigned int dictLen); +int TINFCC uzlib_uncompress(TINF_DATA *d); +int TINFCC uzlib_uncompress_chksum(TINF_DATA *d); + +int TINFCC uzlib_zlib_parse_header(TINF_DATA *d); +int TINFCC uzlib_gzip_parse_header(TINF_DATA *d); + +/* Compression API */ + +void TINFCC uzlib_compress(void *data, const uint8_t *src, unsigned slen); + +/* Checksum API */ + +/* prev_sum is previous value for incremental computation, 1 initially */ +uint32_t TINFCC uzlib_adler32(const void *data, unsigned int length, uint32_t prev_sum); +/* crc is previous value for incremental computation, 0xffffffff initially */ +uint32_t TINFCC uzlib_crc32(const void *data, unsigned int length, uint32_t crc); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* TINF_H_INCLUDED */ diff --git a/x86_64-efi/tinflate.c b/x86_64-efi/tinflate.c new file mode 100644 index 0000000..fc609fc --- /dev/null +++ b/x86_64-efi/tinflate.c @@ -0,0 +1,475 @@ +/* + * tinflate - tiny inflate + * + * Copyright (c) 2003 by Joergen Ibsen / Jibz + * All Rights Reserved + * http://www.ibsensoftware.com/ + * + * Copyright (c) 2014-2016 by Paul Sokolovsky + * + * This software is provided 'as-is', without any express + * or implied warranty. In no event will the authors be + * held liable for any damages arising from the use of + * this software. + * + * Permission is granted to anyone to use this software + * for any purpose, including commercial applications, + * and to alter it and redistribute it freely, subject to + * the following restrictions: + * + * 1. The origin of this software must not be + * misrepresented; you must not claim that you + * wrote the original software. If you use this + * software in a product, an acknowledgment in + * the product documentation would be appreciated + * but is not required. + * + * 2. Altered source versions must be plainly marked + * as such, and must not be misrepresented as + * being the original software. + * + * 3. This notice may not be removed or altered from + * any source distribution. + */ + +#include "tinf.h" + +uint32_t tinf_get_le_uint32(TINF_DATA *d); +uint32_t tinf_get_be_uint32(TINF_DATA *d); + +/* --------------------------------------------------- * + * -- uninitialized global data (static structures) -- * + * --------------------------------------------------- */ + +#ifdef RUNTIME_BITS_TABLES + +/* extra bits and base tables for length codes */ +unsigned char length_bits[30]; +unsigned short length_base[30]; + +/* extra bits and base tables for distance codes */ +unsigned char dist_bits[30]; +unsigned short dist_base[30]; + +#else + +const unsigned char length_bits[30] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, + 5, 5, 5, 5 +}; +const unsigned short length_base[30] = { + 3, 4, 5, 6, 7, 8, 9, 10, + 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, + 131, 163, 195, 227, 258 +}; + +const unsigned char dist_bits[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, + 3, 3, 4, 4, 5, 5, 6, 6, + 7, 7, 8, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 13, 13 +}; +const unsigned short dist_base[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, + 17, 25, 33, 49, 65, 97, 129, 193, + 257, 385, 513, 769, 1025, 1537, 2049, 3073, + 4097, 6145, 8193, 12289, 16385, 24577 +}; + +#endif + +/* special ordering of code length codes */ +const unsigned char clcidx[] = { + 16, 17, 18, 0, 8, 7, 9, 6, + 10, 5, 11, 4, 12, 3, 13, 2, + 14, 1, 15 +}; + +/* ----------------------- * + * -- utility functions -- * + * ----------------------- */ + +#ifdef RUNTIME_BITS_TABLES +/* build extra bits and base tables */ +static void tinf_build_bits_base(unsigned char *bits, unsigned short *base, int delta, int first) +{ + int i, sum; + + /* build bits table */ + for (i = 0; i < delta; ++i) bits[i] = 0; + for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta; + + /* build base table */ + for (sum = first, i = 0; i < 30; ++i) + { + base[i] = sum; + sum += 1 << bits[i]; + } +} +#endif + +/* build the fixed huffman trees */ +static void tinf_build_fixed_trees(TINF_TREE *lt, TINF_TREE *dt) +{ + int i; + + /* build fixed length tree */ + for (i = 0; i < 7; ++i) lt->table[i] = 0; + + lt->table[7] = 24; + lt->table[8] = 152; + lt->table[9] = 112; + + for (i = 0; i < 24; ++i) lt->trans[i] = 256 + i; + for (i = 0; i < 144; ++i) lt->trans[24 + i] = i; + for (i = 0; i < 8; ++i) lt->trans[24 + 144 + i] = 280 + i; + for (i = 0; i < 112; ++i) lt->trans[24 + 144 + 8 + i] = 144 + i; + + /* build fixed distance tree */ + for (i = 0; i < 5; ++i) dt->table[i] = 0; + + dt->table[5] = 32; + + for (i = 0; i < 32; ++i) dt->trans[i] = i; +} + +/* given an array of code lengths, build a tree */ +static void tinf_build_tree(TINF_TREE *t, const unsigned char *lengths, unsigned int num) +{ + unsigned short offs[16]; + unsigned int i, sum; + + /* clear code length count table */ + for (i = 0; i < 16; ++i) t->table[i] = 0; + + /* scan symbol lengths, and sum code length counts */ + for (i = 0; i < num; ++i) t->table[lengths[i]]++; + + t->table[0] = 0; + + /* compute offset table for distribution sort */ + for (sum = 0, i = 0; i < 16; ++i) + { + offs[i] = sum; + sum += t->table[i]; + } + + /* create code->symbol translation table (symbols sorted by code) */ + for (i = 0; i < num; ++i) + { + if (lengths[i]) t->trans[offs[lengths[i]]++] = i; + } +} + +/* ---------------------- * + * -- decode functions -- * + * ---------------------- */ + +unsigned char uzlib_get_byte(TINF_DATA *d) +{ + if (d->source) { + return *d->source++; + } + return d->readSource(d); +} + +uint32_t tinf_get_le_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val >> 8 | uzlib_get_byte(d) << 24; + } + return val; +} + +uint32_t tinf_get_be_uint32(TINF_DATA *d) +{ + uint32_t val = 0; + int i; + for (i = 4; i--;) { + val = val << 8 | uzlib_get_byte(d); + } + return val; +} + +/* get one bit from source stream */ +static int tinf_getbit(TINF_DATA *d) +{ + unsigned int bit; + + /* check if tag is empty */ + if (!d->bitcount--) + { + /* load next tag */ + d->tag = uzlib_get_byte(d); + d->bitcount = 7; + } + + /* shift bit out of tag */ + bit = d->tag & 0x01; + d->tag >>= 1; + + return bit; +} + +/* read a num bit value from a stream and add base */ +static unsigned int tinf_read_bits(TINF_DATA *d, int num, int base) +{ + unsigned int val = 0; + + /* read num bits */ + if (num) + { + unsigned int limit = 1 << (num); + unsigned int mask; + + for (mask = 1; mask < limit; mask *= 2) + if (tinf_getbit(d)) val += mask; + } + + return val + base; +} + +/* given a data stream and a tree, decode a symbol */ +static int tinf_decode_symbol(TINF_DATA *d, TINF_TREE *t) +{ + int sum = 0, cur = 0, len = 0; + + /* get more bits while code value is above sum */ + do { + + cur = 2*cur + tinf_getbit(d); + + ++len; + + sum += t->table[len]; + cur -= t->table[len]; + + } while (cur >= 0); + + return t->trans[sum + cur]; +} + +/* given a data stream, decode dynamic trees from it */ +static void tinf_decode_trees(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + unsigned char lengths[288+32]; + unsigned int hlit, hdist, hclen; + unsigned int i, num, length; + + /* get 5 bits HLIT (257-286) */ + hlit = tinf_read_bits(d, 5, 257); + + /* get 5 bits HDIST (1-32) */ + hdist = tinf_read_bits(d, 5, 1); + + /* get 4 bits HCLEN (4-19) */ + hclen = tinf_read_bits(d, 4, 4); + + for (i = 0; i < 19; ++i) lengths[i] = 0; + + /* read code lengths for code length alphabet */ + for (i = 0; i < hclen; ++i) + { + /* get 3 bits code length (0-7) */ + unsigned int clen = tinf_read_bits(d, 3, 0); + + lengths[clcidx[i]] = clen; + } + + /* build code length tree, temporarily use length tree */ + tinf_build_tree(lt, lengths, 19); + + /* decode code lengths for the dynamic trees */ + for (num = 0; num < hlit + hdist; ) + { + int sym = tinf_decode_symbol(d, lt); + + switch (sym) + { + case 16: + /* copy previous code length 3-6 times (read 2 bits) */ + { + unsigned char prev = lengths[num - 1]; + for (length = tinf_read_bits(d, 2, 3); length; --length) + { + lengths[num++] = prev; + } + } + break; + case 17: + /* repeat code length 0 for 3-10 times (read 3 bits) */ + for (length = tinf_read_bits(d, 3, 3); length; --length) + { + lengths[num++] = 0; + } + break; + case 18: + /* repeat code length 0 for 11-138 times (read 7 bits) */ + for (length = tinf_read_bits(d, 7, 11); length; --length) + { + lengths[num++] = 0; + } + break; + default: + /* values 0-15 represent the actual code lengths */ + lengths[num++] = sym; + break; + } + } + + /* build dynamic trees */ + tinf_build_tree(lt, lengths, hlit); + tinf_build_tree(dt, lengths + hlit, hdist); +} + +/* ----------------------------- * + * -- block inflate functions -- * + * ----------------------------- */ + +/* given a stream and two trees, inflate a block of data */ +static int tinf_inflate_block_data(TINF_DATA *d, TINF_TREE *lt, TINF_TREE *dt) +{ + if (d->curlen == 0) { + unsigned int offs; + int dist; + int sym = tinf_decode_symbol(d, lt); + //printf("huff sym: %02x\n", sym); + + /* literal byte */ + if (sym < 256) { + TINF_PUT(d, sym); + return TINF_OK; + } + + /* end of block */ + if (sym == 256) { + return TINF_DONE; + } + + /* substring from sliding dictionary */ + sym -= 257; + /* possibly get more bits from length code */ + d->curlen = tinf_read_bits(d, length_bits[sym], length_base[sym]); + + dist = tinf_decode_symbol(d, dt); + /* possibly get more bits from distance code */ + offs = tinf_read_bits(d, dist_bits[dist], dist_base[dist]); + d->lzOff = -offs; + } + + /* copy next byte from dict substring */ + d->dest[0] = d->dest[d->lzOff]; + d->dest++; + d->curlen--; + return TINF_OK; +} + +/* inflate an uncompressed block of data */ +static int tinf_inflate_uncompressed_block(TINF_DATA *d) +{ + if (d->curlen == 0) { + unsigned int length, invlength; + + /* get length */ + length = uzlib_get_byte(d) + 256 * uzlib_get_byte(d); + /* get one's complement of length */ + invlength = uzlib_get_byte(d) + 256 * uzlib_get_byte(d); + /* check length */ + if (length != (~invlength & 0x0000ffff)) return TINF_DATA_ERROR; + + /* increment length to properly return TINF_DONE below, without + producing data at the same time */ + d->curlen = length + 1; + + /* make sure we start next block on a byte boundary */ + d->bitcount = 0; + } + + if (--d->curlen == 0) { + return TINF_DONE; + } + + unsigned char c = uzlib_get_byte(d); + TINF_PUT(d, c); + return TINF_OK; +} + +/* ---------------------- * + * -- public functions -- * + * ---------------------- */ + +/* initialize global (static) data */ +void uzlib_init(void) +{ +#ifdef RUNTIME_BITS_TABLES + /* build extra bits and base tables */ + tinf_build_bits_base(length_bits, length_base, 4, 3); + tinf_build_bits_base(dist_bits, dist_base, 2, 1); + + /* fix a special case */ + length_bits[28] = 0; + length_base[28] = 258; +#endif +} + +/* inflate next byte of compressed stream */ +int uzlib_uncompress(TINF_DATA *d) +{ + do { + int res; + + /* start a new block */ + if (d->btype == -1) { +next_blk: + /* read final block flag */ + d->bfinal = tinf_getbit(d); + /* read block type (2 bits) */ + d->btype = tinf_read_bits(d, 2, 0); + + //printf("Started new block: type=%d final=%d\n", d->btype, d->bfinal); + + if (d->btype == 1) { + /* build fixed huffman trees */ + tinf_build_fixed_trees(&d->ltree, &d->dtree); + } else if (d->btype == 2) { + /* decode trees from stream */ + tinf_decode_trees(d, &d->ltree, &d->dtree); + } + } + + /* process current block */ + switch (d->btype) + { + case 0: + /* decompress uncompressed block */ + res = tinf_inflate_uncompressed_block(d); + break; + case 1: + case 2: + /* decompress block with fixed/dyanamic huffman trees */ + /* trees were decoded previously, so it's the same routine for both */ + res = tinf_inflate_block_data(d, &d->ltree, &d->dtree); + break; + default: + return TINF_DATA_ERROR; + } + + if (res == TINF_DONE && !d->bfinal) { + /* the block has ended (without producing more data), but we + can't return without data, so start procesing next block */ + goto next_blk; + } + + if (res != TINF_OK) { + return res; + } + + } while (--d->destSize); + + return TINF_OK; +}