【5.5节】切换堆栈和GDT分析
来源:互联网 发布:程序化交易软件试用 编辑:程序博客网 时间:2024/04/29 20:49
结束了boot和loader,总算进入到了kernel,由于在loader中还留着栈空间和GDT,我们想把它们移到内核,以便于管理和保护。
值得高兴的是,我们可以用C了,可以减轻一些被汇编的晦涩的折磨。
;kernel.asm
extern Gdt_Ptr
extern Copy_GDT
Selector_Flat_C equ 16
Selector_Flat_RW equ 8
Selector_Video equ 40
[section .bss]
Stack_Space: rebs 2 * 1024
Top_Of_Stack
[section .text]
global _start
_start:
mov ax,Selector_Flat_RW
mov ds,ax
mov es,ax
mov ss,ax
mov esp,Top_Of_Stack
sgdt [Gdt_Ptr]
call Copy_GDT
lgdt [Gdt_Ptr]
jmp Selector_Flat_C:_test
_test:
mov ax,Selector_Video
mov gs,ax
mov ah,0ch
mov al,99
mov [gs:(80 * 15) * 2)],ax
hlt
在这里我跟书中的作者不一样,在书中作者在LOADER中没有特别在GDT中安排堆栈段和数据段,原来的GDT请参看loader.asm,直接用Selector_Flat_RW寻址,而在我自己的实现中加入了这两个段,所以在程序的入口必须把ds,es,ss都设置为Selector_Flat_RW,这样就可以在LINUX下编译链接好的ELF格式的汇编和C正确的寻址。在链接的时候命令行中加了这么一个参数:-Ttext 0x30400,简单的类比就是相当于NASM的ORG 30400H.
接下来划出一个SECTION,大小为2K,用来当作栈空间,把栈顶送ESP,这样就完成一个任务。
再下来用SGDT指令保存原来的GDTR中的值,保存在Gdt_Ptr,这是在哪个地儿呢,现在还不知道,但用EXTERN命令说明我们保证在链接的时候会找到Gdt_Ptr这个地儿来存放。
接着就是调用一个函数把在LOADER中的GDT拷贝到内核空间里,这个函数在本KERNEL.ASM中没有写,因为我们把这个函数用C语言写了,同样用EXTERN声明一下,让我们跟随着程序来到盼望已久的C文件:cstart.c
cstart.c
- typedef unsigned int u32;
- typedef unsigned short u16;
- typedef unsigned char u8;
- /* 存储段描述符/系统段描述符 */
- typedef struct s_descriptor /* 共 8 个字节 */
- {
- u16 limit_low; /* Limit */
- u16 base_low; /* Base */
- u8 base_mid; /* Base */
- u8 attr1; /* P(1) DPL(2) DT(1) TYPE(4) */
- u8 limit_high_attr2; /* G(1) D(1) 0(1) AVL(1) LimitHigh(4) */
- u8 base_high; /* Base */
- }DESCRIPTOR;
- void Memory_Copy(void *src,void *des,int len);
- DESCRIPTOR GDT[1024];
- char Gdt_Ptr[6];
- void Copy_GDT()
- {
- Memory_Copy(*(u32*)(&Gdt_Ptr[2]),&GDT,*(u16*)(&Gdt_Ptr[0]) + 1);
- *(u16*)(&Gdt_Ptr[0]) = 1024 * sizeof(DESCRIPTOR);
- *(u32*)(&Gdt_Ptr[2]) = (u32)(&GDT);
- }
首先typedef 3个东东,为了一眼扫过去就能看清一个数是几位的。然后定义一个8个字节的DESCRIPTOR结构,这个也无需赘言。然后是Memory_Copy函数的声明,此函数用汇编写成,等会再说。接下来定义1个DESCRIPTOR的结构数组,就是在内核中放置GDT的空间。最后就是在kernel.asm中用到的Gdt_Ptr,是个6字节的空间。
最后在kernel.asm中调用的Copy_Gdt函数了,首先调用Memory_Copy,参数1是源指针,即使原先GDTR寄存器中指示的GDT的首地址,此地址存放在Gdt_Ptr[2]的连续4个字节中,就需要首先把&Gdt_Ptr强制转换为一个32位的指针,再解引用;参数2是目的指针,直接把GDT的地址传进去;最后是需要拷贝的GDT的字节数,此数据存放在Gdt_Ptr[0]的连续2个字节,也需要强制转换,成为16位的指针。
拷贝完成后,把新的,在内核中的GDT的地址,和GDT的字节数填充到Gdt_Ptr,使之成为新的将要填充到GDTR中的值,代码也清楚,也无需赘言。
执行完毕后回到kernel.asm,用lgdt指令来加载新的数据到GDTR中,此时切换GDT的任务也完成了。我们就要测试一下切换是否成功了,用一个jmp来跳转到_test处,可即使成功了还不会有任何反馈。我们在向显存中写入一个c字母,GDT中表示显存的段在GDT中的偏移为40,把它送入gs中,这样如果成功,就会有反馈了。
附上memory_copy的代码:
global Memory_Copy
;Memory_Copy----------------------------------------------------------
;C函数 Mem_Copy(void *esi,void *edi,len ecx)
;ds:esi -> es:edi
;调用前默认ds,es已经填入
Memory_Copy:
push ebp
mov ebp,esp
push ebx
push ecx
push esi
push edi
mov ecx,[ebp + 16]
mov edi,[ebp + 12]
mov esi,[ebp + 8]
.1:
cmp ecx,0
je .2
mov byte bl,[ds:esi]
inc esi
mov byte [es:edi],bl
inc edi
dec ecx
jmp .1
.2:
pop edi
pop esi
pop ecx
pop ebx
pop ebp
ret
;end of Memory_Copy---------------------------------------------------
最后的最后,附上结果图:
打算今晚重新整理代码滴,谁料到一个小小的问题害我调试了一整天,原来在LOADER里的平坦的段(数据)没有设置为32位的段,我又在内核里用它来寻堆栈里的地址。。唉。。教训啊。
- 【5.5节】切换堆栈和GDT分析
- 堆栈,GDT切换纯汇编版
- 堆栈切换和任务切换 CISR RISR
- 遍历IDT表和分析GDT表结构
- 深入分析任务切换与堆栈
- GDT是如何切换的
- 中断发生时用户堆栈和内核堆栈的切换
- 中断发生时用户堆栈和内核堆栈的切换
- GDT和LDT
- GDT和LDT
- GDT和LDT
- GDT
- GDT
- GDT
- 基于JOS 80x86 的堆栈切换简要分析
- kernel 3.10内核源码分析--内核栈及堆栈切换
- GDT和IDT的初始化
- GDT,LDTH和UuserContex关系
- Java基础:第八讲 使用集成开发环境(上)
- jdbc 组合查找+分页
- 有在杭州 深圳 北京工作的朋友吗
- 顶嵌学员学习笔记:Shell脚本编程总结
- 顶嵌学员学习笔记:内核升级的基本步骤
- 【5.5节】切换堆栈和GDT分析
- 顶嵌学员学习笔记:C指针学习总结
- 学习资源
- 对Java的凝?
- 关于IE6不支持PNG图片的解决办法补充
- 祝福成功
- 我的Blog
- 心情
- 培训机构众多,如何选择优秀嵌入式培训机构?