From: David Devecsery Date: Mon, 18 May 2020 14:50:56 +0000 (-0400) Subject: Forgot to add bootblock... X-Git-Url: https://git.devinivas.org/?a=commitdiff_plain;h=59d76ffefafef85c51155f19675b3829206d0535;p=cs3210-lab0.git Forgot to add bootblock... --- diff --git a/bootblock/CMakeLists.txt b/bootblock/CMakeLists.txt new file mode 100644 index 0000000..5e0f66c --- /dev/null +++ b/bootblock/CMakeLists.txt @@ -0,0 +1,49 @@ +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 $) + +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) + + diff --git a/bootblock/bootasm.S b/bootblock/bootasm.S new file mode 100644 index 0000000..120296f --- /dev/null +++ b/bootblock/bootasm.S @@ -0,0 +1,88 @@ +#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 + diff --git a/bootblock/bootmain.c b/bootblock/bootmain.c new file mode 100644 index 0000000..737fb15 --- /dev/null +++ b/bootblock/bootmain.c @@ -0,0 +1,96 @@ +// 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); +} diff --git a/bootblock/tools/sign.pl b/bootblock/tools/sign.pl new file mode 100755 index 0000000..d793035 --- /dev/null +++ b/bootblock/tools/sign.pl @@ -0,0 +1,19 @@ +#!/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;