xref: /xv6-public/bootasm.S (revision e97519a6)
121a88fd4Skaashoek#include "asm.h"
255e95b16Srtm
35d1f4b8aSrsc# Start the first CPU: switch to 32-bit protected mode, jump into C.
45d1f4b8aSrsc# The BIOS loads this code from the first sector of the hard disk into
55d1f4b8aSrsc# memory at physical address 0x7c00 and starts executing in real mode
65d1f4b8aSrsc# with %cs=0 %ip=7c00.
755e95b16Srtm
8*e97519a6Srsc#define SEG_KCODE 1  // kernel code
9*e97519a6Srsc#define SEG_KDATA 2  // kernel data+stack
10*e97519a6Srsc#define SEG_KCPU  3  // kernel per-cpu data
11*e97519a6Srsc
12*e97519a6Srsc#define CR0_PE    1  // protected mode enable bit
1355e95b16Srtm
14b7f653dcSrsc.code16                       # Assemble for 16-bit mode
152bc72bddSrsc.globl start
16a650c606Srscstart:
1755e95b16Srtm  cli                         # Disable interrupts
1855e95b16Srtm
1955e95b16Srtm  # Set up the important data segment registers (DS, ES, SS).
2055e95b16Srtm  xorw    %ax,%ax             # Segment number zero
2155e95b16Srtm  movw    %ax,%ds             # -> Data Segment
2255e95b16Srtm  movw    %ax,%es             # -> Extra Segment
2355e95b16Srtm  movw    %ax,%ss             # -> Stack Segment
2455e95b16Srtm
250cfc7290Srsc  # Enable A20:
2615868c4bSrsc  #   For backwards compatibility with the earliest PCs, physical
2715868c4bSrsc  #   address line 20 is tied low, so that addresses higher than
2815868c4bSrsc  #   1MB wrap around to zero by default.  This code undoes this.
29a650c606Srscseta20.1:
305d1f4b8aSrsc  inb     $0x64,%al               # Wait for not busy
315d1f4b8aSrsc  testb   $0x2,%al
325d1f4b8aSrsc  jnz     seta20.1
335d1f4b8aSrsc
345d1f4b8aSrsc  movb    $0xd1,%al               # 0xd1 -> port 0x64
355d1f4b8aSrsc  outb    %al,$0x64
36a650c606Srsc
37a650c606Srscseta20.2:
385d1f4b8aSrsc  inb     $0x64,%al               # Wait for not busy
395d1f4b8aSrsc  testb   $0x2,%al
405d1f4b8aSrsc  jnz     seta20.2
415d1f4b8aSrsc
425d1f4b8aSrsc  movb    $0xdf,%al               # 0xdf -> port 0x60
435d1f4b8aSrsc  outb    %al,$0x60
4455e95b16Srtm
452bc72bddSrsc//PAGEBREAK!
462bc72bddSrsc  # Switch from real to protected mode, using a bootstrap GDT
472bc72bddSrsc  # and segment translation that makes virtual addresses
48411ee741Srtm  # identical to physical addresses, so that the
492bc72bddSrsc  # effective memory map does not change during the switch.
502bc72bddSrsc  lgdt    gdtdesc
512bc72bddSrsc  movl    %cr0, %eax
52b7f653dcSrsc  orl     $CR0_PE, %eax
532bc72bddSrsc  movl    %eax, %cr0
542bc72bddSrsc
552bc72bddSrsc  # Jump to next instruction, but in 32-bit code segment.
562bc72bddSrsc  # Switches processor into 32-bit mode.
57*e97519a6Srsc  ljmp    $(SEG_KCODE<<3), $start32
5855e95b16Srtm
592bc72bddSrsc.code32                       # Assemble for 32-bit mode
60b7f653dcSrscstart32:
6155e95b16Srtm  # Set up the protected-mode data segment registers
62*e97519a6Srsc  movw    $(SEG_KDATA<<3), %ax    # Our data segment selector
6355e95b16Srtm  movw    %ax, %ds                # -> DS: Data Segment
6455e95b16Srtm  movw    %ax, %es                # -> ES: Extra Segment
65*e97519a6Srsc  movw    %ax, %ss                # -> SS: Stack Segment
66*e97519a6Srsc  movw    $(SEG_KCPU<<3), %ax     # Our per-cpu segment selector
6755e95b16Srtm  movw    %ax, %fs                # -> FS
6855e95b16Srtm  movw    %ax, %gs                # -> GS
695d1f4b8aSrsc
702bc72bddSrsc  # Set up the stack pointer and call into C.
715d1f4b8aSrsc  movl    $start, %esp
72c35c064eSrsc  call    bootmain
735d1f4b8aSrsc
74b7f653dcSrsc  # If bootmain returns (it shouldn't), trigger a Bochs
75b7f653dcSrsc  # breakpoint if running under Bochs, then loop.
76b7f653dcSrsc  movw    $0x8a00, %ax            # 0x8a00 -> port 0x8a00
77b7f653dcSrsc  movw    %ax, %dx
78b7f653dcSrsc  outw    %ax, %dx
79b7f653dcSrsc  movw    $0x8e00, %ax            # 0x8e00 -> port 0x8a00
80b7f653dcSrsc  outw    %ax, %dx
81a650c606Srscspin:
822bc72bddSrsc  jmp     spin
83a650c606Srsc
845d1f4b8aSrsc# Bootstrap GDT
8555e95b16Srtm.p2align 2                                # force 4 byte alignment
8655e95b16Srtmgdt:
877abf49d2Skaashoek  SEG_NULLASM                             # null seg
887abf49d2Skaashoek  SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)   # code seg
897abf49d2Skaashoek  SEG_ASM(STA_W, 0x0, 0xffffffff)         # data seg
90*e97519a6Srsc  SEG_ASM(STA_W, 0x100, 0xffffffff)       # per-cpu data seg; 0x100 is okay for now
915d1f4b8aSrsc
9255e95b16Srtmgdtdesc:
93*e97519a6Srsc  .word   0x1f                            # sizeof(gdt) - 1
9455e95b16Srtm  .long   gdt                             # address gdt
95