Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/api/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
X86 从加载段切换到保护模式_X86_Nasm_Bootloader - Fatal编程技术网

X86 从加载段切换到保护模式

X86 从加载段切换到保护模式,x86,nasm,bootloader,X86,Nasm,Bootloader,我正在尝试编写简单的引导加载程序 我想以实模式加载boot0,跳转到boot0并从那里加载完整的内核。然后切换到保护模式并执行内核代码 到目前为止,我已经: ;BIOS加载的第一段: 第16位 组织0 jmp 0x07c0:启动 开始: mov-ax,cs mov-ds,ax 斧头 mov al,0x03 莫夫啊,0 int 0x10 斯莫夫,欢迎你 呼叫打印 mov-ax,0x500;将引导0加载到0x500 mov-es,ax;值应为es mov-cl,2;要加载的扇区号 mov al,4;

我正在尝试编写简单的引导加载程序

我想以实模式加载boot0,跳转到boot0并从那里加载完整的内核。然后切换到保护模式并执行内核代码

到目前为止,我已经:

;BIOS加载的第一段:
第16位
组织0
jmp 0x07c0:启动
开始:
mov-ax,cs
mov-ds,ax
斧头
mov al,0x03
莫夫啊,0
int 0x10
斯莫夫,欢迎你
呼叫打印
mov-ax,0x500;将引导0加载到0x500
mov-es,ax;值应为es
mov-cl,2;要加载的扇区号
mov al,4;要加载的扇区数
呼叫加载扇区
jmp 0x500:0000
装载扇区:
mov-bx,0
movdl,0;从软盘加载=0
mov dh,0
mov-ch,0
mov啊,2
int 0x13
jc错误
ret
乘以510-($-$$)db 0
dw 0xaa55
接下来的4段为boot0:

位16
组织0
mov-ax,cs
mov-ds,ax
斧头
mov-ax,0x7000
mov-ss,ax
mov sp,ss
;从教程打印
mov-ax,0xb800;加载gs以指向视频内存
mov-gs,ax;我们打算在实模式下显示棕色a
mov字[gs:80],0x0248;显示MOV字[gs:0],0x641;显示
mov字[gs:82],0x0145;显示MOV字[gs:0],0x641;显示
mov字[gs:84],0x034C;显示MOV字[gs:0],0x641;显示
mov字[gs:86],0x044C;显示MOV字[gs:0],0x641;显示
mov字[gs:88],0x054F;显示MOV字[gs:0],0x641;显示
;加载内核系统
mov-ax,0x2000
斧头
mov-cl,6;启动后,0将是完整的内核
mov al,4;目前只有4个部门
呼叫加载扇区;加载内核
jmp受保护的_模式_运行
装载扇区:
mov-bx,0
软盘
mov dh,0
mov-ch,0
mov啊,2
int 0x13
jc错误
ret
受保护的\u模式\u运行:
cli
lgdt[gdtr]
mov-eax,cr0;cr0的lsb是受保护模式位
或al,0x01;设置保护模式位
mov-cr0,eax;Mov修改后的字到控制寄存器
jmp codesel:下午好
第32位
下午五时
数据集
mov-ds,ax;将ds&es初始化为数据段
斧头
mov-ax,videosel;初始化gs至视频存储器
mov-gs,ax
mov字[gs:0],0x741;在保护模式下显示白色
自旋:jmp自旋;环
;TODO:在这里跳转到加载的代码
第16位
gdtr:
dw gdt_end-gdt-1;gdt的长度
dd0x500+gdt;gdt的物理地址
gdt:
nullsel eq$-gdt;$->当前位置,因此nullsel=0h
gdt0:;根据约定,空描述符gdt0为0
dd0;每个gdt条目是8个字节,因此在08h时是CS
dd0;总之,段描述符是64位的
codesel equ$-gdt;这是8h,即gdt中的第二个描述符
代码_gdt:;0000:0000h处的4Gb扁平段代码描述符
dw0x0ffff;限制段描述符的4Gb位0-15
dw 0x0000;段描述符(sd)的基本0h位16-31
db 0x00;32位地址的seg 16-23的基本地址,sd的32-39的基本地址
db 0x09a;P、 DPL(2),S,类型(3),A->当前位1,描述符
; 特权级别0-3,段描述符1 ie代码
db 0x0cf;上4位G、D、0、AVL->1段长度为第页
; 粒度,1默认操作大小为32位seg
; 段限制的下半字节位16-19
db 0x00;32位地址的seg 24-31的基本地址,sd的56-63的基本地址
数据集equ$-gdt;ie 10h,数据sd下一个8字节的开始
数据_gdt:;0000:0000h时的数据描述符4Gb扁平段
dw0x0ffff;限制4Gb
dw 0x0000;基准0000:0000h
db 0x00;描述符格式同上
数据库0x092
db 0x0cf
数据库0x00
videosel equ$-gdt;ie 18h,下一个gdt条目
德国马克3999;极限80*25*2-1
dw 0x8000;基本0xb8000
数据库0x0b
db 0x92;存在,环0,数据,向上展开,可写
db 0x00;字节粒度16位
数据库0x00
gdt_结束:
乘以2048-($-$$)db 0
当我尝试从BIOS加载的第一段开始进入保护模式时,进入保护模式工作正常。 从加载的段执行此操作的每次尝试都会在“jmp codesel:go_pm”行崩溃

文件的结构是: 1段-初始 4段-引导0(加载到0x500段) 4段-内核(加载到0x2000段)

我只在gdt中更改了“dd0x500+gdt;gdt的物理地址”,但看起来还不够。 你能告诉我我还应该改变什么或提供任何参考,以便我能阅读更多关于GDT和切换到保护模式的详细信息吗

谢谢

第一个问题是:

gdtr:
dw      gdt_end-gdt-1 ; Length of the gdt
dd      0x500+gdt ; physical address of gdt

gdt:
bits 16
org 0
...
jmp     codesel:go_pm

bits 32
go_pm:
...
code_gdt: ; Code descriptor 4Gb flat segment at 0000:0000h
dw      0x0ffff ; Limit 4Gb bits 0-15 of segment descriptor
dw      0x0000 ; Base 0h bits 16-31 of segment descriptor (sd)
db      0x00 ; Base addr of seg 16-23 of 32bit addr,32-39 of sd
db      0x09a ; P,DPL(2),S,TYPE(3),A->Present bit 1,Descriptor
; privilege level 0-3,Segment descriptor 1 ie code
db      0x0cf ; Upper 4 bits G,D,0,AVL ->1 segment len is page
; granular, 1 default operation size is 32bit seg
; Lower nibble bits 16-19 of segment limit
db      0x00 ; Base addr of seg 24-31 of 32bit addr,56-63 of sd
0x500是实模式段,但该段在物理内存中从何处开始?在0x5000,对吗?那么,为什么
0x500+gdt

第二个问题是:

gdtr:
dw      gdt_end-gdt-1 ; Length of the gdt
dd      0x500+gdt ; physical address of gdt

gdt:
bits 16
org 0
...
jmp     codesel:go_pm

bits 32
go_pm:
...
code_gdt: ; Code descriptor 4Gb flat segment at 0000:0000h
dw      0x0ffff ; Limit 4Gb bits 0-15 of segment descriptor
dw      0x0000 ; Base 0h bits 16-31 of segment descriptor (sd)
db      0x00 ; Base addr of seg 16-23 of 32bit addr,32-39 of sd
db      0x09a ; P,DPL(2),S,TYPE(3),A->Present bit 1,Descriptor
; privilege level 0-3,Segment descriptor 1 ie code
db      0x0cf ; Upper 4 bits G,D,0,AVL ->1 segment len is page
; granular, 1 default operation size is 32bit seg
; Lower nibble bits 16-19 of segment limit
db      0x00 ; Base addr of seg 24-31 of 32bit addr,56-63 of sd
您将32位代码段定义为从物理地址0开始,但32位代码的地址0对应于物理地址0x5000。为什么?因为您是通过
org0
请求的,并在0x500:0加载代码。数据段也有同样的问题[等待发生]

我注意到了一些可疑的事情:

mov ax, 0x7000
mov ss, ax
mov sp, ss
您确定要使用SS=SP=0x7000吗?我不能说这是错的(我没有做所有的数学计算),但是SS和SP不是一回事,用相同的值加载它们看起来很奇怪

英特尔/AMD的CPU手册中描述了所有必要的细节。你所要做的就是理解这些东西,并注意你在做什么来避免上面两个错误