boot.s head.s 学习说明

来源:互联网 发布:网页js动画特效代码 编辑:程序博客网 时间:2024/06/11 13:34
<pre name="code" class="plain">!boot.s!! It then loads the system at 0x10000, using BIOS interrupts. Thereafter! it disables all interrupts, changes to protected mode, and calls the !计算机在启动的时候会把磁盘里的第一个扇区的内容存到内存中0x7c00这个位置上!这个时候计算机用的是实地址模式BOOTSET=0x07c0的原因是因为它是段地址,要左!左移一位就变成了0x7c00了BOOTSEG = 0x07c0!引导程序的目的是把head.s编译出来的代码(二进制文件)先放到0x10000处,!这里用SYSSEG=0x1000是因为这属于段地址,需要在十六进制下左移一位再加上偏!地址。SYSSEG  = 0x1000! system loaded at 0x10000 !内核所占用的扇区数,每个扇区是512Byte,这个不是真正内核的大小,而是内核!大小的一个上限。SYSLEN  = 17! sectors occupied.entry startstart:        !跳到go的地方,并且将BOOTSEG作为段地址,go表示的是偏移地址,go表示        !的是go所在的指令地址到程序开始地址的距离,然后加上BOOTSEG这个段        !地址后才能找到真正的地址,起初的cs=0,eip=0x7c00,如果直接执行下        !去的话就变成cs=0,eip=0x7c05了,但是用了这句jmpi后就变成cs=0x07c0        !eip=0x5了,主要是为了初始化cs,eip这两个寄存器。jmpigo,#BOOTSEG                     !通过ax将数据段寄存器赋值,和cs一样,因为这段代码的指令和数据是混        !合在一起的。go:movax,csmovds,ax        !将堆栈段寄存器ss也设置的和cs一样,同时将堆栈偏移地址sp中设成0x400        !0x400转换成十进制为1024,也就是说在第1024字节处,这个地址要大于        !512很多,因为IA-32的架构下堆栈是倒着长的movss,axmovsp,#0x400! arbitrary value >>512! ok, we've written the message, nowload_system:        !load_system这段代码是为了加载head.s        !dx cx 是为了告诉系统一些拷贝代码的参数        !dh=0x00 磁头号 dl=0x00 驱动器号         !cl(7:6)=00B 磁道号高2位 ch=0x00 磁道号低8位 组合出来磁道号也是0        !cl(5:0)=00010B 表示起始扇区号,扇区号从1开始算,前面第一个扇区就        !是boot.s也就是这份代码,紧接着从第二个扇区开始的就是head.s了movdx,#0x0000movcx,#0x0002        !es:bx 也是一组段地址加偏移地址表示地址的组合表示写入的起始位置        !为了把代码先复制到0x10000处,因此通过ax把SYSSEG=0X1000作为段地址        !放到es中,然后xor bx,bx将bx清零,从而组合成0x10000的地址movax,#SYSSEGmoves,axxorbx,bx        !ax也是作为一个传递参数的寄存器需要设置的        !ah 表示后面int 0x13系统中断需要执行的操作是从磁盘复制代码到内存        !al则是需要写的字节数,这个只可大于head.s的长度,保证代码完整movax,#0x200+SYSLENint 0x13        !如果拷贝完成的话就跳到ok_load段,否则此处陷入死循环jncok_loaddie:jmpdie! now we want to move to protected mode ...!这段代码将会把代码从0x10000移到0x0处,之所以不能直接把代码移到0x0处!是因为计算机启动的时候会把bios的代码存到开头,比如int 0x13的代码,如果!在复制的过程中把代码覆盖过去的话系统就出错了,因此把它放到内存0x10000处!后在用cpu的指令把代码挪到0x0ok_load:        !关闭中断cli! no interrupts allowed !        !移动需要源地址ds:si 目的地址 es:di        !通过ax把head.s代码的段地址0x1000放到数据段ds中        !将0x0送到es中,最后将si和di都变成0        !和loop一样cx表示循环次数,每次都自减1,0x2000=8K次(1K=2^10)movax, #SYSSEGmovds, axxorax, axmoves, axmovcx, #0x2000subsi,sisubdi,di        !rep表示重复下面的代码知道cx变成0        !movw 中mov表示他是一个移动指令,就是将ds:si 移到 es:di处        !w表示每次移动一个字,每复制一次si和di就自动+1        !但是为什么移动的代码段明显大于head.s        !我现在能给出的解释就是为了保险起见,多复制总比少复制好        !但是在从磁盘中选定的长度是17个扇区 也就是相当于8704bytes        !而这里移动的次数相当于16384bytes,这两个不相等也是醉了。repmovw        !将ds恢复过来movax, #BOOTSEGmovds, ax        !lidt 将idt的基地址和限制长度载入到idtr中        !lgdt 将gdt的基地址和限制长度载入到gdtr中,这边可以先下去看        ! idt_48 和gdt_48的内容lidtidt_48! load idt with 0,0lgdtgdt_48! load gdt with whatever appropriate! absolute address 0x00000, in 32-bit protected mode.        !设置机器状态字,开启保护模式movax,#0x0001! protected mode (PE) bitlmswax! This is it!        !保护模式开启后 jmpi 偏移地址,段地址就和前面的使用方法不一样        !这里的段地址不再左移变成基地址,而是要从gdt表中取出基地址        !而后再加上偏移地址0,执行后的结果就是eip变成0,cs变成用8        !从gdt中取出的段地址了,这里可以看gdt表        !gdt表每4个字作为一项段描述符,从0开始计,但第0项无用,上面8是选择        !符也就是1000B,是4个字节,共32位,低16位有用,15:3 是索引,表示        !gdt表中的第几项,这里可以看到cs(15:3)是1B,表示第一项        !第0位为1表示执行程序以外的事件造成了异常(不明白什么意思???)        !第1位为1表示这是指向idt的        !第2位为1表示这是指向ldt的        !可见这三位都是0        !因此8表示gdt表中的第一项jmpi0,8! jmp offset 0 of segment 8 (cs)!gdt表中的每项是4个字即64位!查询一下数据段描述符 代码段描述符 系统段描述符!这里用第一项为例子,我们先从中间找到段限长,也就最低字0x07ff中的0x07ff!然后我们再拼出基地址,倒数第二低字节0x0000就是基地址的低16位,第二高字节!0x9A00的低8位0x0 是基地址的23:16位,而最高字的最高8位就是基地址的高8位,!至此我们可以完整地拼出了基地址0x0。!剩下的就是最高双字的中间16位了,这里我们写出来!C    0    9    A!1100 0000 1001 1010 用23到8表示 中间19到16表示段限长的19:16位,这里得!到了完整的段限长0x007ff gdt:.word0,0,0,0! dummy.word0x07FF! 8Mb - limit=2047 (2048*4096=8Mb).word0x0000! base address=0x00000.word0x9A00! code read/exec.word0x00C0! granularity=4096, 386.word0x07FF! 8Mb - limit=2047 (2048*4096=8Mb).word0x0000! base address=0x00000.word0x9200! data read/write.word0x00C0! granularity=4096, 386!这里可以看到这是两个部分的数据,idt_48有6个字节,最低位字节是 idt表的长度!注意这个代码在上面的表示低位地址,高位的两个字节是idt的基地址,这里没写idt!因此都默认成0,idt会在head.s中写出来,接着说字节问题,在书上画idtr的时候是!从左到右是基地址 限制长度,地址从左到右是从高到低,而这里的代码是从下到!上是对应的地址从高到低,因此有点违背常理,需要注意。!gdt_48 可以看到0x7c00+gdt是上面gdt表的位置idt_48: .word0! idt limit=0.word0,0! idt base=0Lgdt_48: .word0x7ff! gdt limit=2048, 256 GDT entries.word0x7c00+gdt,0! gdt base = 07xxx.org 510.word   0xAA55


</pre><pre name="code" class="plain">
<pre name="code" class="plain">#  head.s contains the 32-bit startup code.#  Two L3 task multitasking. The code of tasks are in kernel area,#  just like the Linux. The kernel code is located at 0x10000..code32!!!!!!需要注意的是这份代码的目的操作数和源操作数的放置顺序是反的。!这里有许多选择符,来取各种表中的项,一会儿设置表的时候再回头看SCRN_SEL= 0x18TSS0_SEL= 0x20LDT0_SEL= 0x28TSS1_SEL= 0X30LDT1_SEL= 0x38.global startup_32.textstartup_32:!通过ax将选择符0x10放到ds中,初始化数据段,0x10的低3位是000因此是在gdt表中!的10B项,也就是第二项,而gdt表就是刚才boot.s中设置的,和第一项唯一的区别!就是其中的A和C,这其中蕴含着数据段和代码段的属性区别movl $0x10,%eaxmov %ax,%ds!设置堆栈地址,转去init_stack代码,这里还包括了ss=dslss init_stack,%esp# setup base fields of descriptors.!调用设置idt和gdt的过程,跳转call setup_idtcall setup_gdt!重新设置各个段寄存器,和初始化堆栈movl $0x10,%eaxmov %ax,%dsmov %ax,%esmov %ax,%fsmov %ax,%gslss init_stack,%esp!设置计时器,定时产生中断movb $0x36, %almovl $0x43, %edxoutb %al, %dxmovl $11930, %eax        movl $0x40, %edxoutb %al, %dxmovb %ah, %aloutb %al, %dx!设置内核段选择符是0x8movl $0x00080000, %eax!设置调用的代码是timer_interruptmovw $timer_interrupt, %ax!设置一些属性movw $0x8E00, %dx!系统的定时中断号是8,因此要用新的中断来覆盖它,下面就是把上面设置的写进去movl $0x08, %ecx             lea idt(,%ecx,8), %esimovl %eax,(%esi)movl %edx,4(%esi)!再设置一个80中断来处理!可见是调用system_interrupt来处理movw $system_interrupt, %axmovw $0xef00, %dxmovl $0x80, %ecxlea idt(,%ecx,8), %esimovl %eax,(%esi)movl %edx,4(%esi)# unmask the timer interrupt.#movl $0x21, %edx#inb %dx, %al#andb $0xfe, %al#outb %al, %dx# Move to user mode (task 0)!将标志寄存器压栈,然后在栈中把nt标志复位,完了之后弹出恢复pushflandl $0xffffbfff, (%esp)popfl!设置tr的选择符是0x20,tr寄存器中的值就是0x20movl $TSS0_SEL, %eaxltr %ax!将ldt装载进寄存器movl $LDT0_SEL, %eaxlldt %ax!current表示当前的任务号是0movl $0, current!打开中断sti!通过压栈,回弹,实现跳转!iret的回弹顺序是eip cs eflag esp ss,让初始化,然后指针指向了任务0!0x17是一个选择符,二进制展开是00011111,可见是ldt中的第三项pushl $0x17pushl $init_stackpushflpushl $0x0fpushl $task0iret/****************************************/setup_gdt:lgdt lgdt_opcoderet!这里是把所有的idt都设成一样的,也就是当有中断来的时候就调用ignore_int过程!0xb5!这里终端门设置高双字是edx,低双字是eax!movl表示每次移动双字setup_idt:lea ignore_int,%edxmovl $0x00080000,%eaxmovw %dx,%ax/* selector = 0x0008 = cs */movw $0x8E00,%dx/* interrupt gate - dpl=0, present */lea idt,%edimov $256,%ecxrp_sidt:movl %eax,(%edi)movl %edx,4(%edi)addl $8,%edidec %ecxjne rp_sidt!将idt的表基地址和段限长放入idtr表中lidt lidt_opcoderet# -----------------------------------!write_char:        !保存要用到的寄存器push %gspushl %ebx#pushl %eax        !让gs指向显示内存段mov $SCRN_SEL, %ebxmov %bx, %gs        !字符显示位置值movl scr_loc, %ebx        !因为还有一位属性值,故位置乘2shl $1, %ebx        !显示字符movb %al, %gs:(%ebx)        !恢复位置,然后再加一,如果等于2000则清0shr $1, %ebxincl %ebxcmpl $2000, %ebxjb 1fmovl $0, %ebx        !保存位置1:movl %ebx, scr_loc        !恢复寄存器popl %ebxpop %gsret/***********************************************//* This is the default interrupt "handler" :-) */.align 2ignore_int:push %dspushl %eaxmovl $0x10, %eaxmov %ax, %dsmovl $67, %eax            /* print 'C' */call write_charpopl %eaxpop %dsiret/* Timer interrupt handler */.align 2timer_interrupt:!保存寄存器,包括ax中的字符ASCII码,为了下次切换的时候返回,同时在tss中!保存的是对应ljmp对应的下一个指令的地址push %dspushl %eax!指向内核段movl $0x10, %eaxmov %ax, %ds!向硬件写数据movb $0x20, %aloutb %al, $0x20movl $1, %eax!判断目前的任务号,然后判断跳转cmpl %eax, currentje 1fmovl %eax, currentljmp $TSS1_SEL, $0jmp 2f!切换到0任务,然后切换tss,此时连堆栈也切换了,因此后面弹出的不是上面push!的,而是前一次要切换到1任务是进入这个过程初始压入的ds和eax,同理切换到1任!务1:movl $0, currentljmp $TSS0_SEL, $02:popl %eaxpop %dsiret/* system call handler */.align 2system_interrupt:!保存各个寄存器到堆栈push %dspushl %edxpushl %ecxpushl %ebxpushl %eax!数据段指向gdt 10项中的内核段movl $0x10, %edxmov %dx, %ds!调用写字符过程call write_char!恢复寄存器的值0popl %eaxpopl %ebxpopl %ecxpopl %edxpop %dsiret/*********************************************/current:.long 0scr_loc:.long 0.align 2lidt_opcode:.word 256*8-1# idt contains 256 entries.long idt# This will be rewrite by code.lgdt_opcode:.word (end_gdt-gdt)-1# so does gdt.long gdt# This will be rewrite by code..align 8idt:.fill 256,8,0# idt is uninitializedgdt:.quad 0x0000000000000000/* NULL descriptor */.quad 0x00c09a00000007ff/* 8Mb 0x08, base = 0x00000 */.quad 0x00c09200000007ff/* 8Mb 0x10 */.quad 0x00c0920b80000002/* screen 0x18 - for display */.word 0x0068, tss0, 0xe900, 0x0# TSS0 descr 0x20.word 0x0040, ldt0, 0xe200, 0x0# LDT0 descr 0x28.word 0x0068, tss1, 0xe900, 0x0# TSS1 descr 0x30.word 0x0040, ldt1, 0xe200, 0x0# LDT1 descr 0x38end_gdt:!这里一定要注意堆栈增长方向,栈底地址是0xbd8,用0初始化堆栈.fill 128,4,0init_stack:                          # Will be used as user stack for task0..long init_stack.word 0x10/*************************************/.align 8!对应的段,段限长都是0x3ff,偏移地址都是0x0对应任务0ldt0:.quad 0x0000000000000000.quad 0x00c0fa00000003ff# 代码段0x0f, base = 0x00000.quad 0x00c0f200000003ff# 数据段0x17!注意两个tss初始化不一样,第一次切到任务一是是从task1开始运行的,后面就不!是了,其中task1处都改变了,这里的系统栈也用的init_stacktss0:.long 0 /* back link */.long krn_stk0, 0x10/* esp0, ss0 */.long 0, 0, 0, 0, 0/* esp1, ss1, esp2, ss2, cr3 */.long 0, 0, 0, 0, 0/* eip, eflags, eax, ecx, edx */.long 0, 0, 0, 0, 0/* ebx esp, ebp, esi, edi */.long 0, 0, 0, 0, 0, 0 /* es, cs, ss, ds, fs, gs */.long LDT0_SEL, 0x8000000/* ldt, trace bitmap */.fill 128,4,0krn_stk0:#.long 0/************************************/.align 8!对应的段,段限长都是0x3ff,偏移地址都是0x0对应任务1ldt1:.quad 0x0000000000000000.quad 0x00c0fa00000003ff# 0x0f, base = 0x00000.quad 0x00c0f200000003ff# 0x17tss1:.long 0 /* back link */.long krn_stk1, 0x10/* esp0, ss0 */.long 0, 0, 0, 0, 0/* esp1, ss1, esp2, ss2, cr3 */.long task1, 0x200/* eip, eflags */.long 0, 0, 0, 0/* eax, ecx, edx, ebx */.long usr_stk1, 0, 0, 0/* esp, ebp, esi, edi */.long 0x17,0x0f,0x17,0x17,0x17,0x17 /* es, cs, ss, ds, fs, gs */.long LDT1_SEL, 0x8000000/* ldt, trace bitmap */.fill 128,4,0krn_stk1:/************************************/task0:!这里我们去看一下ldt表中的17项,设置ds段movl $0x17, %eaxmovw %ax, %ds!65对应A的ascii码movb $65, %al              /* print 'A' */!调用80中断,这边我们再看设置80终端的代码int $0x80!延迟作用movl $0xfff, %ecx1:loop 1b!重复task0jmp task0task1:movl $0x17, %eaxmovw %ax, %dsmovb $66, %al              /* print 'B' */int $0x80movl $0xfff, %ecx1:loop 1bjmp task1.fill 128,4,0usr_stk1:


                                             
0 0
原创粉丝点击