Windows编译环境下的Linux启动代码

开发者在线 Builder.com.cn 更新时间:2008-03-27作者:浪人BLUE 来源:CSDN

本文关键词: Linux 编译环境 Windows 开源

过去,有许多讲Linux源代码的资料和书籍,但所讲解的代码都是要在Linux环境下编译的,这给只安装了Windows而又想一睹Linux启动代码的用户带来了极大的不便。监于此,本人将可在Windows编译环境下的编译运行的Linux启动源代码公之于众,以供大家学习、参考。

 

 

 

  由于代码是从Linux源代码翻译过来的,且赵炯博士已经注释的十分详细,这里就不再多说了。代码为汇编语言,编译环境为TASM+TLINK

 

 

 

1bootsect.asm,这个文件是从bootsect.s翻译过来的,功能相同,我在文件末尾增加了用来调试的error例程。内容为:

 

 

 

code segment

;define the const here

  SYSSIZE = 3000h

  SETUPLEN = 4                          ; nr of setup-sectors

  BOOTSEG  = 07c0h                ; original address of boot-sector

  INITSEG  = 9000h                   ; we move boot here - out of the way

  SETUPSEG = 9020h                   ; setup starts here

  SYSSEG   = 1000h                   ; system loaded at 0x10000 (65536).

  ENDSEG   = SYSSEG + SYSSIZE        ; where to stop loading

 

       assume cs:code,ds:code,es:code

start:

  mov       ax,BOOTSEG

       mov ds,ax

       mov       ax,INITSEG

       mov es,ax

       mov cx,256

       sub  si,si

       sub  di,di

       rep movsw

      

       ;jump to 0x90000

       mov bx,8ff0h

       mov ds,bx

       mov bx,00h

       mov word ptr ds:[bx],offset go

       mov word ptr ds:[bx]+2,9000h

 

 

 

       mov bx,00h

       jmp dword ptr [bx] 

go LABEL FAR

 

 

 

  mov       ax,INITSEG

       mov ds,ax

       mov es,ax

; put stack at 0x9ff00.

       mov ss,ax

       mov       sp,0FF00h              ; arbitrary value >>512

        

; load the setup-sectors directly after the bootblock.

; Note that 'es' is already set up.

 

 

 

load_setup:

       ;copy sector 2,3,4,5 code to 0x90200,seg=0x9020

       mov       dx,0000h                ; drive 0, head 0

       mov       cx,0002h                ; sector 2, track 0

       mov       bx,0200h                ; address = 512, in INITSEG

       mov       ax,0200h+SETUPLEN      ; service 2, nr of sectors

       int    13h                ; read it

       jnc       ok_load_setup           ; ok - continue

       mov       dx,0000h

       mov       ax,0000h        ; reset the diskette

       int    13h

       jmp       load_setup

 

 

 

ok_load_setup:

        

; Get disk drive parameters, specifically nr of sectors/track

 

 

 

       mov dl,00h

       mov       ax,0800h        ; AH=8 is get drive parameters

       int    13h

       mov ch,00h

       mov       sectors,cx

  mov    ax,INITSEG

  mov    es,ax   

     

; Print some inane message

 

 

 

       mov ah,03h              ; read cursor pos

       xor  bh,bh

       int    10h

      

       mov cx,24

       mov       bx,0007h        ; page 0, attribute 7 (normal)

       mov       bp,offset msg1

       mov       ax,1301h        ; write string, move cursor

       int    10h 

             

; ok, we've written the message, now

; we want to load the system (at 0x10000)

 

 

 

       mov       ax,SYSSEG

       mov es,ax              ; segment of 0x010000

       call  read_it

       call       kill_motor      

 

 

 

; After that we check which root-device to use. If the device is

; defined (;= 0), nothing is done and the given device is used.

; Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending

; on the number of sectors that the BIOS reports currently.

 

 

 

       mov       ax,root_dev

       cmp ax,00h

       jne       root_defined

       mov       bx,sectors

       mov       ax,0208h        ; /dev/ps0 - 1.2Mb

       cmp bx,15

       je       root_defined

       mov       ax,021ch        ; /dev/PS0 - 1.44Mb

       cmp bx,18

       je       root_defined

undef_root:       ;will loop for ever

       jmp undef_root

root_defined:

       mov       root_dev,ax

 

 

 

; after that (everyting loaded), we jump to

; the setup-routine loaded directly after

; the bootblock:                    

 

 

 

       ;jump to 0x90200

       mov bx,8ff0h

       mov ds,bx

       mov bx,00h

       mov word ptr ds:[bx],0000h

       mov word ptr ds:[bx]+2,9020h

             

       mov bx,00h

       jmp dword ptr [bx]

      

; This routine loads the system at address 0x10000, making sure

; no 64kB boundaries are crossed. We try to load it as fast as

; possible, loading whole tracks whenever we can.

;

; in:  es - starting address segment (normally 0x1000)

;

       sread dw 1+SETUPLEN       ; sectors read of current track

  head  dw 0                     ; current head

  track dw 0                ; current track

 

 

 

read_it:

       mov ax,es

       test ax,0fffh

die:  jne die                   ; es must be at 64kB boundary

       xor bx,bx              ; bx is starting address within segment

       mov       ax,INITSEG

  mov ds,ax

rp_read:

       mov ax,es

       cmp ax,ENDSEG          ; have we loaded all yet?

       jb ok1_read

       ret

ok1_read:

  mov       ax,INITSEG

  mov ds,ax

       mov ax,sectors

       sub ax,sread

       mov cx,ax

       shl cx,09h

       add cx,bx

       jnc ok2_read

       je ok2_read

       xor ax,ax

       sub ax,bx

       shr ax,09h

ok2_read:

       call read_track

       mov cx,ax

       add ax,sread

       cmp ax,sectors

       jne ok3_read

       mov ax,01h

       sub ax,head

       jne ok4_read

       inc track

ok4_read:

       mov head,ax

       xor ax,ax

ok3_read:

       mov sread,ax

       shl cx,09h

       add bx,cx

       jnc rp_read

       mov ax,es

       add ax,1000h

       mov es,ax

       xor bx,bx

       jmp rp_read

      

read_track:

       push ax

       push bx

       push cx

       push dx

       mov dx,track

       mov cx,sread

       inc cx

       mov ch,dl

       mov dx,head

       mov dh,dl

       mov dl,00h

       and dx,0100h

       mov ah,02h

       int 13h

       jc bad_rt

       pop dx

       pop cx

       pop bx

       pop ax

       ret

bad_rt:   

  mov ax,00h

       mov dx,00h

       int 13h

       pop dx

       pop cx

       pop bx

       pop ax

       jmp read_track

      

;/*

; * This procedure turns off the floppy drive motor, so

; * that we enter the kernel in a known state, and

; * don't have to worry about it later.

; */

kill_motor:

       push dx

       mov dx,03f2h

       mov al,0h

       out dx,al

       pop dx

       ret

      

  ;read fail and show error,我自己加的,可以在需要的地方调用以显示信息

error:

       mov ax,0b800h

  mov es,ax

  xor di,di

  mov       ax,INITSEG

  mov ds,ax

  mov si,offset strError

  mov cx,20

repShow:

  lodsb

  stosb

  mov al,87h

  stosb

  dec cx

  cmp cx,0

  jne repShow

  ret

  

;define the vars here

  sectors dw 00h

  strError db 'ERROR***********ERROR***********'

  msg1 db 13,10,'Loading system ...',13,10,13,10

; ROOT_DEV:       0x000 - same type of floppy as boot.

;             0x301 - first partition on first drive etc

  ROOT_DEV dw 0

  ;ROOT_DEV = 0306h

code ends

end start

2setup.asm,是从setup.s翻译过来的。但这里为16位段的386指令,且我增加了可以输出字符的代码段和数据段。内容为:

 

 

 

P386

jump macro selector,offset

  db 0eah       ;

  dw offset     ;

  dw selector   ;

  endm

code segment use16

;define the const

  INITSEG  = 9000h     ; we move boot here - out of the way

  SYSSEG   = 1000h     ; system loaded at 0x10000 (65536).

  SETUPSEG = 9020h     ; this is the current segment 

 

       assume cs:code,ds:code,es:code

start:

  mov ax,INITSEG      ; this is done in bootsect already, but...

       mov ds,ax

       mov ah,03h           ; read cursor pos

       xor  bh,bh

       int    10h                   ; save it in known place, con_init fetches

  mov       ds:[0],dx          ; it from 0x90000.

; Get memory size (extended mem, kB)

 

 

 

       mov ah,88h

       int    15h

       mov       ds:[2],ax 

 

; Get video-card data:

 

 

 

       mov ah,0fh

       int    10h

       mov       ds:[4],bx        ; bh = display page

       mov       ds:[6],ax         ; al = video mode, ah = window width

      

; check for EGA/VGA and some config parameters

 

 

 

       mov ah,12h

       mov bl,10h

       int    10h

       mov       ds:[8],ax

       mov       ds:[10],bx

       mov       ds:[12],cx

      

; Get hd0 data

 

 

 

       mov       ax,0000h

       mov ds,ax

       mov       si,0104h ;4*41h

       mov       ax,INITSEG

       mov es,ax

       mov       di,0080h

       mov cx,10h

       rep   movsb

 

 

 

; Get hd1 data

 

 

 

       mov       ax,0000h

       mov ds,ax

       mov si,0118       ;4*46h

       mov       ax,INITSEG

       mov es,ax

       mov       di,0090h

       mov cx,10h

       rep   movsb

      

; Check that there IS a hd1 :-)

 

 

 

       mov       ax,01500h

       mov dl,81h

       int    13h

       jc       no_disk1

       cmp ah,03h

       je       is_disk1

no_disk1:

       mov       ax,INITSEG

       mov es,ax

       mov       di,0090h

       mov cx,10h

       mov ax,00h

       rep   stosb

is_disk1: 

 

 

 

       ; now we want to move to protected mode ...

 

 

 

       cli                  ; no interrupts allowed ;

 

 

 

; first we move the system to it's rightful place

 

 

 

       mov       ax,0000h

       cld                 ; 'direction'=0, movs moves forward

 

do_move:

       mov es,ax              ; destination segment

       add       ax,1000h

  cmp ax,9000h

       jz       end_move

       mov ds,ax              ; source segment

       xor  di,di

       xor  si,si

  mov cx,8000h

  rep movsw

       jmp       do_move

 

 

 

; then we load the segment descriptors

end_move:

       mov       ax,SETUPSEG   ; right, forgot this at first. didn't work :-)

       mov ds,ax

  lidt      qword ptr idt_48              ; load idt with 0,0 ;can't run in via cpu ,so mark off

       lgdt  qword ptr gdt_48             ; load gdt with whatever appropriate

 

; that was painless, now we enable A20

 

 

 

       call       empty_8042

       mov       al,0D1h          ; command write

       out   64h,al

       call       empty_8042

       mov       al,0DFh          ; A20 on

       out   60h,al

       call       empty_8042

    

; well, that went ok, I hope. Now we have to reprogram the interrupts :-(

; we put them right after the intel-reserved hardware interrupts, at

; int 0x20-0x2F. There they won't mess up anything. Sadly IBM really

; messed this up with the original PC, and they haven't been able to

; rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,

; which is used for the internal hardware interrupts as well. We just

; have to reprogram the 8259's, and it isn't fun.

       mov al,11h              ; initialization sequence

       out   20h,al              ; send it to 8259A-1

       dw 00ebh,00ebh          ; jmp $+2, jmp $+2

       out       0A0h,al          ; and to 8259A-2

       dw 00ebh,00ebh

       mov al,20h              ; start of hardware int's (0x20)

       out   21h,al

       dw 00ebh,00ebh

       mov al,28h              ; start of hardware int's 2 (0x28)

       out       0A1h,al

       dw 00ebh,00ebh

       mov al,04h              ; 8259-1 is master

       out   21h,al

       dw 00ebh,00ebh

       mov al,02h              ; 8259-2 is slave

       out       0A1h,al

       dw 00ebh,00ebh

       mov al,01h              ; 8086 mode for both

       out   21h,al

       dw 00ebh,00ebh

       out       0A1h,al

       dw 00ebh,00ebh

       mov       al,0FFh          ; mask off all interrupts for now

       out   21h,al

       dw 00ebh,00ebh

       out       0A1h,al

      

; well, that certainly wasn't fun :-(. Hopefully it works, and we don't

; need no steenking BIOS anyway (except for the initial loading :-).

; The BIOS-routine wants lots of unnecessary data, and it's less

; "interesting" anyway. This is how REAL programmers do it.

;

; Well, now's the time to actually move into protected mode. To make

; things as simple as possible, we do no register set-up or anything,

; we let the gnu-compiled 32-bit programs do that. We just jump to

; absolute address 0x00000, in 32-bit protected mode.

  mov eax,00000001h

  mov cr0,eax

      mov       ax,0001h ; protected mode (PE) bit

  lmsw    ax              ; This is it;

 

 

 

       ;jmp offset 0 of segment 8 (cs)

  ;mov bx,0018h       ;jump segment select

  ;mov ds,bx

  ;mov bx,00h

  ;mov word ptr ds:[bx],5000h       ;because jump to 00h,then jump to 5000h in sec3 has error,so jump to 5000h dirctly here

  ;mov word ptr ds:[bx]+2,0008h

 

  ;mov bx,00h

  ;jmp dword ptr [ebx] 

  jump <0008h>,<0000h>   ;now,sec3 use32,all is ok

      

; This routine checks that the keyboard command queue is empty

; No timeout is used - if this hangs there is something wrong with

; the machine, and we probably couldn't proceed anyway.

 

 

 

empty_8042:

       dw       00ebh,00ebh

       in     al,64h       ; 8042 status port

       test al,02h              ; is input buffer full?

       jnz       empty_8042     ; yes - loop

       ret         

        

;display,can run in protect mode

showMsg:

       mov bx,0020h

  mov es,bx

  xor di,di

  mov bx,0010h

  mov ds,bx

  mov si,offset strShow

  mov cx,30

repeat:

  lodsb

  stosb

  mov al,87h

  stosb

  dec cx

  cmp cx,0

  jne repeat

 

 

 

;define the vars

  strShow db 'SECTOR2***********SECTOR2***********SECTOR2'

  idt_48 dw 0,0,0

  gdt dw 0,0,0,0 ; dummy

  gdt1 dw 7FFFh,0000h,9A00h,00C0h               ; granularity=4096, 386

  gdt2 dw 7FFFh,0000h,9200h,00C0h               ; 128MB

  gdt3 dw 07FFh,0fff0h,9208h,00C0h      ; for jmp use,base=8fff0h near the 90000h

  gdt4 dw 07FFh,08000h,920Bh,00C0h  ; for show Message use,base=b8000h,4MB size

  ; gdt limit=2048, 256 GDT entries

  gdt_48 dw 800h,0200h+gdt,09h      ; gdt base = 0X9xxxx

 

code ends

 

 

 

end start

3setup.asm,是从setup.s翻译过来的。不过,这里有些改动,我把中断0-F分别用自己的函数实现了,并且加入了两个任务,他们通过TSS相互切换。这里的程序已经是32位的了,且声明支持386指令。内容为:

 

 

 

P386

jump macro selector,offset      ;for 32 bit jmp

  db 0eah       ;

  dw offset     ;

  dw 00h

  dw selector   ;

  endm

call32 macro selector,offset    ;for 32 bit call

  db 09ah

  dw offset

  dw 00h

  dw selector

  endm

code segment use32

       assume cs:code,ds:code,es:code

    public showMsg

    public strShow

pg_dir:

start:

  mov ax,10h

  mov ds,ax

  mov es,ax

  mov fs,ax

  mov gs,ax

  mov ss,ax       ;setup ss,sp by nomad

  mov esp,0ffffffh ;16MB upper

 

   

  call setup_idt

  call setup_gdt ; load gdt with whatever appropriate

 

  mov ax,10h     ; reload all the segment registers

  mov ds,ax            ; after changing gdt. CS was already

  mov es,ax      ; reloaded in 'setup_gdt'

  mov fs,ax

  mov gs,ax

 

 

 

       xor eax,eax

A20:

       inc eax                          ; check that A20 really IS enabled

       mov ds:[000000h],eax     ; loop forever if it isn't

       cmp ds:[100000h],eax

       je A20

 

  ;chach 80287

       mov eax,cr0                 ; check math chip

       and eax,80000011h         ; Save PG,PE,ET

  ; "orl x10020,%eax" here for 486 might be good

       or eax,02h                    ; set MP

       mov cr0,eax

       call check_x87

       jmp after_page_tables

 

;check the 80287/80387*******************

check_x87:

       fninit

       fstsw ax

       cmp al,0

       je x87                           ;no coprocessor: have to set bits

       mov eax,cr0

       xor eax,06h                 ;reset MP, set EM

       mov cr0,eax

       ret

align 2

x87:

  db 0DBh,0E4h              ;fsetpm for 287, ignored by 387

       ret

 

;setup the interrupt********************** 

setup_idt:

       lea edx,ignore_int

       mov eax,00080000h

       mov ax,dx                    ;selector = 0x0008 = cs

       mov dx,8E00h           ;interrupt gate - dpl=0, present

 

 

 

       lea edi,idt

       mov ecx,256

rp_sidt:

       mov [edi],eax

       mov 4[edi],edx

       add edi,8

       dec ecx

       jne rp_sidt

       ;lidt qword ptr idt_48

       ;ret

 

 

 

;add 0-9 interrupt for test

add_int0:

  lea edx,int_0

       mov eax,00080000h

       mov ax,dx                    ;selector = 0x0008 = cs

       mov dx,8E00h           ;interrupt gate - dpl=0, present

 

 

 

       lea edi,idt

       add edi,8*0

  mov [edi],eax

       mov 4[edi],edx

 

 

 

add_int1:

  lea edx,int_1

       mov eax,00080000h

       mov ax,dx                    ;selector = 0x0008 = cs

       mov dx,8E00h           ;interrupt gate - dpl=0, present

 

 

 

       lea edi,idt

       add edi,8*1

  mov [edi],eax

       mov 4[edi],edx