当光盘变成记录数据以后,就开始使用光盘来分发操作系统了。毕竟光盘有着储存数据量大,成本便宜的优势。下面就来分析Reactos是怎么样通过光盘的格式来引导操作系统的。
现今的计算机BIOS,会在开机时根据El Torito规格,查找光盘上的开机代码。若该光盘具有开机代码,则BIOS会指配一个磁盘驱动器代号给该光驱。磁盘驱动器代号通常为80(模拟硬盘)或是00(模拟软盘)等。借由模拟成硬盘或软盘,可让旧式的操作系统由光盘开机。
现今新式的操作系统则不需做模拟,只要有如ISOLINUX之类的开机引导程序(boot loader),即可由光盘开机。
#001 ; ****************************************************************************
#002 ;
#003 ; isolinux.asm
#004 ;
#005 ; A program to boot Linux kernels off a CD-ROM using the El Torito
#006 ; boot standard in "no emulation" mode, making the entire filesystem
#007 ; available. It is based on the SYSLINUX boot loader for MS-DOS
#008 ; floppies.
#009 ;
#010 ; Copyright (C) 1994-2001 H. Peter Anvin
#011 ;
#012 ; This program is free software; you can redistribute it and/or modify
#013 ; it under the terms of the GNU General Public License as published by
#014 ; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
#015 ; USA; either version 2 of the License, or (at your option) any later
#016 ; version; incorporated herein by reference.
#017 ;
#018 ; ****************************************************************************
#019 ;
#020 ; THIS FILE IS A MODIFIED VERSION OF ISOLINUX.ASM
#021 ; MODIFICATION DONE BY MICHAEL K TER LOUW
#022 ; LAST UPDATED 3-9-2002
#023 ; SEE "COPYING" FOR INFORMATION ABOUT THE LICENSE THAT APPLIES TO THIS RELEASE
#024 ;
#025 ; ****************************************************************************
#026 ;
#027 ; This file is a modified version of ISOLINUX.ASM.
#028 ; Modification done by Eric Kohl
#029 ; Last update 04-25-2002
#030 ;
#031 ; ****************************************************************************
#032
#033 ; Note: The Makefile builds one version with DEBUG_MESSAGES automatically.
#034 ;%define DEBUG_MESSAGES ; Uncomment to get debugging messages
#035
#036 %define WAIT_FOR_KEY
#037
#038
下面BIOS的数据区保留内存空间。
#039 ; ---------------------------------------------------------------------------
#040 ; BEGIN THE BIOS/CODE/DATA SEGMENT
#041 ; ---------------------------------------------------------------------------
#042
#043 absolute 0400h
#044 serial_base resw 4 ; Base addresses for 4 serial ports
#045 absolute 0413h
#046 BIOS_fbm resw 1 ; Free Base Memory (kilobytes)
#047 absolute 046Ch
#048 BIOS_timer resw 1 ; Timer ticks
#049 absolute 0472h
#050 BIOS_magic resw 1 ; BIOS reset magic
#051 absolute 0484h
#052 BIOS_vidrows resb 1 ; Number of screen rows
#053
#054 ;
#055 ; Memory below this point is reserved for the BIOS and the MBR
#056 ;
1000h以前都是BIOS的数据区,不能放置代码。
#057 absolute 1000h
#058 trackbuf resb 8192 ; Track buffer goes here
#059 trackbufsize equ $-trackbuf
#060 ; trackbuf ends at 3000h
#061
#062 struc open_file_t
#063 file_sector resd 1 ; Sector pointer (0 = structure free)
#064 file_left resd 1 ; Number of sectors left
#065 endstruc
#066
#067 struc dir_t
#068 dir_lba resd 1 ; Directory start (LBA)
#069 dir_len resd 1 ; Length in bytes
#070 dir_clust resd 1 ; Length in clusters
#071 endstruc
#072
#073
#074 MAX_OPEN_LG2 equ 2 ; log2(Max number of open files)
#075 MAX_OPEN equ (1 << MAX_OPEN_LG2)
#076 SECTORSIZE_LG2 equ 11 ; 2048 bytes/sector (El Torito requirement)
#077 SECTORSIZE equ (1 << SECTORSIZE_LG2)
#078 CR equ 13 ; Carriage Return
#079 LF equ 10 ; Line Feed
#080 retry_count equ 6 ; How patient are we with the BIOS?
#081
#082
#083
BSS段数据。
#084 absolute 5000h ; Here we keep our BSS stuff
#085
#086 DriveNo resb 1 ; CD-ROM BIOS drive number
#087 DiskError resb 1 ; Error code for disk I/O
#088 RetryCount resb 1 ; Used for disk access retries
#089 TimeoutCount resb 1 ; Timeout counter
#090 ISOFlags resb 1 ; Flags for ISO directory search
#091 RootDir resb dir_t_size ; Root directory
#092 CurDir resb dir_t_size ; Current directory
#093 ISOFileName resb 64 ; ISO filename canonicalization buffer
#094 ISOFileNameEnd equ $
#095
#096
#097 alignb open_file_t_size
#098 Files resb MAX_OPEN*open_file_t_size
#099
#100
#101
这里ISOBOOT有代码段开始,由于BIOS会读取光盘的引导区到内存 7000开始位置,跟硬盘和磁盘是一样的。
#102 section .text
#103 org 7000h
#104
#105 start:
#106 cli ; Disable interrupts
#107 xor ax, ax ; ax = segment zero
#108 mov ss, ax ; Initialize stack segment
#109 mov sp, start ; Set up stack
#110 mov ds, ax ; Initialize other segment registers
#111 mov es, ax
#112 mov fs, ax
#113 mov gs, ax
#114 sti ; Enable interrupts
#115 cld ; Increment pointers
#116
把引导代码从0000:7C00位置拷贝到0000:7000位置,然后再运行。
#117 mov cx, 2048 >> 2 ; Copy the bootsector
#118 mov si, 0x7C00 ; from 0000:7C00
#119 mov di, 0x7000 ; to 0000:7000
#120 rep movsd ; copy the program
通过一个跳转,实现重定位代码位置。
#121 jmp 0:relocate ; jump into relocated code
#122
#123 relocate:
如果显示调度信息。
#124 ; Display the banner and copyright
#125 %ifdef DEBUG_MESSAGES
#126 mov si, isolinux_banner ; si points to hello message
#127 call writestr ; display the message
#128 mov si,copyright_str
#129 call writestr
#130 %endif
#131
#132
下面通过检查键盘输入来决定是否从光盘引导系统。
先清空键盘输入。
#133 ; Make sure the keyboard buffer is empty
#134 %ifdef WAIT_FOR_KEY
#135 .kbd_buffer_test:
#136 call pollchar
#137 jz .kbd_buffer_empty
#138 call getchar
#139 jmp .kbd_buffer_test
#140 .kbd_buffer_empty:
到这里已经把键盘缓冲区清空了。
检查硬盘的MBR是否可以引导。
#141
#142 ; Check for MBR on harddisk
#143 pusha
#144 mov ax, 0201h
#145 mov dx, 0080h
#146 mov cx, 0001h
#147 mov bx, trackbuf
#148 int 13h
#149 popa
如果硬盘不是可引导的,就立即跳到光盘引导,不需要用户选择。
#150 jc .boot_cdrom ; could not read hdd
#151
判断硬盘是否有引导扇区。
#152 push ax
#153 mov ax, word [trackbuf]
#154 cmp ax, 0
如果没有引导扇区,就立即跳到光盘引导。
#155 je .boot_cdrom ; no boot sector found (hopefully there are no weird bootsectors which begin with 0)
#156 pop ax
#157
开始显示用户选择光盘引导,还是磁盘引导,同时进行5秒钟计时,如果用户不按下任何按键,就会自动选择硬盘引导。
#158 ; Display the 'Press key' message and wait for a maximum of 5 seconds
输出换行回车。
#159 call crlf
输出按下任何键的提示字符串。
#160 mov si, presskey_msg ; si points to 'Press key' message
#161 call writestr ; display the message
#162
5秒钟倒计时。
#163 mov byte [TimeoutCount], 5
#164 .next_second:
获取当前时钟计数到eax,并且相加19个计数。
#165 mov eax, [BIOS_timer] ; load current tick counter
#166 add eax, 19 ;
#167
测试是否有键盘输入任何按键。
#168 .poll_again:
#169 call pollchar
如果有任何按键输入,就跳到光盘引导。
#170 jnz .boot_cdrom
#171
每次比较BIOS里的计数是否与EAX计数相同。
#172 mov ebx, [BIOS_timer]
#173 cmp eax, ebx
#174 jnz .poll_again
#175
如果经历过19个计数,说明已经过了1秒钟。接着开始在屏幕上打印一个点。
#176 mov si, dot_msg ; print '.'
#177 call writestr
如果超过5秒钟,就跳到硬盘引导。
#178 dec byte [TimeoutCount] ; decrement timeout counter
#179 jz .boot_harddisk
跳到下一秒计时。
#180 jmp .next_second
#181
硬盘引导的代码。
#182 .boot_harddisk:
#183 call crlf
#184
从第一个硬盘引导。
#185 ; Boot first harddisk (drive 0x80)
#186 mov ax, 0201h
#187 mov dx, 0080h
#188 mov cx, 0001h
#189 mov bx, 7C00h
#190 int 13h
#191 jnc .go_hd
#192 jmp kaboom
#193 .go_hd:
#194 mov ax, cs
#195 mov ds, ax
#196 mov es, ax
#197 mov fs, ax
#198 mov gs, ax
#199 mov dx, 0080h
#200
#201 jmp 0:0x7C00
#202 %endif
#203
从光盘引导。
#204 .boot_cdrom:
#205 %ifdef WAIT_FOR_KEY
#206 call crlf
#207 call crlf
#208 %endif
#209
#210 ; Save and display the boot drive number
#211 mov [DriveNo], dl
#212 %ifdef DEBUG_MESSAGES
#213 mov si, startup_msg
#214 call writemsg
#215 mov al, dl
#216 call writehex2
#217 call crlf
#218 %endif
#219
获取光驱的模拟状态。
#220 ; Now figure out what we're actually doing
#221 ; Note: use passed-in DL value rather than 7Fh because
#222 ; at least some BIOSes will get the wrong value otherwise
#223 mov ax, 4B01h ; Get disk emulation status
#224 mov dl, [DriveNo]
#225 mov si, spec_packet
#226 int 13h
#227 jc near spec_query_failed ; Shouldn't happen (BIOS bug)
#228 mov dl, [DriveNo]
#229 cmp [sp_drive], dl ; Should contain the drive number
#230 jne near spec_query_failed
#231
#232 %ifdef DEBUG_MESSAGES
#233 mov si, spec_ok_msg
#234 call writemsg
#235 mov al, byte [sp_drive]
#236 call writehex2
#237 call crlf
#238 %endif
#239
发现有驱动器可以使用。
#240 found_drive:
通过中断13 的48H号功能读取驱动器信息。
#241 ; Get drive information
#242 mov ah, 48h
#243 mov dl, [DriveNo]
#244 mov si, drive_params
#245 int 13h
#246 jnc params_ok
#247
#248 ; mov si, nosecsize_msg No use in reporting this
#249 ; call writemsg
#250
#251 params_ok:
#252 ; Check for the sector size (should be 2048, but
#253 ; some BIOSes apparently think we're 512-byte media)
#254 ;
#255 ; FIX: We need to check what the proper behaviour
#256 ; is for getlinsec when the BIOS thinks the sector
#257 ; size is 512!!! For that, we need such a BIOS, though...
#258 %ifdef DEBUG_MESSAGES
#259 mov si, secsize_msg
#260 call writemsg
#261 mov ax, [dp_secsize]
#262 call writehex4
#263 call crlf
#264 %endif
#265
#266
清空文件结构。
#267 ;
#268 ; Clear Files structures
#269 ;
#270 mov di, Files
#271 mov cx, (MAX_OPEN*open_file_t_size)/4
#272 xor eax, eax
#273 rep stosd
#274
#275 ;
#276 ; Now, we need to sniff out the actual filesystem data structures.
#277 ; mkisofs gave us a pointer to the primary volume descriptor
#278 ; (which will be at 16 only for a single-session disk!); from the PVD
#279 ; we should be able to find the rest of what we need to know.
#280 ;
获取一个扇区数据。
#281 get_fs_structures:
#282 mov eax, 16 ; Primary Volume Descriptor (sector 16)
#283 mov bx, trackbuf
#284 call getonesec
#285
#286 mov eax, [trackbuf+156+2]
#287 mov [RootDir+dir_lba],eax
#288 mov [CurDir+dir_lba],eax
#289 %ifdef DEBUG_MESSAGES
#290 mov si, rootloc_msg
#291 call writemsg
#292 call writehex8
#293 call crlf
#294 %endif
#295
#296 mov eax,[trackbuf+156+10]
#297 mov [RootDir+dir_len],eax
#298 mov [CurDir+dir_len],eax
#299 %ifdef DEBUG_MESSAGES
#300 mov si, rootlen_msg
#301 call writemsg
#302 call writehex8
#303 call crlf
#304 %endif
#305 add eax,SECTORSIZE-1
#306 shr eax,SECTORSIZE_LG2
#307 mov [RootDir+dir_clust],eax
#308 mov [CurDir+dir_clust],eax
#309 %ifdef DEBUG_MESSAGES
#310 mov si, rootsect_msg
#311 call writemsg
#312 call writehex8
#313 call crlf
#314 %endif
#315
这里查找根目录里的目录名称“/LOADER”。
#316 ; Look for the "REACTOS" directory, and if found,
#317 ; make it the current directory instead of the root
#318 ; directory.
#319 mov di,isolinux_dir
#320 mov al,02h ; Search for a directory
#321 call searchdir_iso
#322 jnz .dir_found
#323 mov si,no_dir_msg
#324 call writemsg
#325 jmp kaboom
#326
#327 .dir_found:
#328 mov [CurDir+dir_len],eax
#329 mov eax,[si+file_left]
#330 mov [CurDir+dir_clust],eax
#331 xor eax,eax ; Free this file pointer entry
#332 xchg eax,[si+file_sector]
#333 mov [CurDir+dir_lba],eax
#334
#335
这里查找二级引导程序“SETUPLDR.SYS”文件。
#336 mov di, isolinux_bin ; di points to Isolinux filename
#337 call searchdir ; look for the file
#338 jnz .isolinux_opened ; got the file
#339 mov si, no_isolinux_msg ; si points to error message
#340 call writemsg ; display the message
#341 jmp kaboom ; fail boot
#342
#343 .isolinux_opened:
#344 mov di, si ; save file pointer
#345
#346 %ifdef DEBUG_MESSAGES
#347 mov si, filelen_msg
#348 call writemsg
#349 call writehex8
#350 call crlf
#351 %endif
#352
计算扇区的大小。
#353 mov ecx, eax ; calculate sector count
#354 shr ecx, 11
#355 test eax, 0x7FF
#356 jz .full_sector
#357 inc ecx
#358 .full_sector:
#359
#360 %ifdef DEBUG_MESSAGES
#361 mov eax, ecx
#362 mov si, filesect_msg
#363 call writemsg
#364 call writehex8
#365 call crlf
#366 %endif
#367
这里把二级引导程序加载到0x8000位置。
#368 mov bx, 0x8000 ; bx = load address
#369 mov si, di ; restore file pointer
#370 mov cx, 0xFFFF ; load the whole file
#371 call getfssec ; get the whole file
#372
#373 %ifdef DEBUG_MESSAGES
#374 mov si, startldr_msg
#375 call writemsg
#376 call crlf
#377 %endif
#378
#379 mov dl, [DriveNo] ; dl = boot drive
#380 mov dh, 0 ; dh = boot partition
最后跳转到0x8000的位置运行二级引导程序,也就是运行SETUPLDR.SYS文件。
#381 jmp 0:0x8000 ; jump into OSLoader
#382
#383
#384
#385 ;
#386 ; searchdir:
#387 ;
#388 ; Open a file
#389 ;
#390 ; On entry:
#391 ; DS:DI = filename
#392 ; If successful:
#393 ; ZF clear
#394 ; SI = file pointer
#395 ; DX:AX or EAX = file length in bytes
#396 ; If unsuccessful
#397 ; ZF set
#398 ;
#399
#400 ;
#401 ; searchdir_iso is a special entry point for ISOLINUX only. In addition
#402 ; to the above, searchdir_iso passes a file flag mask in AL. This is useful
#403 ; for searching for directories.
#404 ;
#405 alloc_failure:
#406 xor ax,ax ; ZF <- 1
#407 ret
#408
#409 searchdir:
#410 xor al,al
#411 searchdir_iso:
#412 mov [ISOFlags],al
#413 call allocate_file ; Temporary file structure for directory
#414 jnz alloc_failure
#415 push es
#416 push ds
#417 pop es ; ES = DS
#418 mov si,CurDir
#419 cmp byte [di],'/' ; If filename begins with slash
#420 jne .not_rooted
#421 inc di ; Skip leading slash
#422 mov si,RootDir ; Reference root directory instead
#423 .not_rooted:
#424 mov eax,[si+dir_clust]
#425 mov [bx+file_left],eax
#426 mov eax,[si+dir_lba]
#427 mov [bx+file_sector],eax
#428 mov edx,[si+dir_len]
#429
#430 .look_for_slash:
#431 mov ax,di
#432 .scan:
#433 mov cl,[di]
#434 inc di
#435 and cl,cl
#436 jz .isfile
#437 cmp cl,'/'
#438 jne .scan
#439 mov [di-1],byte 0 ; Terminate at directory name
#440 mov cl,02h ; Search for directory
#441 xchg cl,[ISOFlags]
#442 push di
#443 push cx
#444 push word .resume ; Where to "return" to
#445 push es
#446 .isfile:
#447 xchg ax,di
#448
#449 .getsome:
#450 ; Get a chunk of the directory
#451 mov si,trackbuf
#452 pushad
#453 xchg bx,si
#454 mov cx,1 ; load one sector
#455 call getfssec
#456 popad
#457
#458 .compare:
#459 movzx eax, byte [si] ; Length of directory entry
#460 cmp al, 33
#461 jb .next_sector
#462 mov cl, [si+25]
#463 xor cl, [ISOFlags]
#464 test cl, byte 8Eh ; Unwanted file attributes!
#465 jnz .not_file
#466 pusha
#467 movzx cx, byte [si+32] ; File identifier length
#468 add si, byte 33 ; File identifier offset
#469 call iso_compare_names
#470 popa
#471 je .success
#472 .not_file:
#473 sub edx, eax ; Decrease bytes left
#474 jbe .failure
#475 add si, ax ; Advance pointer
#476
#477 .check_overrun:
#478 ; Did we finish the buffer?
#479 cmp si, trackbuf+trackbufsize
#480 jb .compare ; No, keep going
#481
#482 jmp short .getsome ; Get some more directory
#483
#484 .next_sector:
#485 ; Advance to the beginning of next sector
#486 lea ax, [si+SECTORSIZE-1]
#487 and ax, ~(SECTORSIZE-1)
#488 sub ax, si
#489 jmp short .not_file ; We still need to do length checks
#490
#491 .failure:
#492 %ifdef DEBUG_MESSAGES
#493 mov si, findfail_msg
#494 call writemsg
#495 call crlf
#496 %endif
#497 xor eax, eax ; ZF = 1
#498 mov [bx+file_sector], eax
#499 pop es
#500 ret
#501
#502 .success:
#503 mov eax, [si+2] ; Location of extent
#504 mov [bx+file_sector], eax
#505 mov eax, [si+10] ; Data length
#506 push eax
#507 add eax, SECTORSIZE-1
#508 shr eax, SECTORSIZE_LG2
#509 mov [bx+file_left], eax
#510 pop eax
#511 mov edx, eax
#512 shr edx, 16
#513 and bx, bx ; ZF = 0
#514 mov si, bx
#515 pop es
#516 ret
#517
#518 .resume:
#519 ; We get here if we were only doing part of a lookup
#520 ; This relies on the fact that .success returns bx == si
#521 xchg edx, eax ; Directory length in edx
#522 pop cx ; Old ISOFlags
#523 pop di ; Next filename pointer
#524
#525 mov byte [di-1], '/' ; restore the backslash in the filename
#526
#527 mov [ISOFlags], cl ; Restore the flags
#528 jz .failure ; Did we fail? If so fail for real!
#529 jmp .look_for_slash ; Otherwise, next level
#530
#531 ;
#532 ; allocate_file: Allocate a file structure
#533 ;
#534 ; If successful:
#535 ; ZF set
#536 ; BX = file pointer
#537 ; In unsuccessful:
#538 ; ZF clear
#539 ;
#540 allocate_file:
#541 push cx
#542 mov bx, Files
#543 mov cx, MAX_OPEN
#544 .check:
#545 cmp dword [bx], byte 0
#546 je .found
#547 add bx, open_file_t_size ; ZF = 0
#548 loop .check
#549 ; ZF = 0 if we fell out of the loop
#550 .found:
#551 pop cx
#552 ret
#553
#554 ;
#555 ; iso_compare_names:
#556 ; Compare the names DS:SI and DS:DI and report if they are
#557 ; equal from an ISO 9660 perspective. SI is the name from
#558 ; the filesystem; CX indicates its length, and ';' terminates.
#559 ; DI is expected to end with a null.
#560 ;
#561 ; Note: clobbers AX, CX, SI, DI; assumes DS == ES == base segment
#562 ;
#563 iso_compare_names:
#564 ; First, terminate and canonicalize input filename
#565 push di
#566 mov di, ISOFileName
#567 .canon_loop:
#568 jcxz .canon_end
#569 lodsb
#570 dec cx
#571 cmp al, ';'
#572 je .canon_end
#573 and al, al
#574 je .canon_end
#575 stosb
#576 cmp di, ISOFileNameEnd-1 ; Guard against buffer overrun
#577 jb .canon_loop
#578 .canon_end:
#579 cmp di, ISOFileName
#580 jbe .canon_done
#581 cmp byte [di-1], '.' ; Remove terminal dots
#582 jne .canon_done
#583 dec di
#584 jmp short .canon_end
#585 .canon_done:
#586 mov [di], byte 0 ; Null-terminate string
#587 pop di
#588 mov si, ISOFileName
#589 .compare:
#590 lodsb
#591 mov ah, [di]
#592 inc di
#593 and ax, ax
#594 jz .success ; End of string for both
#595 and al, al ; Is either one end of string?
#596 jz .failure ; If so, failure
#597 and ah, ah
#598 jz .failure
#599 or ax, 2020h ; Convert to lower case
#600 cmp al, ah
#601 je .compare
#602 .failure:
#603 and ax, ax ; ZF = 0 (at least one will be nonzero)
#604 .success:
#605 ret
#606
#607
#608
#609
#610
#611
#612
#613 ;
#614 ; getfssec: Get multiple clusters from a file, given the file pointer.
#615 ;
#616 ; On entry:
#617 ; ES:BX -> Buffer
#618 ; SI -> File pointer
#619 ; CX -> Cluster count; 0FFFFh = until end of file
#620 ; On exit:
#621 ; SI -> File pointer (or 0 on EOF)
#622 ; CF = 1 -> Hit EOF
#623 ;
#624 getfssec:
#625 cmp cx, [si+file_left]
#626 jna .ok_size
#627 mov cx, [si+file_left]
#628
#629 .ok_size:
#630 mov bp, cx
#631 push cx
#632 push si
#633 mov eax, [si+file_sector]
#634 call getlinsec
#635 xor ecx, ecx
#636 pop si
#637 pop cx
#638
#639 add [si+file_sector], ecx
#640 sub [si+file_left], ecx
#641 ja .not_eof ; CF = 0
#642
#643 xor ecx, ecx
#644 mov [si+file_sector], ecx ; Mark as unused
#645 xor si,si
#646 stc
#647
#648 .not_eof:
#649 ret
#650
#651
#652
#653 ; INT 13h, AX=4B01h, DL=
#654 ; Try to scan the entire 80h-FFh from the end.
#655 spec_query_failed:
#656 mov si,spec_err_msg
#657 call writemsg
#658
#659 mov dl, 0FFh
#660 .test_loop:
#661 pusha
#662 mov ax, 4B01h
#663 mov si, spec_packet
#664 mov byte [si], 13 ; Size of buffer
#665 int 13h
#666 popa
#667 jc .still_broken
#668
#669 mov si, maybe_msg
#670 call writemsg
#671 mov al, dl
#672 call writehex2
#673 call crlf
#674
#675 cmp byte [sp_drive], dl
#676 jne .maybe_broken
#677
#678 ; Okay, good enough...
#679 mov si, alright_msg
#680 call writemsg
#681 mov [DriveNo], dl
#682 .found_drive:
#683 jmp found_drive
#684
#685 ; Award BIOS 4.51 apparently passes garbage in sp_drive,
#686 ; but if this was the drive number originally passed in
#687 ; DL then consider it "good enough"
#688 .maybe_broken:
#689 cmp byte [DriveNo], dl
#690 je .found_drive
#691
#692 .still_broken:
#693 dec dx
#694 cmp dl, 80h
#695 jnb .test_loop
#696
#697 fatal_error:
#698 mov si, nothing_msg
#699 call writemsg
#700
#701 .norge:
#702 jmp short .norge
#703
#704
#705
#706 ; Information message (DS:SI) output
#707 ; Prefix with "isolinux: "
#708 ;
#709 writemsg:
#710 push ax
#711 push si
#712 mov si, isolinux_str
#713 call writestr
#714 pop si
#715 call writestr
#716 pop ax
#717 ret
#718
#719 ;
#720 ; crlf: Print a newline
#721 ;
#722 crlf:
#723 mov si, crlf_msg
#724 ; Fall through
#725
#726 ;
#727 ; writestr: write a null-terminated string to the console, saving
#728 ; registers on entry.
#729 ;
#730 writestr:
#731 pushfd
#732 pushad
#733 .top:
#734 lodsb
#735 and al, al
#736 jz .end
#737 call writechr
#738 jmp short .top
#739 .end:
#740 popad
#741 popfd
#742 ret
#743
#744
#745 ;
#746 ; writehex[248]: Write a hex number in (AL, AX, EAX) to the console
#747 ;
#748 writehex2:
#749 pushfd
#750 pushad
#751 shl eax, 24
#752 mov cx, 2
#753 jmp short writehex_common
#754 writehex4:
#755 pushfd
#756 pushad
#757 shl eax, 16
#758 mov cx, 4
#759 jmp short writehex_common
#760 writehex8:
#761 pushfd
#762 pushad
#763 mov cx, 8
#764 writehex_common:
#765 .loop:
#766 rol eax, 4
#767 push eax
#768 and al, 0Fh
#769 cmp al, 10
#770 jae .high
#771 .low:
#772 add al, '0'
#773 jmp short .ischar
#774 .high:
#775 add al, 'A'-10
#776 .ischar:
#777 call writechr
#778 pop eax
#779 loop .loop
#780 popad
#781 popfd
#782 ret
#783
#784 ;
#785 ; Write a character to the screen. There is a more "sophisticated"
#786 ; version of this in the subsequent code, so we patch the pointer
#787 ; when appropriate.
#788 ;
#789
#790 writechr:
#791 pushfd
#792 pushad
#793 mov ah, 0Eh
#794 xor bx, bx
#795 int 10h
#796 popad
#797 popfd
#798 ret
#799
#800 ;
#801 ; Get one sector. Convenience entry point.
#802 ;
#803 getonesec:
#804 mov bp, 1
#805 ; Fall through to getlinsec
#806
#807 ;
#808 ; Get linear sectors - EBIOS LBA addressing, 2048-byte sectors.
#809 ;
#810 ; Note that we can't always do this as a single request, because at least
#811 ; Phoenix BIOSes has a 127-sector limit. To be on the safe side, stick
#812 ; to 32 sectors (64K) per request.
#813 ;
#814 ; Input:
#815 ; EAX - Linear sector number
#816 ; ES:BX - Target buffer
#817 ; BP - Sector count
#818 ;
#819 getlinsec:
#820 mov si,dapa ; Load up the DAPA
#821 mov [si+4],bx
#822 mov bx,es
#823 mov [si+6],bx
#824 mov [si+8],eax
#825 .loop2:
#826 push bp ; Sectors left
#827 cmp bp,[MaxTransfer]
#828 jbe .bp_ok
#829 mov bp,[MaxTransfer]
#830 .bp_ok:
#831 mov [si+2],bp
#832 push si
#833 mov dl,[DriveNo]
#834 mov ah,42h ; Extended Read
#835 call xint13
#836 pop si
#837 pop bp
#838 movzx eax,word [si+2] ; Sectors we read
#839 add [si+8],eax ; Advance sector pointer
#840 sub bp,ax ; Sectors left
#841 shl ax,SECTORSIZE_LG2-4 ; 2048-byte sectors -> segment
#842 add [si+6],ax ; Advance buffer pointer
#843 and bp,bp
#844 jnz .loop2
#845 mov eax,[si+8] ; Next sector
#846 ret
#847
#848 ; INT 13h with retry
#849 xint13:
#850 mov byte [RetryCount], retry_count
#851 .try:
#852 pushad
#853 int 13h
#854 jc .error
#855 add sp, byte 8*4 ; Clean up stack
#856 ret
#857 .error:
#858 mov [DiskError], ah ; Save error code
#859 popad
#860 dec byte [RetryCount]
#861 jz .real_error
#862 push ax
#863 mov al,[RetryCount]
#864 mov ah,[dapa+2] ; Sector transfer count
#865 cmp al,2 ; Only 2 attempts left
#866 ja .nodanger
#867 mov ah,1 ; Drop transfer size to 1
#868 jmp short .setsize
#869 .nodanger:
#870 cmp al,retry_count-2
#871 ja .again ; First time, just try again
#872 shr ah,1 ; Otherwise, try to reduce
#873 adc ah,0 ; the max transfer size, but not to 0
#874 .setsize:
#875 mov [MaxTransfer],ah
#876 mov [dapa+2],ah
#877 .again:
#878 pop ax
#879 jmp .try
#880
#881 .real_error:
#882 mov si, diskerr_msg
#883 call writemsg
#884 mov al, [DiskError]
#885 call writehex2
#886 mov si, ondrive_str
#887 call writestr
#888 mov al, dl
#889 call writehex2
#890 call crlf
#891 ; Fall through to kaboom
#892
#893 ;
#894 ; kaboom: write a message and bail out. Wait for a user keypress,
#895 ; then do a hard reboot.
#896 ;
#897 kaboom:
#898 mov ax, cs
#899 mov ds, ax
#900 mov es, ax
#901 mov fs, ax
#902 mov gs, ax
#903 sti
#904 mov si, err_bootfailed
#905 call writestr
#906 call getchar
#907 cli
#908 mov word [BIOS_magic], 0 ; Cold reboot
#909 jmp 0F000h:0FFF0h ; Reset vector address
#910
#911 getchar:
#912 .again:
#913 mov ah, 1 ; Poll keyboard
#914 int 16h
#915 jz .again
#916 .kbd:
#917 xor ax, ax ; Get keyboard input
#918 int 16h
#919 .func_key:
#920 ret
#921
#922
#923 ;
#924 ; pollchar: check if we have an input character pending (ZF = 0)
#925 ;
#926 pollchar:
#927 pushad
#928 mov ah,1 ; Poll keyboard
#929 int 16h
#930 popad
#931 ret
#932
#933
#934
#935 isolinux_banner db CR, LF, 'Loading IsoBoot...', CR, LF, 0
#936 copyright_str db ' Copyright (C) 1994-2002 H. Peter Anvin', CR, LF, 0
#937 presskey_msg db 'Press any key to boot from CD', 0
#938 dot_msg db '.',0
#939
#940 %ifdef DEBUG_MESSAGES
#941 startup_msg: db 'Starting up, DL = ', 0
#942 spec_ok_msg: db 'Loaded spec packet OK, drive = ', 0
#943 secsize_msg: db 'Sector size appears to be ', 0
#944 rootloc_msg: db 'Root directory location: ', 0
#945 rootlen_msg: db 'Root directory length: ', 0
#946 rootsect_msg: db 'Root directory length(sectors): ', 0
#947 fileloc_msg: db 'SETUPLDR.SYS location: ', 0
#948 filelen_msg: db 'SETUPLDR.SYS length: ', 0
#949 filesect_msg: db 'SETUPLDR.SYS length(sectors): ', 0
#950 findfail_msg: db 'Failed to find file!', 0
#951 startldr_msg: db 'Starting SETUPLDR.SYS', 0
#952 %endif
#953
#954 nosecsize_msg: db 'Failed to get sector size, assuming 0800', CR, LF, 0
#955 spec_err_msg: db 'Loading spec packet failed, trying to wing it...', CR, LF, 0
#956 maybe_msg: db 'Found something at drive = ', 0
#957 alright_msg: db 'Looks like it might be right, continuing...', CR, LF, 0
#958 nothing_msg: db 'Failed to locate CD-ROM device; boot failed.', CR, LF, 0
#959 isolinux_str db 'IsoBoot: ', 0
#960 crlf_msg db CR, LF, 0
#961 diskerr_msg: db 'Disk error ', 0
#962 ondrive_str: db ', drive ', 0
#963 err_bootfailed db CR, LF, 'Boot failed: press a key to retry...'
#964 isolinux_dir db '/LOADER', 0
#965 no_dir_msg db 'Could not find the LOADER directory.', CR, LF, 0
#966 isolinux_bin db 'SETUPLDR.SYS', 0
#967 no_isolinux_msg db 'Could not find SETUPLDR.SYS.', CR, LF, 0
#968
#969 ;
#970 ; El Torito spec packet
#971 ;
#972 align 8, db 0
#973 spec_packet: db 13h ; Size of packet
#974 sp_media: db 0 ; Media type
#975 sp_drive: db 0 ; Drive number
#976 sp_controller: db 0 ; Controller index
#977 sp_lba: dd 0 ; LBA for emulated disk image
#978 sp_devspec: dw 0 ; IDE/SCSI information
#979 sp_buffer: dw 0 ; User-provided buffer
#980 sp_loadseg: dw 0 ; Load segment
#981 sp_sectors: dw 0 ; Sector count
#982 sp_chs: db 0,0,0 ; Simulated CHS geometry
#983 sp_dummy: db 0 ; Scratch, safe to overwrite
#984
#985 ;
#986 ; EBIOS drive parameter packet
#987 ;
#988 align 8, db 0
#989 drive_params: dw 30 ; Buffer size
#990 dp_flags: dw 0 ; Information flags
#991 dp_cyl: dd 0 ; Physical cylinders
#992 dp_head: dd 0 ; Physical heads
#993 dp_sec: dd 0 ; Physical sectors/track
#994 dp_totalsec: dd 0,0 ; Total sectors
#995 dp_secsize: dw 0 ; Bytes per sector
#996 dp_dpte: dd 0 ; Device Parameter Table
#997 dp_dpi_key: dw 0 ; 0BEDDh if rest valid
#998 dp_dpi_len: db 0 ; DPI len
#999 db 0
#1000 dw 0
#1001 dp_bus: times 4 db 0 ; Host bus type
#1002 dp_interface: times 8 db 0 ; Interface type
#1003 db_i_path: dd 0,0 ; Interface path
#1004 db_d_path: dd 0,0 ; Device path
#1005 db 0
#1006 db_dpi_csum: db 0 ; Checksum for DPI info
#1007
#1008 ;
#1009 ; EBIOS disk address packet
#1010 ;
#1011 align 8, db 0
#1012 dapa: dw 16 ; Packet size
#1013 .count: dw 0 ; Block count
#1014 .off: dw 0 ; Offset of buffer
#1015 .seg: dw 0 ; Segment of buffer
#1016 .lba: dd 0 ; LBA (LSW)
#1017 dd 0 ; LBA (MSW)
#1018
#1019 alignb 4, db 0
#1020 MaxTransfer dw 2 ;32 ; Max sectors per transfer
#1021
#1022 times 2046-($-$$) db 0 ; Pad to file offset 2046
#1023 dw 0aa55h ; BootSector signature
#1024
上面分析了光盘引导程序,知道怎么跳转到二级引导程序工作。也就是加载SETUPLDR.SYS文件到内存,然后跳到相应位置运行。最后加载内核ntoskrnl.exe运行。