--- /dev/null
+project(bootblock ASM)
+
+include_directories(include)
+
+set(bootblock_SOURCES
+ # NOTE: bootasm.S must go first in this list, to ensure its placed at 0x7c00
+ # in the bootloader dump
+ bootasm.S
+ bootmain.c)
+
+add_library(bootblockobjs OBJECT
+ ${bootblock_SOURCES})
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -fno-builtin -fno-pic -nostdinc -m32")
+set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS}")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -m elf_i386")
+
+# Always force -Os for bootblock
+set(CMAKE_C_FLAGS_RELEASE "-Os")
+set(CMAKE_C_FLAGS_DEBUG "-Os -ggdb3")
+
+add_prefix_suffix(bootblock_OBJECTS
+ "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/bootblockobjs.dir/"
+ ".o"
+ ${bootblock_SOURCES})
+
+add_custom_command(
+ OUTPUT bootblock.obj
+ COMMAND ${CMAKE_LINKER} -m elf_i386 -N -e start -Ttext 0x7C00 -o ${CMAKE_CURRENT_BINARY_DIR}/bootblock.obj ${bootblock_OBJECTS}
+ DEPENDS $<TARGET_OBJECTS:bootblockobjs>)
+
+add_custom_command(
+ OUTPUT bootblock
+ COMMAND objcopy -S -O binary -j .text bootblock.obj bootblock
+ COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/tools/sign.pl bootblock
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bootblock.obj)
+
+add_custom_command(
+ OUTPUT bootblock.asm
+ COMMAND objdump -S bootblock.obj > bootblock.asm
+ DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/bootblock.obj)
+
+add_custom_target(
+ buildboot ALL
+ DEPENDS bootblockobjs bootblock.asm bootblock)
+
+set_property(TARGET bootblockobjs PROPERTY C_STANDARD 11)
+
+
--- /dev/null
+#include "asm/asm.h"
+#include "memlayout.h"
+#include "mmu.h"
+
+# Start the first CPU: switch to 32-bit protected mode, jump into C.
+# The BIOS loads this code from the first sector of the hard disk into
+# memory at physical address 0x7c00 and starts executing in real mode
+# with %cs=0 %ip=7c00.
+
+.code16 # Assemble for 16-bit mode
+.globl start
+start:
+ cli # BIOS enabled interrupts; disable
+
+ # Zero data segment registers DS, ES, and SS.
+ xorw %ax,%ax # Set %ax to zero
+ movw %ax,%ds # -> Data Segment
+ movw %ax,%es # -> Extra Segment
+ movw %ax,%ss # -> Stack Segment
+
+ # Physical address line A20 is tied to zero so that the first PCs
+ # with 2 MB would run software that assumed 1 MB. Undo that.
+seta20.1:
+ inb $0x64,%al # Wait for not busy
+ testb $0x2,%al
+ jnz seta20.1
+
+ movb $0xd1,%al # 0xd1 -> port 0x64
+ outb %al,$0x64
+
+seta20.2:
+ inb $0x64,%al # Wait for not busy
+ testb $0x2,%al
+ jnz seta20.2
+
+ movb $0xdf,%al # 0xdf -> port 0x60
+ outb %al,$0x60
+
+ # Switch from real to protected mode. Use a bootstrap GDT that makes
+ # virtual addresses map directly to physical addresses so that the
+ # effective memory map doesn't change during the transition.
+ lgdt gdtdesc
+ movl %cr0, %eax
+ orl $CR0_PE, %eax
+ movl %eax, %cr0
+
+//PAGEBREAK!
+ # Complete the transition to 32-bit protected mode by using a long jmp
+ # to reload %cs and %eip. The segment descriptors are set up with no
+ # translation, so that the mapping is still the identity mapping.
+ ljmp $(SEG_KCODE<<3), $start32
+
+.code32 # Tell assembler to generate 32-bit code now.
+start32:
+ # Set up the protected-mode data segment registers
+ movw $(SEG_KDATA<<3), %ax # Our data segment selector
+ movw %ax, %ds # -> DS: Data Segment
+ movw %ax, %es # -> ES: Extra Segment
+ movw %ax, %ss # -> SS: Stack Segment
+ movw $0, %ax # Zero segments not ready for use
+ movw %ax, %fs # -> FS
+ movw %ax, %gs # -> GS
+
+ # Set up the stack pointer and call into C.
+ movl $start, %esp
+ call bootmain
+
+ # If bootmain returns (it shouldn't), trigger a Bochs
+ # breakpoint if running under Bochs, then loop.
+ movw $0x8a00, %ax # 0x8a00 -> port 0x8a00
+ movw %ax, %dx
+ outw %ax, %dx
+ movw $0x8ae0, %ax # 0x8ae0 -> port 0x8a00
+ outw %ax, %dx
+spin:
+ jmp spin
+
+# Bootstrap GDT
+.p2align 2 # force 4 byte alignment
+gdt:
+ SEG_NULLASM # null seg
+ SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg
+ SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg
+
+gdtdesc:
+ .word (gdtdesc - gdt - 1) # sizeof(gdt) - 1
+ .long gdt # address gdt
+
--- /dev/null
+// Boot loader.
+//
+// Part of the boot block, along with bootasm.S, which calls bootmain().
+// bootasm.S has put the processor into protected 32-bit mode.
+// bootmain() loads an ELF kernel image from the disk starting at
+// sector 1 and then jumps to the kernel entry routine.
+
+#include "asm/x86.h"
+#include "elf.h"
+#include "memlayout.h"
+#include "types.h"
+
+#define SECTSIZE 512
+
+void readseg(uchar*, uint, uint);
+
+void
+bootmain(void)
+{
+ struct elfhdr *elf;
+ struct proghdr *ph, *eph;
+ void (*entry)(void);
+ uchar* pa;
+
+ elf = (struct elfhdr*)0x10000; // scratch space
+
+ // Read 1st page off disk
+ readseg((uchar*)elf, 4096, 0);
+
+ // Is this an ELF executable?
+ if(elf->magic != ELF_MAGIC)
+ return; // let bootasm.S handle error
+
+ // Load each program segment (ignores ph flags).
+ ph = (struct proghdr*)((uchar*)elf + elf->phoff);
+ eph = ph + elf->phnum;
+ for(; ph < eph; ph++){
+ pa = (uchar*)ph->paddr;
+ readseg(pa, ph->filesz, ph->off);
+ if(ph->memsz > ph->filesz)
+ stosb(pa + ph->filesz, 0, ph->memsz - ph->filesz);
+ }
+
+ // Call the entry point from the ELF header.
+ // Does not return!
+ entry = (void(*)(void))(elf->entry);
+ entry();
+}
+
+void
+waitdisk(void)
+{
+ // Wait for disk ready.
+ while((inb(0x1F7) & 0xC0) != 0x40)
+ ;
+}
+
+// Read a single sector at offset into dst.
+void
+readsect(void *dst, uint offset)
+{
+ // Issue command.
+ waitdisk();
+ outb(0x1F2, 1); // count = 1
+ outb(0x1F3, offset);
+ outb(0x1F4, offset >> 8);
+ outb(0x1F5, offset >> 16);
+ outb(0x1F6, (offset >> 24) | 0xE0);
+ outb(0x1F7, 0x20); // cmd 0x20 - read sectors
+
+ // Read data.
+ waitdisk();
+ insl(0x1F0, dst, SECTSIZE/4);
+}
+
+// Read 'count' bytes at 'offset' from kernel into physical address 'pa'.
+// Might copy more than asked.
+void
+readseg(uchar* pa, uint count, uint offset)
+{
+ uchar* epa;
+
+ epa = pa + count;
+
+ // Round down to sector boundary.
+ pa -= offset % SECTSIZE;
+
+ // Translate from bytes to sectors; kernel starts at sector 1.
+ offset = (offset / SECTSIZE) + 1;
+
+ // If this is too slow, we could read lots of sectors at a time.
+ // We'd write more to memory than asked, but it doesn't matter --
+ // we load in increasing order.
+ for(; pa < epa; pa += SECTSIZE, offset++)
+ readsect(pa, offset);
+}
--- /dev/null
+#!/usr/bin/perl
+
+open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
+
+$n = sysread(SIG, $buf, 1000);
+
+if($n > 510){
+ print STDERR "boot block too large: $n bytes (max 510)\n";
+ exit 1;
+}
+
+print STDERR "boot block is $n bytes (max 510)\n";
+
+$buf .= "\0" x (510-$n);
+$buf .= "\x55\xAA";
+
+open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
+print SIG $buf;
+close SIG;