1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| #include <asm.h>
# Start the 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.
.set PROT_MODE_CSEG, 0x8 # kernel code segment selector .set PROT_MODE_DSEG, 0x10 # kernel data segment selector .set CR0_PE_ON, 0x1 # protected mode enable flag
# start address should be 0:7c00, in real mode, the beginning address of the running bootloader .globl start start: .code16 # Assemble for 16-bit mode cli # Disable interrupts cld # String operations increment
# Set up the important data segment registers (DS, ES, SS). xorw %ax, %ax # Segment number zero movw %ax, %ds # -> Data Segment movw %ax, %es # -> Extra Segment movw %ax, %ss # -> Stack Segment
# Enable A20: # For backwards compatibility with the earliest PCs, physical # address line 20 is tied low, so that addresses higher than # 1MB wrap around to zero by default. This code undoes this. -------------------------------------------------------- A20 开启方法是 将 0x64 端口读入一个字节 到 al中 然后 testb 即做一个 and 运算 只不过不保存结果 判断一下 0x64端口的第二位是否为0 即 8042 键盘缓冲区是否为空 若不为空 则循环 直至为空 后 将 0xdl(写入数据到8042的p2端口) 写入到 0x64端口中
后面的也很类似 还是判断 0x64端口的第二位是不是为0 不是就循环 然后把 0xdf(11011111) 写入到 0x60 设置了 P2的 A20位 即第一位为 1 开启 A20地址线 -------------------------------------------------------- seta20.1: inb $0x64, %al # Wait for not busy(8042 input buffer empty). testb $0x2, %al jnz seta20.1
movb $0xd1, %al # 0xd1 -> port 0x64 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port
seta20.2: inb $0x64, %al # Wait for not busy(8042 input buffer empty). testb $0x2, %al jnz seta20.2
movb $0xdf, %al # 0xdf -> port 0x60 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1
# Switch from real to protected mode, using a bootstrap GDT # and segment translation that makes virtual addresses # identical to physical addresses, so that the # effective memory map does not change during the switch. -------------------------------------------------------- 加载 GDT 全局描述符表 打开 保护模式 需要将 cr0 控制寄存器的 第0位 PE位 置1 -------------------------------------------------------- lgdt gdtdesc movl %cr0, %eax orl $CR0_PE_ON, %eax movl %eax, %cr0
# Jump to next instruction, but in 32-bit code segment. # Switches processor into 32-bit mode. -------------------------------------------------------- 刷新流水线 进入 32位模式 将 PROT_MODE_CSEG = 0x8 此时 指向的是 GDT 中的第一个段描述符 加载到 CS 后 protcseg 加载到 IP -------------------------------------------------------- ljmp $PROT_MODE_CSEG, $protcseg
.code32 # Assemble for 32-bit mode protcseg: # Set up the protected-mode data segment registers movw $PROT_MODE_DSEG, %ax # Our data segment selector movw %ax, %ds # -> DS: Data Segment movw %ax, %es # -> ES: Extra Segment movw %ax, %fs # -> FS movw %ax, %gs # -> GS movw %ax, %ss # -> SS: Stack Segment
# Set up the stack pointer and call into C. The stack region is from 0--start(0x7c00) movl $0x0, %ebp movl $start, %esp call bootmain
# If bootmain returns (it shouldn't), loop. spin: jmp spin -------------------------------------------------------- GDT 构建一个代码段描述符 和 一个数据段描述符 使用的平坦模型 -------------------------------------------------------- # Bootstrap GDT .p2align 2 # force 4 byte alignment gdt: 第0个描述符项不可用 SEG_NULLASM # null seg SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel
gdtdesc: .word 0x17 # sizeof(gdt) - 1 .long gdt # address gdt
|