# Lecture 2 Notes Prep questions: http://cs3210.cc.gatech.edu/q.html?q=boot&c=lec&n=2 ## Minboot - BIOS and bootloader work together to boot the system. - Begin at reset vector 0xffffff0. The boot loader (BIOS) must contain a jump into the BIOS here. ### Bootstrapping demo ~~~ $ cd lab $ make qemu-gdb ~~~ - The Reset vector is at 0xfffffff0 in x86 and contains an instruction that jumps to the start of the BIOS at 0xfe05b. ~~~ The target architecture is assumed to be i8086 [f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b 0x0000fff0 in ?? () + symbol-file obj/kern/kernel (gdb) b bootstack bootstack bootstacktop (gdb) x/li 0xfffffff0 0xfffffff0: ljmp $0xf000,$0xe05b (gdb) info reg eax 0x0 0 ecx 0x0 0 edx 0x663 1635 ebx 0x0 0 esp 0x0 0x0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0xfff0 0xfff0 eflags 0x2 [ ] cs 0xf000 61440 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 (gdb) ~~~ - Talk about registers in IA32 from slides. - Jumping to the BIOS ~~~ (gdb) si [f000:e05b] 0xfe05b: cmpl $0x0,%cs:0x6574 0x0000e05b in ?? () (gdb) info reg eax 0x0 0 ecx 0x0 0 edx 0x663 1635 ebx 0x0 0 esp 0x0 0x0 ebp 0x0 0x0 esi 0x0 0 edi 0x0 0 eip 0xe05b 0xe05b eflags 0x2 [ ] cs 0xf000 61440 ss 0x0 0 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0 ~~~ - At this point you should see the start of bootmain. ## SeaBIOS - SeaBIOS arranges for romlayout.S:reset_vector() to be present at the reset_vector and starts in 16 bit real mode. Line 641 of romlayout.S. ~~~ reset_vector: ljmpw $SEG_BIOS, $entry_post ~~~ - The reset vector jumps to the beginning of Power-On Self-Test (POST). Line 547 of romlayout.S. ~~~ entry_post: cmpl $0, %cs:HaveRunPost // Check for resume/reboot jnz entry_resume ENTRY_INTO32 _cfunc32flat_handle_post // Normal entry point ORG 0xe2c3 .global entry_02 ~~~ - Entry post assembly code calls handle_post in post.c in 32bit flat mode. - Show minboot mode selection. - ENTRY_INTO32 is a macro in entryfuncs.S:153 that resets the stack and transitions to 32-bit mode and calls a c function. - The transition code is "transition32" (line 24 romlayout.S) which does the transition to 32 bit. - These transitions will happen again in the boot loader. - In handle_post, dopost is called which carries out the POST initialization. This ends with startBoot which calls int 0x19 into 16 bit real mode. - After POST, the boot phase occurs. This loads the OS's boot loader by calling an interrupt (0x18 or 0x19). (The interrupt is a farcall using the interrupt vector table set in post.c:ivt_init() - This takes us to entry_19, a 16 bit wrapper for a switch to 32 bit flat mode to handle interrupt 19. This calls doboot which selects the appropriate media to boot using the sequence number (just starting at 0) - Eventually, the disk is booted using boot_disk, boot.c:612. Interrupt 0x13 is called which leads to the real mode handle_13. This leads to a number of disk handlers that allow the bios to read and control the disk. All we are doing here is reading the MBR using disk.c:disk_1302 - The MBR gets loaded to 0x7c00 (Beginning of segment 0x07c0). - Check if the MBR is an MBR. (boot.c:634) - We then call the boot entry in the MBR which takes us to the OS's boot loader. (boot.c:646) ### Stepping through SeaBIOS ~~~ qemu-system-i386 -serial mon:stdio -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 -m 512 -S -gdb tcp::26000 -bios ../seabios/out/bios.bin ~~~ - In another terminal run ~~~ $ gdb ../seabios/src/out/rom.o ... (gdb) br boot.c:646 Breakpoint 1 at 0xf1672: file ./src/boot.c, line 646. (gdb) c Continuing. ~~~ For 16-bit: When debugging 16bit code it is necessary to load the 16bit symbols twice in order for gdb to properly handle break points. To do this, run the following command objcopy --adjust-vma 0xf0000 out/rom16.o rom16offset.o and then run the following in gdb: set architecture i8086 add-symbol-file rom16offset.o 0 - The BIOS jumps back and forth between 32-bit mode and 16-bit mode so it's a little hard to debug. ### Stepping through xv6 ~~~ $ cd xv6 $ make qemu-gdb ~~~ ### Stepping through the bootloader for xv6. - In another terminal in the same directory, ~~~ $ gdb (gdb) symbol-file bootblock.o (gdb) br bootmain (gdb) c (gdb) layout src ~~~ To get back to kernel do, ~~~ $ gdb (gdb) symbol-file kernel (gdb) br main (gdb) c ~~~ ### QEMU monitor - Run make qemu-gdb - Type Ctrl-a + c to get to monitor. ~~~ (qemu) help (qemu) info registers ~~~ ### Info Registers info registers Display a full dump of the machine's internal register state. In particular, this includes the machine's hidden segment state for the segment selectors and the local, global, and interrupt descriptor tables, plus the task register. This hidden state is the information the virtual CPU read from the GDT/LDT when the segment selector was loaded. Here's the CS when running in the JOS kernel in lab 1 and the meaning of each field: CS =0008 10000000 ffffffff 10cf9a00 DPL=0 CS32 [-R-] CS =0008 The visible part of the code selector. We're using segment 0x8. This also tells us we're referring to the global descriptor table (0x8&4=0), and our CPL (current privilege level) is 0x8&3=0. 10000000 The base of this segment. Linear address = logical address + 0x10000000. ffffffff The limit of this segment. Linear addresses above 0xffffffff will result in segment violation exceptions. 10cf9a00 The raw flags of this segment, which QEMU helpfully decodes for us in the next few fields. DPL=0 The privilege level of this segment. Only code running with privilege level 0 can load this segment. CS32 This is a 32-bit code segment. Other values include DS for data segments (not to be confused with the DS register), and LDT for local descriptor tables. [-R-] This segment is read-only. ### A20 - After disabling interrupts (see discussion of interrupts in SeaBIOS documentation), enable A20 line with keyboard controller method in bootasm.S:88. - The A20 line just signals to the controller port a write is coming (0xD1 to 0x64). Then it sends 0xDF to 0x60 data buffer which enables A20. - Every action on the keyboard controller (or any I/O device) needs a wait if busy loop. ### Switch from real to protected - Set up a bootstrap global descriptor table. So when we transition to 32-bit mode, the memory addresses don't change. (Flat memory mode. No paging enabled same as in BIOS.) - Note that the data and code segments are the same. They just have different flags. Segments almost always used for permissions not actual isolation. - Starting in protected mode. Set up our segment addresses to the kernel segment. - Discuss segmentation and Global Descriptor Table here off of slides. (https://tc.gtisc.gatech.edu/cs3210/2017/spring/l/lec02.pdf) - Call bootmain here to jump into C code in bootmain.c. (Note on debugging 32-bit vs. 16-bit code.) - Boot main loads the kernel image into memory and calls the kernel entry point.