主引导程序控制权转移
这一节,我们来真正的读取文件中的内容到内存中,首先来看一下内存布局是什么样的,如下所示:
Boot占用了512字节,Fat Table占用了4KB,而真正的文件中的内容,我们把它存在0x9000开始的内存地址处。
加载文件内容的过程如下:
实验步骤如下:
1、在虚拟软盘中创建体积较大的文本文件,使之内容大小超过一个扇区。
2、将文件的内容加载到BaseOfLoader地址处。
3、打印加载的内容,判断是否加载完全。
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 BaseOfLoader equ 0x9000 9 RootEntryOffset equ 19 10 RootEntryLength equ 14 11 EntryItemLength equ 32 12 FatEntryOffset equ 1 13 FatEntryLength equ 9 14 15 header: 16 BS_OEMName db "D.T.Soft" 17 BPB_BytsPerSec dw 512 18 BPB_SecPerClus db 1 19 BPB_RsvdSecCnt dw 1 20 BPB_NumFATs db 2 21 BPB_RootEntCnt dw 224 22 BPB_TotSec16 dw 2880 23 BPB_Media db 0xF0 24 BPB_FATSz16 dw 9 25 BPB_SecPerTrk dw 18 26 BPB_NumHeads dw 2 27 BPB_HiddSec dd 0 28 BPB_TotSec32 dd 0 29 BS_DrvNum db 0 30 BS_Reserved1 db 0 31 BS_BootSig db 0x29 32 BS_VolID dd 0 33 BS_VolLab db "D.T.OS-0.01" 34 BS_FileSysType db "FAT12 " 35 36 start: 37 mov ax, cs 38 mov ss, ax 39 mov ds, ax 40 mov es, ax 41 mov sp, BaseOfStack 42 43 mov ax, RootEntryOffset 44 mov cx, RootEntryLength 45 mov bx, Buf 46 47 call ReadSector 48 49 mov si, Target 50 mov cx, TarLen 51 mov dx, 0 52 53 call FindEntry 54 55 cmp dx, 0 56 jz output 57 58 mov si, bx 59 mov di, EntryItem 60 mov cx, EntryItemLength 61 62 call MemCpy 63 64 mov ax, FatEntryLength 65 mov cx, [BPB_BytsPerSec] 66 mul cx 67 mov bx, BaseOfLoader 68 sub bx, ax 69 70 mov ax, FatEntryOffset 71 mov cx, FatEntryLength 72 73 call ReadSector 74 75 mov dx, [EntryItem + 0x1A] 76 mov si, BaseOfLoader 77 78 loading: 79 mov ax, dx 80 add ax, 31 81 mov cx, 1 82 push dx 83 push bx 84 mov bx, si 85 call ReadSector 86 pop bx 87 pop cx 88 call FatVec 89 cmp dx, 0xFF7 90 jnb output 91 add si, 512 92 jmp loading 93 94 output: 95 mov bp, BaseOfLoader 96 mov cx, [EntryItem + 0x1C] 97 call Print 98 99 last: 100 hlt 101 jmp last 102 103 104 ; cx --> index 105 ; bx --> fat table address 106 ; 107 ; return: 108 ; dx --> fat[index] 109 FatVec: 110 mov ax, cx 111 mov cl, 2 112 div cl 113 114 push ax 115 116 mov ah, 0 117 mov cx, 3 118 mul cx 119 mov cx, ax 120 121 pop ax 122 123 cmp ah, 0 124 jz even 125 jmp odd 126 127 even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; 128 mov dx, cx 129 add dx, 1 130 add dx, bx 131 mov bp, dx 132 mov dl, byte [bp] 133 and dl, 0x0F 134 shl dx, 8 135 add cx, bx 136 mov bp, cx 137 or dl, byte [bp] 138 jmp return 139 140 odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 141 mov dx, cx 142 add dx, 2 143 add dx, bx 144 mov bp, dx 145 mov dl, byte [bp] 146 mov dh, 0 147 shl dx, 4 148 add cx, 1 149 add cx, bx 150 mov bp, cx 151 mov cl, byte [bp] 152 shr cl, 4 153 and cl, 0x0F 154 mov ch, 0 155 or dx, cx 156 157 return: 158 ret 159 160 ; ds:si --> source 161 ; es:di --> destination 162 ; cx --> length 163 MemCpy: 164 165 cmp si, di 166 167 ja btoe 168 169 add si, cx 170 add di, cx 171 dec si 172 dec di 173 174 jmp etob 175 176 btoe: 177 cmp cx, 0 178 jz done 179 mov al, [si] 180 mov byte [di], al 181 inc si 182 inc di 183 dec cx 184 jmp btoe 185 186 etob: 187 cmp cx, 0 188 jz done 189 mov al, [si] 190 mov byte [di], al 191 dec si 192 dec di 193 dec cx 194 jmp etob 195 196 done: 197 ret 198 199 ; es:bx --> root entry offset address 200 ; ds:si --> target string 201 ; cx --> target length 202 ; 203 ; return: 204 ; (dx !=0 ) ? exist : noexist 205 ; exist --> bx is the target entry 206 FindEntry: 207 push cx 208 209 mov dx, [BPB_RootEntCnt] 210 mov bp, sp 211 212 find: 213 cmp dx, 0 214 jz noexist 215 mov di, bx 216 mov cx, [bp] 217 push si 218 call MemCmp 219 pop si 220 cmp cx, 0 221 jz exist 222 add bx, 32 223 dec dx 224 jmp find 225 226 exist: 227 noexist: 228 pop cx 229 230 ret 231 232 ; ds:si --> source 233 ; es:di --> destination 234 ; cx --> length 235 ; 236 ; return: 237 ; (cx == 0) ? equal : noequal 238 MemCmp: 239 240 compare: 241 cmp cx, 0 242 jz equal 243 mov al, [si] 244 cmp al, byte [di] 245 jz goon 246 jmp noequal 247 goon: 248 inc si 249 inc di 250 dec cx 251 jmp compare 252 253 equal: 254 noequal: 255 256 ret 257 258 ; es:bp --> string address 259 ; cx --> string length 260 Print: 261 mov dx, 0 262 mov ax, 0x1301 263 mov bx, 0x0007 264 int 0x10 265 ret 266 267 ; no parameter 268 ResetFloppy: 269 270 mov ah, 0x00 271 mov dl, [BS_DrvNum] 272 int 0x13 273 274 ret 275 276 ; ax --> logic sector number 277 ; cx --> number of sector 278 ; es:bx --> target address 279 ReadSector: 280 281 call ResetFloppy 282 283 push bx 284 push cx 285 286 mov bl, [BPB_SecPerTrk] 287 div bl 288 mov cl, ah 289 add cl, 1 290 mov ch, al 291 shr ch, 1 292 mov dh, al 293 and dh, 1 294 mov dl, [BS_DrvNum] 295 296 pop ax 297 pop bx 298 299 mov ah, 0x02 300 301 read: 302 int 0x13 303 jc read 304 305 ret 306 307 MsgStr db "No LOADER ..." 308 MsgLen equ ($-MsgStr) 309 Target db "START " 310 TarLen equ ($-Target) 311 EntryItem times EntryItemLength db 0x00 312 Buf: 313 times 510-($-$$) db 0x00 314 db 0x55, 0xaa
95行跳转到加载文件内容的地址处,96行为文件的长度。运行bochs,可以看到文件内容全部打印出来了,结果如下:
以上我们加载的文件只是文本内容,下面我们在文件中写入程序,并编译成可执行程序,然后将这个可执行程序文件放到虚拟软盘中,并加载到指定地址,然后跳转到这个地址执行。
待加载文件中的内容如下:
1 org 0x9000 2 3 begin: 4 mov si, msg 5 6 print: 7 mov al, [si] 8 add si, 1 9 cmp al, 0x00 10 je end 11 mov ah, 0x0E 12 mov bx, 0x0F 13 int 0x10 14 jmp print 15 16 end: 17 hlt 18 jmp end 19 20 msg: 21 db 0x0a, 0x0a 22 db "Hello, D.T.OS!" 23 db 0x0a, 0x0a 24 db 0x00
为了方便起见,我们修改makefile,如下:
1 .PHONY : all clean rebuild 2 3 BOOT_SRC := boot.asm 4 BOOT_OUT := boot 5 6 LOADER_SRC := loader.asm 7 LOADER_OUT := loader 8 9 IMG := a.img 10 IMG_PATH := /mnt/hgfs 11 12 RM := rm -fr 13 14 all : $(IMG) $(BOOT_OUT) $(LOADER_OUT) 15 @echo "Build Success ==> D.T.OS!" 16 17 $(IMG) : 18 bximage $@ -q -fd -size=1.44 19 20 $(BOOT_OUT) : $(BOOT_SRC) 21 nasm $^ -o $@ 22 dd if=$@ of=$(IMG) bs=512 count=1 conv=notrunc 23 24 $(LOADER_OUT) : $(LOADER_SRC) 25 nasm $^ -o $@ 26 sudo mount -o loop $(IMG) $(IMG_PATH) 27 sudo cp $@ $(IMG_PATH)/$@ 28 sudo umount $(IMG_PATH) 29 30 clean : 31 $(RM) $(IMG) $(BOOT_OUT) $(LOADER_OUT) 32 33 rebuild : 34 @$(MAKE) clean 35 @$(MAKE) all
makefile完成编译boot.asm,编译loader.asm,写入boot.bin到第一个扇区,挂载a.img到/mnt/hgfs目录,写入loader可执行文件,卸载a.img等。
我们修改boot.asm,程序如下:
1 org 0x7c00 2 3 jmp short start 4 nop 5 6 define: 7 BaseOfStack equ 0x7c00 8 BaseOfLoader equ 0x9000 9 RootEntryOffset equ 19 10 RootEntryLength equ 14 11 EntryItemLength equ 32 12 FatEntryOffset equ 1 13 FatEntryLength equ 9 14 15 header: 16 BS_OEMName db "D.T.Soft" 17 BPB_BytsPerSec dw 512 18 BPB_SecPerClus db 1 19 BPB_RsvdSecCnt dw 1 20 BPB_NumFATs db 2 21 BPB_RootEntCnt dw 224 22 BPB_TotSec16 dw 2880 23 BPB_Media db 0xF0 24 BPB_FATSz16 dw 9 25 BPB_SecPerTrk dw 18 26 BPB_NumHeads dw 2 27 BPB_HiddSec dd 0 28 BPB_TotSec32 dd 0 29 BS_DrvNum db 0 30 BS_Reserved1 db 0 31 BS_BootSig db 0x29 32 BS_VolID dd 0 33 BS_VolLab db "D.T.OS-0.01" 34 BS_FileSysType db "FAT12 " 35 36 start: 37 mov ax, cs 38 mov ss, ax 39 mov ds, ax 40 mov es, ax 41 mov sp, BaseOfStack 42 43 mov ax, RootEntryOffset 44 mov cx, RootEntryLength 45 mov bx, Buf 46 47 call ReadSector 48 49 mov si, Target 50 mov cx, TarLen 51 mov dx, 0 52 53 call FindEntry 54 55 cmp dx, 0 56 jz output 57 58 mov si, bx 59 mov di, EntryItem 60 mov cx, EntryItemLength 61 62 call MemCpy 63 64 mov ax, FatEntryLength 65 mov cx, [BPB_BytsPerSec] 66 mul cx 67 mov bx, BaseOfLoader 68 sub bx, ax 69 70 mov ax, FatEntryOffset 71 mov cx, FatEntryLength 72 73 call ReadSector 74 75 mov dx, [EntryItem + 0x1A] 76 mov si, BaseOfLoader 77 78 loading: 79 mov ax, dx 80 add ax, 31 81 mov cx, 1 82 push dx 83 push bx 84 mov bx, si 85 call ReadSector 86 pop bx 87 pop cx 88 call FatVec 89 cmp dx, 0xFF7 90 jnb BaseOfLoader 91 add si, 512 92 jmp loading 93 94 output: 95 mov bp, MsgStr 96 mov cx, MsgLen 97 call Print 98 99 last: 100 hlt 101 jmp last 102 103 104 ; cx --> index 105 ; bx --> fat table address 106 ; 107 ; return: 108 ; dx --> fat[index] 109 FatVec: 110 mov ax, cx 111 mov cl, 2 112 div cl 113 114 push ax 115 116 mov ah, 0 117 mov cx, 3 118 mul cx 119 mov cx, ax 120 121 pop ax 122 123 cmp ah, 0 124 jz even 125 jmp odd 126 127 even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; 128 mov dx, cx 129 add dx, 1 130 add dx, bx 131 mov bp, dx 132 mov dl, byte [bp] 133 and dl, 0x0F 134 shl dx, 8 135 add cx, bx 136 mov bp, cx 137 or dl, byte [bp] 138 jmp return 139 140 odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); 141 mov dx, cx 142 add dx, 2 143 add dx, bx 144 mov bp, dx 145 mov dl, byte [bp] 146 mov dh, 0 147 shl dx, 4 148 add cx, 1 149 add cx, bx 150 mov bp, cx 151 mov cl, byte [bp] 152 shr cl, 4 153 and cl, 0x0F 154 mov ch, 0 155 or dx, cx 156 157 return: 158 ret 159 160 ; ds:si --> source 161 ; es:di --> destination 162 ; cx --> length 163 MemCpy: 164 165 cmp si, di 166 167 ja btoe 168 169 add si, cx 170 add di, cx 171 dec si 172 dec di 173 174 jmp etob 175 176 btoe: 177 cmp cx, 0 178 jz done 179 mov al, [si] 180 mov byte [di], al 181 inc si 182 inc di 183 dec cx 184 jmp btoe 185 186 etob: 187 cmp cx, 0 188 jz done 189 mov al, [si] 190 mov byte [di], al 191 dec si 192 dec di 193 dec cx 194 jmp etob 195 196 done: 197 ret 198 199 ; es:bx --> root entry offset address 200 ; ds:si --> target string 201 ; cx --> target length 202 ; 203 ; return: 204 ; (dx !=0 ) ? exist : noexist 205 ; exist --> bx is the target entry 206 FindEntry: 207 push cx 208 209 mov dx, [BPB_RootEntCnt] 210 mov bp, sp 211 212 find: 213 cmp dx, 0 214 jz noexist 215 mov di, bx 216 mov cx, [bp] 217 push si 218 call MemCmp 219 pop si 220 cmp cx, 0 221 jz exist 222 add bx, 32 223 dec dx 224 jmp find 225 226 exist: 227 noexist: 228 pop cx 229 230 ret 231 232 ; ds:si --> source 233 ; es:di --> destination 234 ; cx --> length 235 ; 236 ; return: 237 ; (cx == 0) ? equal : noequal 238 MemCmp: 239 240 compare: 241 cmp cx, 0 242 jz equal 243 mov al, [si] 244 cmp al, byte [di] 245 jz goon 246 jmp noequal 247 goon: 248 inc si 249 inc di 250 dec cx 251 jmp compare 252 253 equal: 254 noequal: 255 256 ret 257 258 ; es:bp --> string address 259 ; cx --> string length 260 Print: 261 mov dx, 0 262 mov ax, 0x1301 263 mov bx, 0x0007 264 int 0x10 265 ret 266 267 ; no parameter 268 ResetFloppy: 269 270 mov ah, 0x00 271 mov dl, [BS_DrvNum] 272 int 0x13 273 274 ret 275 276 ; ax --> logic sector number 277 ; cx --> number of sector 278 ; es:bx --> target address 279 ReadSector: 280 281 call ResetFloppy 282 283 push bx 284 push cx 285 286 mov bl, [BPB_SecPerTrk] 287 div bl 288 mov cl, ah 289 add cl, 1 290 mov ch, al 291 shr ch, 1 292 mov dh, al 293 and dh, 1 294 mov dl, [BS_DrvNum] 295 296 pop ax 297 pop bx 298 299 mov ah, 0x02 300 301 read: 302 int 0x13 303 jc read 304 305 ret 306 307 MsgStr db "No LOADER ..." 308 MsgLen equ ($-MsgStr) 309 Target db "LOADER " 310 TarLen equ ($-Target) 311 EntryItem times EntryItemLength db 0x00 312 Buf: 313 times 510-($-$$) db 0x00 314 db 0x55, 0xaa
第90行改为跳转到文件内容加载地址处,而不再跳转到output。第309行文件的名字也改为LOADER。
执行make,然后运行bochs,结果如下:
可以看到,文件中的可执行程序被加载并成功执行了,成功打印出字符串。此时的打印是文件中的可执行程序中print函数实现的,而不是我们的加载程序中的Print函数实现的。
我们将a.img拷贝到windows下,在真正的机器上试一下,设置如下:
启动虚拟机,我们看到了以下结果:
我们的加载程序和可执行文件都成功运行了。