光盘启动 (Boot from CDROM) Part 2- SakiProject
2014-07-08 12:56
423 查看
继续上1P,我们现在要开始读取光驱里面的内容了。以前我描述过BIOS显示服务和磁盘服务(这里),但是我只将了CHS读软盘,没有涉及到硬盘和光驱。由于硬盘和光驱的构造与软盘截然不同,使用读软盘的手段是行不通的,为此,BIOS给我们提供了扩展13h中断。
Continue to the next part, we are going to read the content in the cdrom. I described the bios display service and disk service in previous posts (Here,
in Chinese), but I only introduced the way to read floppy through CHS method, but not harddisk and cdrom. Because the hardware construction of these devices are totally different, we cannot apply the floppy-way to harddisk(ATA) and cdrom(ATAPI) devices.
In order to solve the problem, BIOS provides us extended int 13h.
参数和读软盘不一样了。我们使用Disk Address Packet(DAP, 磁盘地址包)来间接传递参数,而不是传CHS信息。DAP的构造如下表:
The argument is different from the normal read function. We no longer pass the CHS arguments to the BIOS, but a Disk Address Packet(DAP) instead. The format of DAP is described by the following table:
有了这两张表,工作就变得轻松了,我们用这段代码来描述DAP:
With the help with these two tables, it's pretty easy to implement it. We use the following code to describe DAP:
readSector函数的实现也会很轻松。我用了Javadoc样式的注释,这样可以让别人读起来易于理解逻辑和寄存器的关系。
And we can implement the readSector function easily. Notice that I use javadoc styled comments, in order to keep the code organized and easy to understand by others.
这段代码从EDI里的物理地址计算了段:偏移格式的地址,然后还用到了上次保存的driveNum。
The code segment uses the physical address stored in edi to calculate segment:offset style address, and make use of the driveNum variable stored last time.
万事具备,接下来就要开始分析光盘了。光盘的文件系统是ISO 9660,这个文件系统的标准可以在ECMA的官网免费下到。
Getting everything prepared, we are going to analyze the cdrom. The file system of cdrom is ISO 9660, the specification can be found on the official site of ECMA (Here).
ISO 9660(又名CDFS)开始是32KB的空余空间,然后是一些卷描述符。一般来说,CD的扇区大小是2KB,所以第一个卷描述符应该在第16扇区。这个描述符很特殊,它是主描述符,有了这个描述符我们就可以逐步揭开CD的面纱。
ISO 9660 (Also called CD File System, CDFS) starts with 32KB free space, then a few volume descriptors. Normally, a sector in CD is 2KB-big, so the first volume descriptor is the 16th sector. This descriptor is very special,
it's the primary one, and we can unseal the CD file system.
在主描述符中,我摘出了一些我们感兴趣的字段:
I picked some interesting fields in the primary volume descriptor.
我们有两种方法找到一个文件,我们可以现在Path Table(路径表)中找到这个文件所在的目录,然后到这个目录里去找,我们也可以直接从根目录直接一层一层找下去。反正都需要从目录里面找文件的代码,我们还不如一层一层找,这样可以增加代码的共用。顺带一提,路径表是有大小限制的,所以只能存放最多65536个目录,但是从根目录找是没有任何限制的。Windows是用路径表的,而Linux是层级寻找的,所以有些Linux能用的光盘Windows读不出来,而Windows能用的光盘Linux都能用。(这是不是解释了为什么Windows慢>_<)
We have two ways to locate a file. We can either locate the parent directory of the file and find it in path table, then find the file in that directory, or we can just recursively find the file/directory from the root directory.
In either way we need code to find a file inside a directory, so we can simply use the second method to shorten code and increase efficiency. By the way, the size of path table is limited, and it can only store up to 65536 directories, but the latter method
has no restrictions. Windows use the former method and Linux use the latter method, so every cd readable in Windows can be read by Linux, but some CDs are not readable by Windows. (Maybe it proved why Windows is so slow >_<)
我们先加载主卷描述符,然后分析目录项(根目录项在BUFFER+156):
We first load the descriptor into the buffer, then we can analyze the directory record (note that the root record is at BUFFER+156):
我写了上面这个函数,然后可以edi=BUFFER+156调用这个函数加载ROOT目录。对于目录来说,文件内容就是连续分布的目录项,所以我们可以比较目录项里的文件名来找到文件的地址。
The function can be called using edi=BUFFER+156 to load the root directory. For directories, file content is several continuous distributed directory records, so we can compare file names in records to find the LBA address of
file.
上面的代码就是分析了目录项,如果不好理解的话可以看下面的伪代码:
The above code analysis the directory records, if it's hard to understand, you can see the following pseudo-code:
通过调用这些函数,我们可以逐级找文件。要注意的是,非目录文件会以;1结尾,而且文件名都是大写,所以我们要加载的文件其实是"/SAKI/BOOTMGR/BOOT.TXT;1"
By calling these functions, we can code the rest, which is recursively find files. Notice that files (not directories) will look like FILENAME;1, and all file names should be uppercase, so the path is actually "/SAKI/BOOTMGR/BOOT.TXT;1".
After we find the file, we can load the file to memory and display its content.
好了,现在我们运行一下看看,看看我们能不能成功显示出文件里的内容吧!
Okay, let's run the program to see whether we succeeded or not!
![](http://img.blog.csdn.net/20140708125018640?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbmJkZDAxMjE=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
真赞!引导扇区的任务就到此为止了,接下来的任务就交给Bootmgr了。在我的设计中,Bootmgr要负责切换到保护模式(或长模式)并且加载操作系统本体。下次开发Bootmgr应该就不止2P了。
Wonderful! This is the end of a bootsect, when a bootsect loaded bootmgr, it will give control to bootmgr. In my design, bootmgr will switch to protected mode and load the real operating system. The next series (developing bootmgr
will be more than two parts I think)
Continue to the next part, we are going to read the content in the cdrom. I described the bios display service and disk service in previous posts (Here,
in Chinese), but I only introduced the way to read floppy through CHS method, but not harddisk and cdrom. Because the hardware construction of these devices are totally different, we cannot apply the floppy-way to harddisk(ATA) and cdrom(ATAPI) devices.
In order to solve the problem, BIOS provides us extended int 13h.
Register | Value | Description |
AH | Function(0x42) | Extended Read from ATA/ATAPI Device |
DL | Drive Number | |
DS:SI | Address of Disk Address Packet |
The argument is different from the normal read function. We no longer pass the CHS arguments to the BIOS, but a Disk Address Packet(DAP) instead. The format of DAP is described by the following table:
Offset | Size | Name | Description |
0 | 1 | Packet Size | Always 16 (At least now) |
1 | 1 | Reserved | Should be zero |
2 | 2 | Sector Count | Number of sectors to read |
4 | 4 | Buffer Address | Format is segment:offset, a word-long offset is stored first, then a word-long segment. |
8 | 8 | LBA | The physical address of the first sector to read. |
With the help with these two tables, it's pretty easy to implement it. We use the following code to describe DAP:
; Disk Address Packet(DAP), argument for extended 13h DAP: .packetSize db 16, 0 .sectCount dw 0 .bufferOff dw 0 .bufferSeg dw 0 .lbaLow dd 0 .lbaHigh dd 0
readSector函数的实现也会很轻松。我用了Javadoc样式的注释,这样可以让别人读起来易于理解逻辑和寄存器的关系。
And we can implement the readSector function easily. Notice that I use javadoc styled comments, in order to keep the code organized and easy to understand by others.
; read a sector from the boot device ; @param eax lba address of the first sector to read ; @param cx number of sectors ; @param edi buffer to store read sectors readSector: mov [DAP.lbaLow], eax mov [DAP.sectCount], cx mov eax, edi shr eax, 4 ; Calculate the segment from absolute address mov [DAP.bufferSeg] ,ax and di, 0xf ; Calculate the offset from absolute address mov [DAP.bufferOff], di mov ah, 0x42 mov dl, [driveNum] mov si, DAP; int 13h ret ;end readSector
这段代码从EDI里的物理地址计算了段:偏移格式的地址,然后还用到了上次保存的driveNum。
The code segment uses the physical address stored in edi to calculate segment:offset style address, and make use of the driveNum variable stored last time.
万事具备,接下来就要开始分析光盘了。光盘的文件系统是ISO 9660,这个文件系统的标准可以在ECMA的官网免费下到。
Getting everything prepared, we are going to analyze the cdrom. The file system of cdrom is ISO 9660, the specification can be found on the official site of ECMA (Here).
ISO 9660(又名CDFS)开始是32KB的空余空间,然后是一些卷描述符。一般来说,CD的扇区大小是2KB,所以第一个卷描述符应该在第16扇区。这个描述符很特殊,它是主描述符,有了这个描述符我们就可以逐步揭开CD的面纱。
ISO 9660 (Also called CD File System, CDFS) starts with 32KB free space, then a few volume descriptors. Normally, a sector in CD is 2KB-big, so the first volume descriptor is the 16th sector. This descriptor is very special,
it's the primary one, and we can unseal the CD file system.
在主描述符中,我摘出了一些我们感兴趣的字段:
I picked some interesting fields in the primary volume descriptor.
Offset | Size | Name | Description |
0 | 1 | Type Code | Always 1, indicating that the sector is a primary volume descriptor. |
140 | 4 | Path Table(Little endian) | The LBA location of path table. |
156 | 34 | Directory Entry of root | A directory structure, which will be mentioned below. |
We have two ways to locate a file. We can either locate the parent directory of the file and find it in path table, then find the file in that directory, or we can just recursively find the file/directory from the root directory.
In either way we need code to find a file inside a directory, so we can simply use the second method to shorten code and increase efficiency. By the way, the size of path table is limited, and it can only store up to 65536 directories, but the latter method
has no restrictions. Windows use the former method and Linux use the latter method, so every cd readable in Windows can be read by Linux, but some CDs are not readable by Windows. (Maybe it proved why Windows is so slow >_<)
PRIM_DESC_LBA equ 16 BUFFER equ 0x600 DESC_TYPE_CODE equ BUFFER DESC_ROOT_DIR equ BUFFER+156 mov eax, PRIM_DESC_LBA mov cx, 1 mov di, BUFFER call readSector
我们先加载主卷描述符,然后分析目录项(根目录项在BUFFER+156):
We first load the descriptor into the buffer, then we can analyze the directory record (note that the root record is at BUFFER+156):
Offset | Size | Description |
0 | 1 | Length of Directory Record |
2 | 4 | LBA of file contents |
10 | 4 | Data length |
32 | 1 | Length of file name |
33 | Variable | File identifier |
; Load directory table into memory ; @param edi directory entry ; @destory eax, ecx, edi loadDirTable: mov eax, [edi+2] mov cx, 1 mov di, BUFFER call readSector ret ;end loadDirTable
我写了上面这个函数,然后可以edi=BUFFER+156调用这个函数加载ROOT目录。对于目录来说,文件内容就是连续分布的目录项,所以我们可以比较目录项里的文件名来找到文件的地址。
The function can be called using edi=BUFFER+156 to load the root directory. For directories, file content is several continuous distributed directory records, so we can compare file names in records to find the LBA address of
file.
; Search for file in directory at BUFFER ; @param esi filename ; @destory eax, ecx, edx, edi searchForFile: call strlen ; Get length of filename mov edi, BUFFER .loop: movzx eax, byte[edi] ; Get size of record or eax, eax ; If zero, no more record is available jz .err movzx ecx, byte[edi+32] ; Get length of actual filename cmp ecx, edx ; If not equal, next record jnz .cont push esi push edi add edi, 33 ; Set edi to the actual filename repe cmpsb pop edi pop esi jz .ret .cont: add edi, eax ; Point to the next record jmp .loop .ret: ret .err: call print mov si, fileNotFound call print cli hlt ;end searchForFile ; Find length of given string ; @param si target string ; @return edx length of string ; @destory eax, edx, ecx, edi strlen: mov di, si or cx, -1 ; -1 means that the function will never stop xor ax, ax ; Clear eax to 0 repne scasb ; Compare until [esi] to 0 neg cx ; Since cx=-1-(len+1), we change the cx to 1+(len+1) dec cx dec cx movzx edx, cx ret ;end strlen fileNotFound db " does not exist in cdrom.",0
上面的代码就是分析了目录项,如果不好理解的话可以看下面的伪代码:
The above code analysis the directory records, if it's hard to understand, you can see the following pseudo-code:
void searchForFile(char* filename){ int len=strlen(filename); void* record=BUFFER; while(1){ int recordLen=*(unsigned char*)record; assert(recordLen); int nameLen=*(unsigned char*)(record+32); if(nameLen==len&&strcmp((char*)(record+33), filename)==0){ return record; } record+=recordLen; } }
通过调用这些函数,我们可以逐级找文件。要注意的是,非目录文件会以;1结尾,而且文件名都是大写,所以我们要加载的文件其实是"/SAKI/BOOTMGR/BOOT.TXT;1"
By calling these functions, we can code the rest, which is recursively find files. Notice that files (not directories) will look like FILENAME;1, and all file names should be uppercase, so the path is actually "/SAKI/BOOTMGR/BOOT.TXT;1".
After we find the file, we can load the file to memory and display its content.
; Load the "/" mov edi, DESC_ROOT_DIR call loadDirTable ; Search for "SAKI" mov esi, dirNameSaki call searchForFile ; Load "/SAKI" call loadDirTable ; Search for "BOOTMGR" mov esi, dirNameBootmgr call searchForFile ; Load "/SAKI/BOOTMGR" call loadDirTable ; Search for "BOOT.TXT;1" mov esi, dirNameBooter call searchForFile ; Load "/SAKI/BOOTMGR/BOOT.TXT;1" mov eax, [edi+2] ; LBA Address mov ecx, [edi+10] ; Size of file add ecx, 2048 shr ecx, 11 ; Change unit from byte to sector mov di, BUFFER call readSector ; Print information mov esi, BUFFER call print cli hlt dirNameSaki db "SAKI",0 dirNameBootmgr db "BOOTMGR",0 dirNameBooter db "BOOT.TXT;1",0
好了,现在我们运行一下看看,看看我们能不能成功显示出文件里的内容吧!
Okay, let's run the program to see whether we succeeded or not!
真赞!引导扇区的任务就到此为止了,接下来的任务就交给Bootmgr了。在我的设计中,Bootmgr要负责切换到保护模式(或长模式)并且加载操作系统本体。下次开发Bootmgr应该就不止2P了。
Wonderful! This is the end of a bootsect, when a bootsect loaded bootmgr, it will give control to bootmgr. In my design, bootmgr will switch to protected mode and load the real operating system. The next series (developing bootmgr
will be more than two parts I think)
相关文章推荐
- 光盘启动 (Boot from CDROM) Part 1- SakiProject
- 光盘启动 (Boot from CDROM) Part 2- SakiProject
- 光盘启动 (Boot from CDROM) Part 1- SakiProject
- bootloader的启动 from rom code to uboot
- Howto Setup yum repositories to update or install package from ISO CDROM Image
- imx51 ROM boot code 启动分析 .
- U盘安装Ubuntu 12.04 Server版提示“Faild to copy file from CD-ROM”的解决办法
- VMware虚拟机安装XP【要先分区,再设置BOOT 启动CD,shif+上移】
- Eclipse编程技术与实例(附CD-ROM光盘一张)
- vxWorks/BootROM Imageq启动顺序详解
- 固件升级 android启动 uboot启动 rom制作
- imx51 ROM boot code 启动分析
- linux /boot目录丢失后使用启动光盘修复
- vxWorks/BootROM Imageq启动顺序详解
- LINQ Introduction Part 1 Of 3 By Sacha Barber(Refer from codeproject)
- Linux 系统启动过程(initrd部分) --- Linux boot process (initrd part)
- 一本需要购买的图形学方面的好书:GPU精粹——实时图形编程的技术、技巧和技艺(附CD-ROM光盘一张)
- Win PE CD-ROM 制作简介(系统修复光盘)
- Howto Setup yum repositories to update or install package from ISO CDROM Image
- Howto Setup yum repositories to update or install package from ISO CDROM Image