nasm重写linux-0.11 head.s (博古以通今)

;文件名:followking/boot/head.s
;本文件改写linux-0.11/boot/head.s,目的是为了体验整个系统构建的过程。
;我是看着赵炯《Linux内核0.11完全注释》编写的。不过,我是编写代码,有疑问再看。
;我用的nasm的语法格式。我想写一个操作系统,现在觉得最简单的方式莫过于
;先把前辈的实现的东西重新实现一遍。等到对这个问题有更深刻认识的时候,
;再重新思考,写出有自己特色的系统。
;作者:hk0625
;开始时间: 2010年03月20号星期六 20:57 
;完成时间: 2010年03月21号星期天 11:16
;最后修改时间: 2010年04月07日星期三 11:47
;修改理由:
;(1) 2010年03月26号星期五 20:51, 我把原来有下划线的全局变量做了修改
;(除_start外),理由现在的编译器对c代码编译后有关标识符已经不再加下划线了。
;地点:北化1#宿舍楼426
;下面let's try

;head.s含有32位启动代码。
;注意!32位启动代码是从绝对地址0x00000000开始的,这里也同样是页目录将存在的地方
;因此这里的启动代码将被页目录覆盖掉。
[section .text]
global idt, gdt, pg_dir, tmp_floppy_area, _start
extern stack_start, main, printk
_start:
pg_dir: ;页目录将会存放在这里。
 mov eax, 0x10
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
; mov ss, ax
 lss esp, [stack_start]
; mov esp, 0xff00  ;测试用的。
 
 call setup_idt
 call setup_gdt
 mov eax, 0x10
 mov ds, ax
 mov es, ax
 mov fs, ax
 mov gs, ax
; mov ss, ax
 lss esp, [stack_start]
; mov esp, 0xff00  ;测试用的。

;下面测试A20地址线是否已经开启。采用的方法是向内存地址0x000000处写入任意一个
;数值,然后看内存地址0x100000(1M)处是否也是这个数值。如果相同,就一直比较下去
;也即是循环。呵呵,这里不太明白为什么会是这样。
 xor eax, eax
lb: inc eax
 mov [0x000000], eax
 cmp [0x100000], eax
 je lb

;注意!在下面这段程序中,486应该将位16置位,以检查在超级用户模式下的写保护,
;此后“verify_area()”调用中就不需要了。486的用户通常也会想将NE(#5)置位,
;一边对数学协处理器的出错使用 int 16。
 mov eax, cr0
 and eax, 0x80000011
 
 or eax, 2
 mov cr0, eax
 call check_x87
 jmp after_page_tables

; 依赖ET标志的正确性来检测287/387存在与否。
check_x87:
 fninit
 fstsw ax
 cmp al, 0
 je lf
 mov eax, cr0
 xor eax, 6
 mov cr0, eax
 ret
align 2

lf: db 0xDB, 0xE4
 ret

;下面这段是设置中断描述符表子程序 setup_idt
;将中断描述符表idt设置成具有256个项,并都指向ignore_int中断门。然后加载中断
;描述符表寄存器(用lidt指令)。真正实用的中断门以后在安装。当我们在其他地方认为
;一切都正常时在开启中断。盖子程序将会被页表覆盖掉。
setup_idt:
 mov edx, ignore_int - $$  ;lea edx, ignore_int
 mov eax, 0x00080000
 mov ax, dx
 mov dx, 0x8E00
 
 mov edi, idt - $$  ;lea edx, _idt
 mov ecx, 256
rp_sidt:
 mov [edi], eax
 mov [edi+4], edx
 add edi, 8
 dec ecx
 jne rp_sidt
 lidt [idt_descr - $$]
 ret
;设置全局描述表项setup_gdt
;这个子程序设置一个新的全局描述表项gdt,并加载。此时仅创建了两个表项,
;与前面的一样。该子程序只有两行。
;该子程序将被页表覆盖。
setup_gdt:
 lgdt [gdt_descr - $$]
 ret

;这里将内核的内存页表直接放在页目录之后,使用了4个表来寻址16Mb的物理内存。
;如果你有多于16Mb的内存,就需要在这里进行扩充修改。
        times 4096 - ($ - $$) db 0 ;resb 0x1000 ;0rg 0x1000
pg0:
 times 4096 db 0 ;resb 0x1000 ;org 0x2000
pg1: 
 times 4096 db 0 ;resb 0x1000 ;org 0x3000
pg2:
 times 4096 db 0 ;resb 0x1000 ;org 0x4000
pg3:
 times 4096 db 0 ;resb 0x1000 ;org 0x5000
;当DMA(直接存储器访问)不能访问缓冲块时,下面的tmp_floppy_area内存块
;就可供软盘驱动程序使用。其地址需要对其调整,这样就不会跨越64kB边界。
tmp_floppy_area:
 times 1024 db 0 ;resb 1024

after_page_tables:
 push 0
 push 0
 push 0
 push L6 - $$
 push main
; push L6 - $$ ;调试用的,接下来死循环。
 jmp setup_paging
; call setup_paging
; push 0
; push 0
; jmp main
L6:
 jmp $  ;应该不会从main中返回这里,不过为了以防万一,
   ;在这里设一条指令吧。
;下面是默认的中断“向量句柄”
int_msg:
 db "Unknown interrupt. in the head.s", 0xa, 0x00
align 2
ignore_int:
 push eax
 push ecx
 push edx
 push ds
 
 push es
 push fs
 mov eax, 0x10
 mov ds, ax
 mov es, ax
 mov fs, ax
; mov ss, ax
 push int_msg - $$
 call printk ;在/kernel/printk.c中。

 pop eax
 pop fs
 pop es
 pop ds
 pop edx
 pop ecx
 pop eax
 jmp $
 iret
;这个子程序通过设置控制寄存器cr0的标志(PG位31)来启动对内存的分页处理功能,
;并设置各个页表的内容,以恒等映射前16MB的物理内纯。分页假定不会产生非法的
;地址映射(也即在只有4Mb的机器上设置出大于4Mb的内存地址)。
align 2
setup_paging:
 mov ecx, 5*1024
 xor eax, eax
 xor edi, edi

 cld
 rep
 stosd
 
 mov word [pg_dir + 0x00 - $$], pg0 + 7 - $$
 mov word [pg_dir + 0x04 - $$], pg1 + 7 - $$
 mov word [pg_dir + 0x08 - $$], pg2 + 7 - $$
 mov word [pg_dir + 0x0c - $$], pg3 + 7 - $$

 mov edi, pg3+4092 - $$
 mov eax, 0xfff007

 std
lb2: stosd ;这里存在问题
 sub eax, 0x1000
 jge lb2

  xor eax, eax
 mov cr3, eax
 
 mov eax, cr0
 or eax, 0x80000000
 mov cr0, eax
 ret

align 2
dw 0
idt_descr:
 dw 256*8-1
 dd idt - $$
align 2
dw 0
gdt_descr:
 dw 256*8-1
 dd gdt - $$
 
 align 8
times 0x6000 - ($ - $$) db 0
idt: times 256 dd 0, 0

gdt: dd 0x00000000
 dd 0x00000000
 dd 0x00000fff
 dd 0x00c09a00
 dd 0x00000fff
 dd 0x00c09200
 dd 0x00000000
 dd 0x00000000
 times 252 dd  0, 0
length equ $ - $$
[section .data]
[section .bss]

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值