将实模式的寻呼设置为64位长模式切换

时间:2016-12-11 10:35:39

标签: x86 x86-64 paging bootloader osdev

我正在尝试使用 NASM 编写引导加载程序,因此,我发现OSDEV非常有用。但是,在设置分页,加载GDT或转换(直接来自实模式)的过程中的某个地方,会出现导致计算机重新启动的错误。我已将代码基于Long mode OSDEV article。这就是我对这个问题非常重要的事情:

GDT

gdt_start:

.gdt_null: equ $-gdt_start      ; mandatory null descriptor
  dw 0
  dw 0
  db 0                         ; define double word
  db 0
  db 0
  db 0

.gdt_code: equ $-gdt_start      ; code segment
                               ; base = 0x0, limif = 0xffff
                               ; 1st flags: (present)1 (privilege)00 (descriptor type)1 -> 1001
                               ; type flags: (code)1 (conforming)0 (readable)1 (accessed)0 -> 1010
                               ; 2nd flags: (granularity)1 (32 bit default)1 (64 bit seg)0 (AVL)0 -> 1100
  dw 0                         ; limit (0-15)
  dw 0                         ; base (0-15)
  db 0                         ; base (16-23)
  db 10011010b                 ; 1st/type flags
  db 00100000b                 ; 2nd flags, limit (16-19)
  db 0                         ; base (bits 24-31)

.gdt_data: equ $-gdt_start      ; data segment descriptor
                ; type flags (code)0 (expand down)0 (writable)1 (accessed)0 -> 0010
  dw 0                         ; limit
  dw 0                         ; base
  db 0                         ; base
  db 10010010b                 ; 1st/type flags
  db 00000000b                 ; 2nd flags, limit
  db 0                         ; base

gdt_end:

gdt_descriptor:
  dw gdt_end - gdt_start - 1   ; size of GDT

  dq gdt_start

CODE_SEG equ gdt_start.gdt_code
DATA_SEG equ gdt_start.gdt_data

分页和切换:

%include "gdt.ns"

;test/enable A20 line
call test_a20
fin:

cmp ax, 1
je enabled

call enable_A20

enabled:

;switch
call switch_to_lm
jmp $

switch_to_lm:
;
; SET UP PAGING!!!!!
;

;no previous paging defined so the below code is unnecessary
;mov eax, cr0
;and eax, 01111111111111111111111111111111b
;mov cr0, eax

;clear tables
mov edi, 0x1000
mov cr3, edi
xor eax, eax
mov ecx, 4096
rep stosd
mov edi, cr3

;set up new tables
mov DWORD [edi], 0x2003
add edi, 0x1000
mov DWORD [edi], 0x3003
add edi, 0x1000
mov DWORD [edi], 0x4003
add edi, 0x1000

mov ebx, 0x00000003
mov ecx, 512

.setEntry:
  mov DWORD [edi], ebx
  add ebx, 0x1000
  add edi, 8
  loop .setEntry

;enable PAE bit in CR4
mov eax, cr4
or eax, 1<<5
mov cr4, eax

;switch from REAL MODE
;set long mode bit
mov ecx, 0xc0000080
rdmsr
or eax, 1<<8
wrmsr

;enable paging
mov eax, cr0
or eax, 1<<31
mov cr0, eax

lgdt [gdt_descriptor]
jmp CODE_SEG:init_lm

[bits 64]

init_lm:

  cli
  mov ax, DATA_SEG
  mov ds, ax
  mov es, ax
  mov fs, ax
  mov gs, ax
  mov ss, ax

  mov ebp, 0x90000
  mov esp, ebp

  call BEGIN_LM

我测试A20的代码:

test_a20:
  pushf
  push ds
  push es
  push di
  push si

  cli

  xor ax, ax
  mov es, ax

  not ax
  mov ds, ax

  mov di, 0x0500
  mov si, 0x0510

  mov al, byte [es:di]
  push ax

  mov byte [es:di], 0x00
  mov byte [ds:si], 0xff

  cmp byte [es:di], 0xff

  pop ax
  mov byte [ds:si], al

  pop ax
  mov byte [es:di], al

  mov ax, 0
  je test_exit

  mov ax, 1

test_exit:
  pop si
  pop di
  pop es
  pop ds
  popf

  jmp fin

1 个答案:

答案 0 :(得分:1)

在您的代码中,您的test_a20功能存在问题。特别是你有这个代码:

mov al, byte [es:di]
push ax

mov byte [es:di], 0x00
mov byte [ds:si], 0xff

cmp byte [es:di], 0xff

pop ax
mov byte [ds:si], al

pop ax
mov byte [es:di], al

您似乎在堆栈上推送 AX 的一个值,之后弹出2。这会弄乱堆栈,寄存器将被错误地恢复,包括 DS 和标志。看来你可能一直试图通过不使用ret来回避这个错误。相反,您使用jmp fin跳转到call test_a20指令后的某个点。

您似乎正在尝试使用A20 test code from OSDEV Wiki。您会注意到您缺少这些内容:

mov al, byte [ds:si]
push ax

如果您修改test_a20功能以添加缺失的行并使用ret,则应该如下所示:

test_a20:
  pushf
  push ds
  push es
  push di
  push si

  cli

  xor ax, ax
  mov es, ax

  not ax
  mov ds, ax

  mov di, 0x0500
  mov si, 0x0510

  mov al, byte [es:di]
  push ax

  mov al, byte [ds:si]
  push ax

  mov byte [es:di], 0x00
  mov byte [ds:si], 0xff

  cmp byte [es:di], 0xff

  pop ax
  mov byte [ds:si], al

  pop ax
  mov byte [es:di], al

  mov ax, 0
  je test_exit

  mov ax, 1

test_exit:
  pop si
  pop di
  pop es
  pop ds
  popf
  ret

此更改应修复 DS 寄存器被销毁的问题,并且在页面编写代码中的后续内存访问不能正常工作。您还必须修改页面启用代码以启用保护模式。这段代码:

;enable paging
mov eax, cr0
or eax, 1<<31
mov cr0, eax

应该是:

;enable paging
mov eax, cr0
or eax, (1<<31) | (1<<0)
mov cr0, eax

通过这些更改,您应该可以进入64位长模式。