ASM:《X86汇编语言-从实模式到保护模式》第15章:任务切换
15章其实应该是和14章相辅相成的(感觉应该是作者觉得14章内容太多了然后切出来了一点)。任务切换和14章的某些概念是分不开的。
★PART1:任务门与任务切换的方法
1. 任务管理程序
14章的时候我们说过,一个程序他可以有很多个任务,特权级指的是任务的不同部分的特权级,一个任务可以有两个空间,一个全局空间,一个局部空间。在一个任务内,全局空间和局部空间具有不同的特权级别,使用门,可以在任务内将控制从3特权级的局部空间转移到0特权级的全局空间,以使用内核或者操作系统提供的服务。
任务切换时以任务为单位的,是指离开一个任务,转到另一个任务中去执行。任务转移相对复杂得多,执行任务时,系统必须至少有两个任务,而且已经有一个正在执行,为了实现任务在全局空间和局部空间可以很快速地切换,系统使用任务管理器机制来管理系统(任务管理器随内核加载而加载,处于0特权级)。任务管理程序以一个任务而独立存在,以完成一些管理和控制功能(相当于一个中继)。
任务管理程序可以没有自己的LDT(处理器允许一个程序没有LDT),但是他必须要有TSS来进行任务切换。下面就演示一段来设置任务管理器的程序(把内核看成任务管理器)。
经过上面一番设置以后,就可以认为任务管理器已经加载了。
2. 任务切换的方法
Intel处理器为任务切换提供了很多方法,以灵活地在各个任务实施切换。
第一个方法就是借助中断来进行任务切换,这是现代抢占式多任务的基础。在实模式下,内存最低端1KB是中断向量表,保存着256个中断处理过程的段地址和偏移地址。在保护模式下,处理器不再使用中断向量表,而是使用中断描述符表。中段描述符表和GDT,LDT是一样的,用于保存描述符,唯一不同的地方是,他保存的是门描述符,包括中断门,陷阱门和任务门。中断门和陷阱门允许在任务内实施中断处理,转到全局空间去执行一些系统级的管理工作,本质上,也是任务内的控制转移行为。但是,在中断发生的时候,如果该中断号对应的门是任务门,那么必须进行任务切换(中断当前任务的执行,保护当前任务的现场,并转移到另一个任务去执行)。
任务门的样子如上,任务门也是一个系统段,所以S一定是0,TYPE位是0101表明是任务门。任务门的P位来表示这个门是否有效,当P=0时候,不允许通过此门来进行任务切换,DPL是任务门描述符的特权级,但是对因为中断而发起的任务切换不起作用,处理器不按特权级施加任何保护。
当中断发生的时候,处理器用中断号乘以8作为索引访问中段描述符表,当这个门是任务门时,则处理器会取出新任务的TSS选择子,然后用TSS的选择子访问GDT,取出TSS描述符。然后,处理器访问新的TSS,从中恢复在TSS登记的所有东西。然后,TR开始指向新任务的TSS,一旦新任务开始执行,则处理器固件会自动将TSS描述符的B位设置为1(表示在忙)。
当中断发生的时候,可以执行常规的中断处理过程。也可以进行任务切换。这两者都使用iret指令返回。前者是返回到同一个任务的不同代码段,后者是返回到被中断的任务。处理器通过EFLAGS位来区分这两者的区别。
如图,EFLAGS的14位就是NT位(Nested Task Flag),这个位指示的是任务是否嵌套。本质上,因为现在中断的描述符全部都是门,所以都是可以根据NT位来确定如何进行返回的。如果一个任务的切换是因为中断发起的,发生任务切换以后,旧任务的TSS描述符的B位不会改变,处理器将现场的TSS的所有需要填写的项都填写好,然后把旧任务的TSS描述符选择子写入新任务的任务连接域中(0x00)处,同时将新任务EFLAGS寄存器的NT位置变为1,以允许新任务返回旧任务的操作,同时还要把新任务的TSS描述符的B位设置为1(忙)。
回到iret指令上来,当处理器看见iret指令,则会立马检查NT位,如果这个位是0,那么就按一般的中断返回操作进行;如果这个位是1,那么就会返回原先被中断的任务的位置继续进行旧任务,同时把NT位复原,并把切换前的任务的TSS描述符的B位设置为0。然后TR指向原来被切换的任务的TSS,进行任务切换。
事实上,处理器除了可以用中断进行任务切换,还有其他三种切换任务的方式。处理器切换任务的方式有4种,如下:
- 当前程序,任务或者过程执行控制转移到GDT内某个TSS描述符(使用jmp或者call进行切换)。
- 当前程序,任务或者过程执行控制转移到GDT或者当前LDT内某个任务门描述符(任务门可以在任务的LDT上,TSS一定要在GDT上)(使用jmp或者call进行切换)。
- 一个异常或者中断发生时候,中断号指向中断描述表内的任务门。
- 在EFLAGS寄存器的NT位置为的情况下,当前任务执行一个iret指令。
由call指令发起的任务切换类似于因为中断发起的任务切换,也就是说,call指令发起的任务切换是嵌套的,当前任务(旧任务)TSS描述符的B位保持原来的“1”不变。EFLAGS寄存器的NT位也不会发生变化。新任务的TSS描述符的B位置1,EFLAGS的NT位也置1,表示此任务嵌套于其他任务中,同时,TSS任务连接域的内容改为旧任务的TSS描述符选择子。
和call指令不同,使用jmp指令发起的任务切换,不会形成任务的嵌套关系。执行任务切换的时候,当前任务(旧任务)的TSS描述符清零,变为非忙状态,EFLAGS寄存器的NT位不变;新任务的TSS描述符的B位置1,进入忙的状态,EFLAGS寄存器的NT位保持从TSS中加载时的状态度不变。
不管如何,任务是不可以重入的,本质上来说,就是发起任务切换的时候,新任务的TSS描述符的B位不能是1,处理器每次切换任务都会检查TSS描述符的B位。
在进行任务切换的时候,处理器执行以下操作:
- 使用JMP或者CALL指令的操作数(注意任务门的选择子问题,和调用门一样,进行远调用的时候任务门会忽略32位的偏移地址,直接用16位的选择子部分),任务门或者当前任务的TSS任务连接域取得新任务的TSS描述符选择子。
- 检查是否允许从当前任务切换到新任务,数据访问的特权级检查适用于jmp和call指令,当前(旧)任务的CPL和新任务段的RPL必须在数值上小于或者等于目标TSS或者任务门的DPL。异常,中断(除了由int n指令发起的中断)和iret指令引起的任务切换忽略任务门或者TSS描述符的DPL,对于以int n发起的任务切换,要检查DPL。
- 检查新任务的TSS描述符是否被标记为有效(P=1),而且界限要大于103,同时检查任务的B位,除了iret指令发起的切换,其他切换B位要等于0(防止任务重入),iret指令发起的切换B=1。
- 检查当前任务(旧任务)和新任务的TSS,以及所有在任务切换时要用到的段描述符已经安排到系统内存中。
- 如果任务切换是由jmp或者iret发起的,处理器清除当前(旧)任务的B位,如果是由call,异常或者中断发起的,那么B位就维持原来的状态。
- 如果任务切换是由iret发起的,那么处理器建立EFLAGS寄存器的副本并且把NT位清除,如果是其他三种方式发起的切换,那么副本中的NT位保持不变。
- 保存当前任务状态到他的TSS中(TSS所需要的所有副本)。
- 如果任务切换是由call,异常或者中断发起的,则处理器从新任务加载的EFLAGS寄存器的NT位置1;如果是由iret或者jmp发起的,则NT位保持不变。
- 如果任务是由call,jmp,异常或者中断发起的,则处理器将新任务的TSS描述符的B位置1;如果由iret发起,则B位保持不变。
- 用新任务的TSS选择子和TSS描述符加载到任务寄存器TR(注意描述符的基地址,段界限和属性加载到TR的高速缓存器中了)。
- 新任务的TSS状态数据被加载到处理器(TSS里面有的都要加载)。载入期间只要有一个发生故障,架构状态会被破坏(无法撤销)。所谓架构状态就是从一个状态转移到另一个状态是一确定的,不会出现不可预知的状况。
- 段选择子对应的描述符也会被加载,与加载和验证新环境有关的任务错误都会破坏架构状态,如果前面1-10步出现了不可恢复的错误,处理器将不能完成任务切换,并且确保处理器返回到执行发起任务切换的那条指令前的状态,如果在11步出现了不可恢复的错误,架构状态将会被破坏。如果在12步(提交点)以后发生了不可恢复错误,处理器将会完成任务切换并且在开始执行新任务之前产生一个相应的异常。
- 开始执行新任务。任务切换的时候,新任务的特权级别不是从那个被挂起的任务继承来的。新任务的特权级别都是由其段寄存器CS的低2位决定的,而该寄存器的内容取自新任务的TSS。因为每个任务都有自己的独立地址空间和任务状态段TSS,所以任务之间是彼此隔离的。只需要用特权级规则控制对TSS的访问就行,软件不需要在任务切换时进行显式的特权级检查。
★PART2:本章代码
1. 书上原来配套代码
注意书上返回的时候用的是iretd,这个指令功能其实是和iret是一样的,只是他是编译器提供的,和pushfd,popfd一样,只是在16位模式下往iret前面加前缀而已。iret指令的机器码是CF,在16位模式下,iret是没有指令前缀0x66的,ireted有0x66;在32位保护模式下,iret和iretd指令的机器码都是CF。
不过书上的任务返回的时候,为了说清楚call和jmp指令的返回差别,他用一个调用门强行把任务转移回内核中,事实上这样的话就任务切换机制就被破坏了,也就是不能再用TSS再加载到上一个任务的跳转位置再进行指令。(可以跑回调用门的最后,但是这样就没有意义了)。
1 ;代码清单15-1 2 ;文件名:c15_core.asm 3 ;文件说明:保护模式微型核心程序 4 ;创建日期:2011-11-19 21:40 5 6 ;以下常量定义部分。内核的大部分内容都应当固定 7 core_code_seg_sel equ 0x38 ;内核代码段选择子 8 core_data_seg_sel equ 0x30 ;内核数据段选择子 9 sys_routine_seg_sel equ 0x28 ;系统公共例程代码段的选择子 10 video_ram_seg_sel equ 0x20 ;视频显示缓冲区的段选择子 11 core_stack_seg_sel equ 0x18 ;内核堆栈段选择子 12 mem_0_4_gb_seg_sel equ 0x08 ;整个0-4GB内存的段的选择子 13 14 ;------------------------------------------------------------------------------- 15 ;以下是系统核心的头部,用于加载核心程序 16 core_length dd core_end ;核心程序总长度#00 17 18 sys_routine_seg dd section.sys_routine.start 19 ;系统公用例程段位置#04 20 21 core_data_seg dd section.core_data.start 22 ;核心数据段位置#08 23 24 core_code_seg dd section.core_code.start 25 ;核心代码段位置#0c 26 27 28 core_entry dd start ;核心代码段入口点#10 29 dw core_code_seg_sel 30 31 ;=============================================================================== 32 [bits 32] 33 ;=============================================================================== 34 SECTION sys_routine vstart=0 ;系统公共例程代码段 35 ;------------------------------------------------------------------------------- 36 ;字符串显示例程 37 put_string: ;显示0终止的字符串并移动光标 38 ;输入:DS:EBX=串地址 39 push ecx 40 .getc: 41 mov cl,[ebx] 42 or cl,cl 43 jz .exit 44 call put_char 45 inc ebx 46 jmp .getc 47 48 .exit: 49 pop ecx 50 retf ;段间返回 51 52 ;------------------------------------------------------------------------------- 53 put_char: ;在当前光标处显示一个字符,并推进 54 ;光标。仅用于段内调用 55 ;输入:CL=字符ASCII码 56 pushad 57 58 ;以下取当前光标位置 59 mov dx,0x3d4 60 mov al,0x0e 61 out dx,al 62 inc dx ;0x3d5 63 in al,dx ;高字 64 mov ah,al 65 66 dec dx ;0x3d4 67 mov al,0x0f 68 out dx,al 69 inc dx ;0x3d5 70 in al,dx ;低字 71 mov bx,ax ;BX=代表光标位置的16位数 72 73 cmp cl,0x0d ;回车符? 74 jnz .put_0a 75 mov ax,bx 76 mov bl,80 77 div bl 78 mul bl 79 mov bx,ax 80 jmp .set_cursor 81 82 .put_0a: 83 cmp cl,0x0a ;换行符? 84 jnz .put_other 85 add bx,80 86 jmp .roll_screen 87 88 .put_other: ;正常显示字符 89 push es 90 mov eax,video_ram_seg_sel ;0xb8000段的选择子 91 mov es,eax 92 shl bx,1 93 mov [es:bx],cl 94 pop es 95 96 ;以下将光标位置推进一个字符 97 shr bx,1 98 inc bx 99 100 .roll_screen: 101 cmp bx,2000 ;光标超出屏幕?滚屏 102 jl .set_cursor 103 104 push ds 105 push es 106 mov eax,video_ram_seg_sel 107 mov ds,eax 108 mov es,eax 109 cld 110 mov esi,0xa0 ;小心!32位模式下movsb/w/d 111 mov edi,0x00 ;使用的是esi/edi/ecx 112 mov ecx,1920 113 rep movsd 114 mov bx,3840 ;清除屏幕最底一行 115 mov ecx,80 ;32位程序应该使用ECX 116 .cls: 117 mov word[es:bx],0x0720 118 add bx,2 119 loop .cls 120 121 pop es 122 pop ds 123 124 mov bx,1920 125 126 .set_cursor: 127 mov dx,0x3d4 128 mov al,0x0e 129 out dx,al 130 inc dx ;0x3d5 131 mov al,bh 132 out dx,al 133 dec dx ;0x3d4 134 mov al,0x0f 135 out dx,al 136 inc dx ;0x3d5 137 mov al,bl 138 out dx,al 139 140 popad 141 142 ret 143 144 ;------------------------------------------------------------------------------- 145 read_hard_disk_0: ;从硬盘读取一个逻辑扇区 146 ;EAX=逻辑扇区号 147 ;DS:EBX=目标缓冲区地址 148 ;返回:EBX=EBX+512 149 push eax 150 push ecx 151 push edx 152 153 push eax 154 155 mov dx,0x1f2 156 mov al,1 157 out dx,al ;读取的扇区数 158 159 inc dx ;0x1f3 160 pop eax 161 out dx,al ;LBA地址7~0 162 163 inc dx ;0x1f4 164 mov cl,8 165 shr eax,cl 166 out dx,al ;LBA地址15~8 167 168 inc dx ;0x1f5 169 shr eax,cl 170 out dx,al ;LBA地址23~16 171 172 inc dx ;0x1f6 173 shr eax,cl 174 or al,0xe0 ;第一硬盘 LBA地址27~24 175 out dx,al 176 177 inc dx ;0x1f7 178 mov al,0x20 ;读命令 179 out dx,al 180 181 .waits: 182 in al,dx 183 and al,0x88 184 cmp al,0x08 185 jnz .waits ;不忙,且硬盘已准备好数据传输 186 187 mov ecx,256 ;总共要读取的字数 188 mov dx,0x1f0 189 .readw: 190 in ax,dx 191 mov [ebx],ax 192 add ebx,2 193 loop .readw 194 195 pop edx 196 pop ecx 197 pop eax 198 199 retf ;段间返回 200 201 ;------------------------------------------------------------------------------- 202 ;汇编语言程序是极难一次成功,而且调试非常困难。这个例程可以提供帮助 203 put_hex_dword: ;在当前光标处以十六进制形式显示 204 ;一个双字并推进光标 205 ;输入:EDX=要转换并显示的数字 206 ;输出:无 207 pushad 208 push ds 209 210 mov ax,core_data_seg_sel ;切换到核心数据段 211 mov ds,ax 212 213 mov ebx,bin_hex ;指向核心数据段内的转换表 214 mov ecx,8 215 .xlt: 216 rol edx,4 217 mov eax,edx 218 and eax,0x0000000f 219 xlat 220 221 push ecx 222 mov cl,al 223 call put_char 224 pop ecx 225 226 loop .xlt 227 228 pop ds 229 popad 230 retf 231 232 ;------------------------------------------------------------------------------- 233 allocate_memory: ;分配内存 234 ;输入:ECX=希望分配的字节数 235 ;输出:ECX=起始线性地址 236 push ds 237 push eax 238 push ebx 239 240 mov eax,core_data_seg_sel 241 mov ds,eax 242 243 mov eax,[ram_alloc] 244 add eax,ecx ;下一次分配时的起始地址 245 246 ;这里应当有检测可用内存数量的指令 247 248 mov ecx,[ram_alloc] ;返回分配的起始地址 249 250 mov ebx,eax 251 and ebx,0xfffffffc 252 add ebx,4 ;强制对齐 253 test eax,0x00000003 ;下次分配的起始地址最好是4字节对齐 254 cmovnz eax,ebx ;如果没有对齐,则强制对齐 255 mov [ram_alloc],eax ;下次从该地址分配内存 256 ;cmovcc指令可以避免控制转移 257 pop ebx 258 pop eax 259 pop ds 260 261 retf 262 263 ;------------------------------------------------------------------------------- 264 set_up_gdt_descriptor: ;在GDT内安装一个新的描述符 265 ;输入:EDX:EAX=描述符 266 ;输出:CX=描述符的选择子 267 push eax 268 push ebx 269 push edx 270 271 push ds 272 push es 273 274 mov ebx,core_data_seg_sel ;切换到核心数据段 275 mov ds,ebx 276 277 sgdt [pgdt] ;以便开始处理GDT 278 279 mov ebx,mem_0_4_gb_seg_sel 280 mov es,ebx 281 282 movzx ebx,word [pgdt] ;GDT界限 283 inc bx ;GDT总字节数,也是下一个描述符偏移 284 add ebx,[pgdt+2] ;下一个描述符的线性地址 285 286 mov [es:ebx],eax 287 mov [es:ebx+4],edx 288 289 add word [pgdt],8 ;增加一个描述符的大小 290 291 lgdt [pgdt] ;对GDT的更改生效 292 293 mov ax,[pgdt] ;得到GDT界限值 294 xor dx,dx 295 mov bx,8 296 div bx ;除以8,去掉余数 297 mov cx,ax 298 shl cx,3 ;将索引号移到正确位置 299 300 pop es 301 pop ds 302 303 pop edx 304 pop ebx 305 pop eax 306 307 retf 308 ;------------------------------------------------------------------------------- 309 make_seg_descriptor: ;构造存储器和系统的段描述符 310 ;输入:EAX=线性基地址 311 ; EBX=段界限 312 ; ECX=属性。各属性位都在原始 313 ; 位置,无关的位清零 314 ;返回:EDX:EAX=描述符 315 mov edx,eax 316 shl eax,16 317 or ax,bx ;描述符前32位(EAX)构造完毕 318 319 and edx,0xffff0000 ;清除基地址中无关的位 320 rol edx,8 321 bswap edx ;装配基址的31~24和23~16 (80486+) 322 323 xor bx,bx 324 or edx,ebx ;装配段界限的高4位 325 326 or edx,ecx ;装配属性 327 328 retf 329 330 ;------------------------------------------------------------------------------- 331 make_gate_descriptor: ;构造门的描述符(调用门等) 332 ;输入:EAX=门代码在段内偏移地址 333 ; BX=门代码所在段的选择子 334 ; CX=段类型及属性等(各属 335 ; 性位都在原始位置) 336 ;返回:EDX:EAX=完整的描述符 337 push ebx 338 push ecx 339 340 mov edx,eax 341 and edx,0xffff0000 ;得到偏移地址高16位 342 or dx,cx ;组装属性部分到EDX 343 344 and eax,0x0000ffff ;得到偏移地址低16位 345 shl ebx,16 346 or eax,ebx ;组装段选择子部分 347 348 pop ecx 349 pop ebx 350 351 retf 352 353 ;------------------------------------------------------------------------------- 354 terminate_current_task: ;终止当前任务 355 ;注意,执行此例程时,当前任务仍在 356 ;运行中。此例程其实也是当前任务的 357 ;一部分 358 pushfd 359 mov edx,[esp] ;获得EFLAGS寄存器内容 360 add esp,4 ;恢复堆栈指针 361 362 mov eax,core_data_seg_sel 363 mov ds,eax 364 365 test dx,0100_0000_0000_0000B ;测试NT位 366 jnz .b1 ;当前任务是嵌套的,到.b1执行iretd 367 mov ebx,core_msg1 ;当前任务不是嵌套的,直接切换到 368 call sys_routine_seg_sel:put_string 369 jmp far [prgman_tss] ;程序管理器任务 370 371 .b1: 372 mov ebx,core_msg0 373 call sys_routine_seg_sel:put_string 374 iretd 375 376 sys_routine_end: 377 378 ;=============================================================================== 379 SECTION core_data vstart=0 ;系统核心的数据段 380 ;------------------------------------------------------------------------------- 381 pgdt dw 0 ;用于设置和修改GDT 382 dd 0 383 384 ram_alloc dd 0x00100000 ;下次分配内存时的起始地址 385 386 ;符号地址检索表 387 salt: 388 salt_1 db '@PrintString' 389 times 256-($-salt_1) db 0 390 dd put_string 391 dw sys_routine_seg_sel 392 393 salt_2 db '@ReadDiskData' 394 times 256-($-salt_2) db 0 395 dd read_hard_disk_0 396 dw sys_routine_seg_sel 397 398 salt_3 db '@PrintDwordAsHexString' 399 times 256-($-salt_3) db 0 400 dd put_hex_dword 401 dw sys_routine_seg_sel 402 403 salt_4 db '@TerminateProgram' 404 times 256-($-salt_4) db 0 405 dd terminate_current_task 406 dw sys_routine_seg_sel 407 408 salt_item_len equ $-salt_4 409 salt_items equ ($-salt)/salt_item_len 410 411 message_1 db ' If you seen this message,that means we ' 412 db 'are now in protect mode,and the system ' 413 db 'core is loaded,and the video display ' 414 db 'routine works perfectly.',0x0d,0x0a,0 415 416 message_2 db ' System wide CALL-GATE mounted.',0x0d,0x0a,0 417 418 bin_hex db '0123456789ABCDEF' 419 ;put_hex_dword子过程用的查找表 420 421 core_buf times 2048 db 0 ;内核用的缓冲区 422 423 cpu_brnd0 db 0x0d,0x0a,' ',0 424 cpu_brand times 52 db 0 425 cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 426 427 ;任务控制块链 428 tcb_chain dd 0 429 430 ;程序管理器的任务信息 431 prgman_tss dd 0 ;程序管理器的TSS基地址 432 dw 0 ;程序管理器的TSS描述符选择子 433 434 prgman_msg1 db 0x0d,0x0a 435 db '[PROGRAM MANAGER]: Hello! I am Program Manager,' 436 db 'run at CPL=0.Now,create user task and switch ' 437 db 'to it by the CALL instruction...',0x0d,0x0a,0 438 439 prgman_msg2 db 0x0d,0x0a 440 db '[PROGRAM MANAGER]: I am glad to regain control.' 441 db 'Now,create another user task and switch to ' 442 db 'it by the JMP instruction...',0x0d,0x0a,0 443 444 prgman_msg3 db 0x0d,0x0a 445 db '[PROGRAM MANAGER]: I am gain control again,' 446 db 'HALT...',0 447 448 core_msg0 db 0x0d,0x0a 449 db '[SYSTEM CORE]: Uh...This task initiated with ' 450 db 'CALL instruction or an exeception/ interrupt,' 451 db 'should use IRETD instruction to switch back...' 452 db 0x0d,0x0a,0 453 454 core_msg1 db 0x0d,0x0a 455 db '[SYSTEM CORE]: Uh...This task initiated with ' 456 db 'JMP instruction, should switch to Program ' 457 db 'Manager directly by the JMP instruction...' 458 db 0x0d,0x0a,0 459 460 core_data_end: 461 462 ;=============================================================================== 463 SECTION core_code vstart=0 464 ;------------------------------------------------------------------------------- 465 fill_descriptor_in_ldt: ;在LDT内安装一个新的描述符 466 ;输入:EDX:EAX=描述符 467 ; EBX=TCB基地址 468 ;输出:CX=描述符的选择子 469 push eax 470 push edx 471 push edi 472 push ds 473 474 mov ecx,mem_0_4_gb_seg_sel 475 mov ds,ecx 476 477 mov edi,[ebx+0x0c] ;获得LDT基地址 478 479 xor ecx,ecx 480 mov cx,[ebx+0x0a] ;获得LDT界限 481 inc cx ;LDT的总字节数,即新描述符偏移地址 482 483 mov [edi+ecx+0x00],eax 484 mov [edi+ecx+0x04],edx ;安装描述符 485 486 add cx,8 487 dec cx ;得到新的LDT界限值 488 489 mov [ebx+0x0a],cx ;更新LDT界限值到TCB 490 491 mov ax,cx 492 xor dx,dx 493 mov cx,8 494 div cx 495 496 mov cx,ax 497 shl cx,3 ;左移3位,并且 498 or cx,0000_0000_0000_0100B ;使TI位=1,指向LDT,最后使RPL=00 499 500 pop ds 501 pop edi 502 pop edx 503 pop eax 504 505 ret 506 507 ;------------------------------------------------------------------------------- 508 load_relocate_program: ;加载并重定位用户程序 509 ;输入: PUSH 逻辑扇区号 510 ; PUSH 任务控制块基地址 511 ;输出:无 512 pushad 513 514 push ds 515 push es 516 517 mov ebp,esp ;为访问通过堆栈传递的参数做准备 518 519 mov ecx,mem_0_4_gb_seg_sel 520 mov es,ecx 521 522 mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 523 524 ;以下申请创建LDT所需要的内存 525 mov ecx,160 ;允许安装20个LDT描述符 526 call sys_routine_seg_sel:allocate_memory 527 mov [es:esi+0x0c],ecx ;登记LDT基地址到TCB中 528 mov word [es:esi+0x0a],0xffff ;登记LDT初始的界限到TCB中 529 530 ;以下开始加载用户程序 531 mov eax,core_data_seg_sel 532 mov ds,eax ;切换DS到内核数据段 533 534 mov eax,[ebp+12*4] ;从堆栈中取出用户程序起始扇区号 535 mov ebx,core_buf ;读取程序头部数据 536 call sys_routine_seg_sel:read_hard_disk_0 537 538 ;以下判断整个程序有多大 539 mov eax,[core_buf] ;程序尺寸 540 mov ebx,eax 541 and ebx,0xfffffe00 ;使之512字节对齐(能被512整除的数低 542 add ebx,512 ;9位都为0 543 test eax,0x000001ff ;程序的大小正好是512的倍数吗? 544 cmovnz eax,ebx ;不是。使用凑整的结果 545 546 mov ecx,eax ;实际需要申请的内存数量 547 call sys_routine_seg_sel:allocate_memory 548 mov [es:esi+0x06],ecx ;登记程序加载基地址到TCB中 549 550 mov ebx,ecx ;ebx -> 申请到的内存首地址 551 xor edx,edx 552 mov ecx,512 553 div ecx 554 mov ecx,eax ;总扇区数 555 556 mov eax,mem_0_4_gb_seg_sel ;切换DS到0-4GB的段 557 mov ds,eax 558 559 mov eax,[ebp+12*4] ;起始扇区号 560 .b1: 561 call sys_routine_seg_sel:read_hard_disk_0 562 inc eax 563 loop .b1 ;循环读,直到读完整个用户程序 564 565 mov edi,[es:esi+0x06] ;获得程序加载基地址 566 567 ;建立程序头部段描述符 568 mov eax,edi ;程序头部起始线性地址 569 mov ebx,[edi+0x04] ;段长度 570 dec ebx ;段界限 571 mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 572 call sys_routine_seg_sel:make_seg_descriptor 573 574 ;安装头部段描述符到LDT中 575 mov ebx,esi ;TCB的基地址 576 call fill_descriptor_in_ldt 577 578 or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 579 mov [es:esi+0x44],cx ;登记程序头部段选择子到TCB 580 mov [edi+0x04],cx ;和头部内 581 582 ;建立程序代码段描述符 583 mov eax,edi 584 add eax,[edi+0x14] ;代码起始线性地址 585 mov ebx,[edi+0x18] ;段长度 586 dec ebx ;段界限 587 mov ecx,0x0040f800 ;字节粒度的代码段描述符,特权级3 588 call sys_routine_seg_sel:make_seg_descriptor 589 mov ebx,esi ;TCB的基地址 590 call fill_descriptor_in_ldt 591 or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 592 mov [edi+0x14],cx ;登记代码段选择子到头部 593 594 ;建立程序数据段描述符 595 mov eax,edi 596 add eax,[edi+0x1c] ;数据段起始线性地址 597 mov ebx,[edi+0x20] ;段长度 598 dec ebx ;段界限 599 mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级3 600 call sys_routine_seg_sel:make_seg_descriptor 601 mov ebx,esi ;TCB的基地址 602 call fill_descriptor_in_ldt 603 or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 604 mov [edi+0x1c],cx ;登记数据段选择子到头部 605 606 ;建立程序堆栈段描述符 607 mov ecx,[edi+0x0c] ;4KB的倍率 608 mov ebx,0x000fffff 609 sub ebx,ecx ;得到段界限 610 mov eax,4096 611 mul ecx 612 mov ecx,eax ;准备为堆栈分配内存 613 call sys_routine_seg_sel:allocate_memory 614 add eax,ecx ;得到堆栈的高端物理地址 615 mov ecx,0x00c0f600 ;字节粒度的堆栈段描述符,特权级3 616 call sys_routine_seg_sel:make_seg_descriptor 617 mov ebx,esi ;TCB的基地址 618 call fill_descriptor_in_ldt 619 or cx,0000_0000_0000_0011B ;设置选择子的特权级为3 620 mov [edi+0x08],cx ;登记堆栈段选择子到头部 621 622 ;重定位SALT 623 mov eax,mem_0_4_gb_seg_sel ;这里和前一章不同,头部段描述符 624 mov es,eax ;已安装,但还没有生效,故只能通 625 ;过4GB段访问用户程序头部 626 mov eax,core_data_seg_sel 627 mov ds,eax 628 629 cld 630 631 mov ecx,[es:edi+0x24] ;U-SALT条目数(通过访问4GB段取得) 632 add edi,0x28 ;U-SALT在4GB段内的偏移 633 .b2: 634 push ecx 635 push edi 636 637 mov ecx,salt_items 638 mov esi,salt 639 .b3: 640 push edi 641 push esi 642 push ecx 643 644 mov ecx,64 ;检索表中,每条目的比较次数 645 repe cmpsd ;每次比较4字节 646 jnz .b4 647 mov eax,[esi] ;若匹配,则esi恰好指向其后的地址 648 mov [es:edi-256],eax ;将字符串改写成偏移地址 649 mov ax,[esi+4] 650 or ax,0000000000000011B ;以用户程序自己的特权级使用调用门 651 ;故RPL=3 652 mov [es:edi-252],ax ;回填调用门选择子 653 .b4: 654 655 pop ecx 656 pop esi 657 add esi,salt_item_len 658 pop edi ;从头比较 659 loop .b3 660 661 pop edi 662 add edi,256 663 pop ecx 664 loop .b2 665 666 mov esi,[ebp+11*4] ;从堆栈中取得TCB的基地址 667 668 ;创建0特权级堆栈 669 mov ecx,4096 670 mov eax,ecx ;为生成堆栈高端地址做准备 671 mov [es:esi+0x1a],ecx 672 shr dword [es:esi+0x1a],12 ;登记0特权级堆栈尺寸到TCB 673 call sys_routine_seg_sel:allocate_memory 674 add eax,ecx ;堆栈必须使用高端地址为基地址 675 mov [es:esi+0x1e],eax ;登记0特权级堆栈基地址到TCB 676 mov ebx,0xffffe ;段长度(界限) 677 mov ecx,0x00c09600 ;4KB粒度,读写,特权级0 678 call sys_routine_seg_sel:make_seg_descriptor 679 mov ebx,esi ;TCB的基地址 680 call fill_descriptor_in_ldt 681 ;or cx,0000_0000_0000_0000 ;设置选择子的特权级为0 682 mov [es:esi+0x22],cx ;登记0特权级堆栈选择子到TCB 683 mov dword [es:esi+0x24],0 ;登记0特权级堆栈初始ESP到TCB 684 685 ;创建1特权级堆栈 686 mov ecx,4096 687 mov eax,ecx ;为生成堆栈高端地址做准备 688 mov [es:esi+0x28],ecx 689 shr [es:esi+0x28],12 ;登记1特权级堆栈尺寸到TCB 690 call sys_routine_seg_sel:allocate_memory 691 add eax,ecx ;堆栈必须使用高端地址为基地址 692 mov [es:esi+0x2c],eax ;登记1特权级堆栈基地址到TCB 693 mov ebx,0xffffe ;段长度(界限) 694 mov ecx,0x00c0b600 ;4KB粒度,读写,特权级1 695 call sys_routine_seg_sel:make_seg_descriptor 696 mov ebx,esi ;TCB的基地址 697 call fill_descriptor_in_ldt 698 or cx,0000_0000_0000_0001 ;设置选择子的特权级为1 699 mov [es:esi+0x30],cx ;登记1特权级堆栈选择子到TCB 700 mov dword [es:esi+0x32],0 ;登记1特权级堆栈初始ESP到TCB 701 702 ;创建2特权级堆栈 703 mov ecx,4096 704 mov eax,ecx ;为生成堆栈高端地址做准备 705 mov [es:esi+0x36],ecx 706 shr [es:esi+0x36],12 ;登记2特权级堆栈尺寸到TCB 707 call sys_routine_seg_sel:allocate_memory 708 add eax,ecx ;堆栈必须使用高端地址为基地址 709 mov [es:esi+0x3a],ecx ;登记2特权级堆栈基地址到TCB 710 mov ebx,0xffffe ;段长度(界限) 711 mov ecx,0x00c0d600 ;4KB粒度,读写,特权级2 712 call sys_routine_seg_sel:make_seg_descriptor 713 mov ebx,esi ;TCB的基地址 714 call fill_descriptor_in_ldt 715 or cx,0000_0000_0000_0010 ;设置选择子的特权级为2 716 mov [es:esi+0x3e],cx ;登记2特权级堆栈选择子到TCB 717 mov dword [es:esi+0x40],0 ;登记2特权级堆栈初始ESP到TCB 718 719 ;在GDT中登记LDT描述符 720 mov eax,[es:esi+0x0c] ;LDT的起始线性地址 721 movzx ebx,word [es:esi+0x0a] ;LDT段界限 722 mov ecx,0x00408200 ;LDT描述符,特权级0 723 call sys_routine_seg_sel:make_seg_descriptor 724 call sys_routine_seg_sel:set_up_gdt_descriptor 725 mov [es:esi+0x10],cx ;登记LDT选择子到TCB中 726 727 ;创建用户程序的TSS 728 mov ecx,104 ;tss的基本尺寸 729 mov [es:esi+0x12],cx 730 dec word [es:esi+0x12] ;登记TSS界限值到TCB 731 call sys_routine_seg_sel:allocate_memory 732 mov [es:esi+0x14],ecx ;登记TSS基地址到TCB 733 734 ;登记基本的TSS表格内容 735 mov word [es:ecx+0],0 ;反向链=0 736 737 mov edx,[es:esi+0x24] ;登记0特权级堆栈初始ESP 738 mov [es:ecx+4],edx ;到TSS中 739 740 mov dx,[es:esi+0x22] ;登记0特权级堆栈段选择子 741 mov [es:ecx+8],dx ;到TSS中 742 743 mov edx,[es:esi+0x32] ;登记1特权级堆栈初始ESP 744 mov [es:ecx+12],edx ;到TSS中 745 746 mov dx,[es:esi+0x30] ;登记1特权级堆栈段选择子 747 mov [es:ecx+16],dx ;到TSS中 748 749 mov edx,[es:esi+0x40] ;登记2特权级堆栈初始ESP 750 mov [es:ecx+20],edx ;到TSS中 751 752 mov dx,[es:esi+0x3e] ;登记2特权级堆栈段选择子 753 mov [es:ecx+24],dx ;到TSS中 754 755 mov dx,[es:esi+0x10] ;登记任务的LDT选择子 756 mov [es:ecx+96],dx ;到TSS中 757 758 mov dx,[es:esi+0x12] ;登记任务的I/O位图偏移 759 mov [es:ecx+102],dx ;到TSS中 760 761 mov word [es:ecx+100],0 ;T=0 762 763 mov dword [es:ecx+28],0 ;登记CR3(PDBR) 764 765 ;访问用户程序头部,获取数据填充TSS 766 mov ebx,[ebp+11*4] ;从堆栈中取得TCB的基地址 767 mov edi,[es:ebx+0x06] ;用户程序加载的基地址 768 769 mov edx,[es:edi+0x10] ;登记程序入口点(EIP) 770 mov [es:ecx+32],edx ;到TSS 771 772 mov dx,[es:edi+0x14] ;登记程序代码段(CS)选择子 773 mov [es:ecx+76],dx ;到TSS中 774 775 mov dx,[es:edi+0x08] ;登记程序堆栈段(SS)选择子 776 mov [es:ecx+80],dx ;到TSS中 777 778 mov dx,[es:edi+0x04] ;登记程序数据段(DS)选择子 779 mov word [es:ecx+84],dx ;到TSS中。注意,它指向程序头部段 780 781 mov word [es:ecx+72],0 ;TSS中的ES=0 782 783 mov word [es:ecx+88],0 ;TSS中的FS=0 784 785 mov word [es:ecx+92],0 ;TSS中的GS=0 786 787 pushfd 788 pop edx 789 790 mov dword [es:ecx+36],edx ;EFLAGS 791 792 ;在GDT中登记TSS描述符 793 mov eax,[es:esi+0x14] ;TSS的起始线性地址 794 movzx ebx,word [es:esi+0x12] ;段长度(界限) 795 mov ecx,0x00408900 ;TSS描述符,特权级0 796 call sys_routine_seg_sel:make_seg_descriptor 797 call sys_routine_seg_sel:set_up_gdt_descriptor 798 mov [es:esi+0x18],cx ;登记TSS选择子到TCB 799 800 pop es ;恢复到调用此过程前的es段 801 pop ds ;恢复到调用此过程前的ds段 802 803 popad 804 805 ret 8 ;丢弃调用本过程前压入的参数 806 807 ;------------------------------------------------------------------------------- 808 append_to_tcb_link: ;在TCB链上追加任务控制块 809 ;输入:ECX=TCB线性基地址 810 push eax 811 push edx 812 push ds 813 push es 814 815 mov eax,core_data_seg_sel ;令DS指向内核数据段 816 mov ds,eax 817 mov eax,mem_0_4_gb_seg_sel ;令ES指向0..4GB段 818 mov es,eax 819 820 mov dword [es: ecx+0x00],0 ;当前TCB指针域清零,以指示这是最 821 ;后一个TCB 822 823 mov eax,[tcb_chain] ;TCB表头指针 824 or eax,eax ;链表为空? 825 jz .notcb 826 827 .searc: 828 mov edx,eax 829 mov eax,[es: edx+0x00] 830 or eax,eax 831 jnz .searc 832 833 mov [es: edx+0x00],ecx 834 jmp .retpc 835 836 .notcb: 837 mov [tcb_chain],ecx ;若为空表,直接令表头指针指向TCB 838 839 .retpc: 840 pop es 841 pop ds 842 pop edx 843 pop eax 844 845 ret 846 847 ;------------------------------------------------------------------------------- 848 start: 849 mov ecx,core_data_seg_sel ;令DS指向核心数据段 850 mov ds,ecx 851 852 mov ecx,mem_0_4_gb_seg_sel ;令ES指向4GB数据段 853 mov es,ecx 854 855 mov ebx,message_1 856 call sys_routine_seg_sel:put_string 857 858 ;显示处理器品牌信息 859 mov eax,0x80000002 860 cpuid 861 mov [cpu_brand + 0x00],eax 862 mov [cpu_brand + 0x04],ebx 863 mov [cpu_brand + 0x08],ecx 864 mov [cpu_brand + 0x0c],edx 865 866 mov eax,0x80000003 867 cpuid 868 mov [cpu_brand + 0x10],eax 869 mov [cpu_brand + 0x14],ebx 870 mov [cpu_brand + 0x18],ecx 871 mov [cpu_brand + 0x1c],edx 872 873 mov eax,0x80000004 874 cpuid 875 mov [cpu_brand + 0x20],eax 876 mov [cpu_brand + 0x24],ebx 877 mov [cpu_brand + 0x28],ecx 878 mov [cpu_brand + 0x2c],edx 879 880 mov ebx,cpu_brnd0 ;显示处理器品牌信息 881 call sys_routine_seg_sel:put_string 882 mov ebx,cpu_brand 883 call sys_routine_seg_sel:put_string 884 mov ebx,cpu_brnd1 885 call sys_routine_seg_sel:put_string 886 887 ;以下开始安装为整个系统服务的调用门。特权级之间的控制转移必须使用门 888 mov edi,salt ;C-SALT表的起始位置 889 mov ecx,salt_items ;C-SALT表的条目数量 890 .b3: 891 push ecx 892 mov eax,[edi+256] ;该条目入口点的32位偏移地址 893 mov bx,[edi+260] ;该条目入口点的段选择子 894 mov cx,1_11_0_1100_000_00000B ;特权级3的调用门(3以上的特权级才 895 ;允许访问),0个参数(因为用寄存器 896 ;传递参数,而没有用栈) 897 call sys_routine_seg_sel:make_gate_descriptor 898 call sys_routine_seg_sel:set_up_gdt_descriptor 899 mov [edi+260],cx ;将返回的门描述符选择子回填 900 add edi,salt_item_len ;指向下一个C-SALT条目 901 pop ecx 902 loop .b3 903 904 ;对门进行测试 905 mov ebx,message_2 906 call far [salt_1+256] ;通过门显示信息(偏移量将被忽略) 907 908 ;为程序管理器的TSS分配内存空间 909 mov ecx,104 ;为该任务的TSS分配内存 910 call sys_routine_seg_sel:allocate_memory 911 mov [prgman_tss+0x00],ecx ;保存程序管理器的TSS基地址 912 913 ;在程序管理器的TSS中设置必要的项目 914 mov word [es:ecx+96],0 ;没有LDT。处理器允许没有LDT的任务。 915 mov word [es:ecx+102],103 ;没有I/O位图。0特权级事实上不需要。 916 mov word [es:ecx+0],0 ;反向链=0 917 mov dword [es:ecx+28],0 ;登记CR3(PDBR) 918 mov word [es:ecx+100],0 ;T=0 919 ;不需要0、1、2特权级堆栈。0特级不 920 ;会向低特权级转移控制。 921 922 ;创建TSS描述符,并安装到GDT中 923 mov eax,ecx ;TSS的起始线性地址 924 mov ebx,103 ;段长度(界限) 925 mov ecx,0x00408900 ;TSS描述符,特权级0 926 call sys_routine_seg_sel:make_seg_descriptor 927 call sys_routine_seg_sel:set_up_gdt_descriptor 928 mov [prgman_tss+0x04],cx ;保存程序管理器的TSS描述符选择子 929 930 ;任务寄存器TR中的内容是任务存在的标志,该内容也决定了当前任务是谁。 931 ;下面的指令为当前正在执行的0特权级任务“程序管理器”后补手续(TSS)。 932 ltr cx 933 934 ;现在可认为“程序管理器”任务正执行中 935 mov ebx,prgman_msg1 936 call sys_routine_seg_sel:put_string 937 938 mov ecx,0x46 939 call sys_routine_seg_sel:allocate_memory 940 call append_to_tcb_link ;将此TCB添加到TCB链中 941 942 push dword 50 ;用户程序位于逻辑50扇区 943 push ecx ;压入任务控制块起始线性地址 944 945 call load_relocate_program 946 947 call far [es:ecx+0x14] ;执行任务切换。和上一章不同,任务切 948 ;换时要恢复TSS内容,所以在创建任务 949 ;时TSS要填写完整 950 951 ;重新加载并切换任务 952 mov ebx,prgman_msg2 953 call sys_routine_seg_sel:put_string 954 955 mov ecx,0x46 956 call sys_routine_seg_sel:allocate_memory 957 call append_to_tcb_link ;将此TCB添加到TCB链中 958 959 push dword 50 ;用户程序位于逻辑50扇区 960 push ecx ;压入任务控制块起始线性地址 961 962 call load_relocate_program 963 964 jmp far [es:ecx+0x14] ;执行任务切换 965 966 mov ebx,prgman_msg3 967 call sys_routine_seg_sel:put_string 968 969 hlt 970 971 core_code_end: 972 973 ;------------------------------------------------------------------------------- 974 SECTION core_trail 975 ;------------------------------------------------------------------------------- 976 core_end:
1 ;代码清单15-2 2 ;文件名:c15.asm 3 ;文件说明:用户程序 4 ;创建日期:2011-11-15 19:11 5 6 ;=============================================================================== 7 SECTION header vstart=0 8 9 program_length dd program_end ;程序总长度#0x00 10 11 head_len dd header_end ;程序头部的长度#0x04 12 13 stack_seg dd 0 ;用于接收堆栈段选择子#0x08 14 stack_len dd 1 ;程序建议的堆栈大小#0x0c 15 ;以4KB为单位 16 17 prgentry dd start ;程序入口#0x10 18 code_seg dd section.code.start ;代码段位置#0x14 19 code_len dd code_end ;代码段长度#0x18 20 21 data_seg dd section.data.start ;数据段位置#0x1c 22 data_len dd data_end ;数据段长度#0x20 23 ;------------------------------------------------------------------------------- 24 ;符号地址检索表 25 salt_items dd (header_end-salt)/256 ;#0x24 26 27 salt: ;#0x28 28 PrintString db '@PrintString' 29 times 256-($-PrintString) db 0 30 31 TerminateProgram db '@TerminateProgram' 32 times 256-($-TerminateProgram) db 0 33 34 ReadDiskData db '@ReadDiskData' 35 times 256-($-ReadDiskData) db 0 36 37 header_end: 38 39 ;=============================================================================== 40 SECTION data vstart=0 41 42 message_1 db 0x0d,0x0a 43 db '[USER TASK]: Hi! nice to meet you,' 44 db 'I am run at CPL=',0 45 46 message_2 db 0 47 db '.Now,I must exit...',0x0d,0x0a,0 48 49 data_end: 50 51 ;=============================================================================== 52 [bits 32] 53 ;=============================================================================== 54 SECTION code vstart=0 55 start: 56 ;任务启动时,DS指向头部段,也不需要设置堆栈 57 mov eax,ds 58 mov fs,eax 59 60 mov eax,[data_seg] 61 mov ds,eax 62 63 mov ebx,message_1 64 call far [fs:PrintString] 65 66 mov ax,cs 67 and al,0000_0011B 68 or al,0x30 69 mov [message_2],al 70 71 mov ebx,message_2 72 call far [fs:PrintString] 73 74 call far [fs:TerminateProgram] ;退出,并将控制权返回到核心 75 76 code_end: 77 78 ;------------------------------------------------------------------------------- 79 SECTION trail 80 ;------------------------------------------------------------------------------- 81 program_end:
3. 习题2
这一题要求演示任务嵌套,说实话这题还是有点难度的,因为他要求我们在程序A中加载任务B并且切换的,这就要我们直接调用内核的加载程序的程序。我们把内核调用程序的例程改成调用门就好了,这个不是问题。但是问题是我们怎么切换任务B呢?当然我们可以改一下load_program让他返回TSS的选择子给我们,但是我又想到了程序A最好要有他自己所有的私有数据,所以想了一想我还是往任务A的LDT写了调用程序B的任务门了,程序A要有一个地方可以记录加载程序的LDT选择子。最后我给所有任务都填上了任务管理器的TSS选择子,方便切换。
1 ;===============================内核程序================================= 2 ;定义内核所要用到的选择子 3 All_4GB_Segment equ 0x0008 ;4GB的全内存区域 4 Stack_Segement equ 0x0018 ;内核栈区 5 Print_Segement equ 0x0020 ;显存映射区 6 Sys_Routine_Segement equ 0x0028 ;公用例程段 7 Core_Data_Segement equ 0x0030 ;内核数据区 8 Core_Code_Segement equ 0x0038 ;内核代码段 9 ;---------------------------------------------------------------- 10 User_Program_AddressA equ 50 ;用户程序所在逻辑扇区 11 Switch_Stack_Size equ 4096 ;切换栈段的大小 12 ;=============================内核程序头部=============================== 13 SECTION header vstart=0 14 Program_Length dd Program_end ;内核总长度 15 Sys_Routine_Seg dd section.Sys_Routine.start ;公用例程段线性地址 16 Core_Data_Seg dd section.Core_Data.start ;内核数据区线性地址 17 Core_Code_Seg dd section.Core_Code.start ;内核代码区线性地址 18 Code_Entry dd start ;注意偏移地址一定是32位的 19 dw Core_Code_Segement 20 ;---------------------------------------------------------------- 21 [bits 32] 22 ;========================================================================= 23 ;============================公用例程区=================================== 24 ;========================================================================= 25 SECTION Sys_Routine align=16 vstart=0 26 ReadHarddisk: ;push1:28位磁盘号(esi) 27 ;push2:应用程序数据段选择子(ax->ds) 28 ;push3: 偏移地址(ebx) 29 ;push4: 应用程序代码段选择子(dx) 30 pushad 31 push ds 32 push es 33 34 mov ebp,esp 35 36 mov esi,[ebp+15*4] 37 movzx eax,word[ebp+14*4] 38 mov ebx,[ebp+13*4] 39 movzx edx,word[ebp+12*4] 40 41 arpl ax,dx 42 mov ds,ax 43 44 mov dx,0x1f2 45 mov al,0x01 ;读一个扇区 46 out dx,al 47 48 inc edx ;0-7位 49 mov eax,esi 50 out dx,al 51 52 inc edx ;8-15位 53 mov al,ah 54 out dx,al 55 56 inc edx ;16-23位 57 shr eax,16 58 out dx,al 59 60 inc edx ;24-28位,主硬盘,LBA模式 61 mov al,ah 62 and al,0x0f 63 or al,0xe0 64 out dx,al 65 66 inc edx 67 mov al,0x20 68 out dx,al 69 70 _wait: 71 in al,dx 72 and al,0x88 73 cmp al,0x08 74 jne _wait 75 76 mov dx,0x1f0 77 mov ecx,256 78 79 _read: 80 in ax,dx 81 mov [ebx],ax 82 add ebx,2 83 loop _read 84 85 pop es 86 pop ds 87 popad 88 retf 16 ;4个数据 89 ;---------------------------------------------------------------- 90 put_string: ;ebx:偏移地址 91 pushad 92 push ds 93 push es 94 95 _print: 96 mov cl,[ebx] 97 cmp cl,0 98 je _exit 99 call put_char 100 inc ebx 101 jmp _print 102 _exit: 103 pop es 104 pop ds 105 popad 106 retf ;段间返回 107 ;-------------------------------------------------------------- 108 put_char: ;cl就是要显示的字符 109 push ebx 110 push es 111 push ds 112 113 mov dx,0x3d4 114 mov al,0x0e ;高8位 115 out dx,al 116 mov dx,0x3d5 117 in al,dx 118 mov ah,al ;先把高8位存起来 119 mov dx,0x3d4 120 mov al,0x0f ;低8位 121 out dx,al 122 mov dx,0x3d5 123 in al,dx ;现在ax就是当前光标的位置 124 125 _judge: 126 cmp cl,0x0a 127 je _set_0x0a 128 cmp cl,0x0d 129 je _set_0x0d 130 _print_visible: 131 mov bx,ax 132 mov eax,Print_Segement 133 mov es,eax 134 shl bx,1 ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍 135 mov [es:bx],cl ;注意这里是屏幕! 136 mov byte[es:bx+1],0x07 137 add bx,2 138 shr bx,1 139 jmp _roll_screen 140 _set_0x0d: ;回车 141 mov bl,80 142 div bl 143 mul bl 144 mov bx,ax 145 jmp _set_cursor 146 _set_0x0a: ;换行 147 mov bx,ax 148 add bx,80 149 jmp _roll_screen 150 _roll_screen: 151 cmp bx,2000 152 jl _set_cursor 153 mov eax,Print_Segement 154 mov ds,eax 155 mov es,eax 156 157 cld 158 mov edi,0x00 159 mov esi,0xa0 160 mov ecx,1920 161 rep movsw 162 _cls: 163 mov bx,3840 164 mov ecx,80 165 _print_blank: 166 mov word[es:bx],0x0720 167 add bx,2 168 loop _print_blank 169 mov bx,1920 ;别总是忘了光标的位置! 170 _set_cursor: ;改变后的光标位置在bx上 171 mov dx,0x3d4 172 mov al,0x0f ;低8位 173 out dx,al 174 175 mov al,bl 176 mov dx,0x3d5 177 out dx,al 178 179 mov dx,0x3d4 180 mov al,0x0e ;高8位 181 out dx,al 182 183 mov al,bh 184 mov dx,0x3d5 185 out dx,al 186 187 pop ds 188 pop es 189 pop ebx 190 ret 191 ;---------------------------------------------------------------- 192 allocate_memory: ;简易内存分配策略 193 ;输入ecx:想要分配的总字节数 194 ;输出ecx:分配的线性基地址 195 push ds 196 push eax 197 push ebx 198 call Cal_User_Mem 199 200 mov eax,Core_Data_Segement 201 mov ds,eax 202 mov eax,[ram_alloc] 203 mov edx,eax ;edx暂存一下eax 204 add eax,ecx 205 206 cmp eax,edx ;发现新分配的现地址比原来的还小,说明已经溢出 207 jge _alloc 208 mov ebx,mem_alloc_fail 209 call Sys_Routine_Segement:put_string 210 mov ecx,0 ;分配为0说明已经分配失败 211 jmp _exit1 212 _alloc: 213 214 mov ebx,eax 215 and ebx,0xfffffffc 216 add ebx,4 ;强行向上取整 217 test eax,0x00000003 218 cmovnz eax,ebx 219 mov ecx,[ram_alloc] ;要返回要分配的初始地址 220 mov [ram_alloc],eax ;下一次分配的线性基地址 221 222 _exit1: 223 pop ebx 224 pop eax 225 pop ds 226 227 retf 228 ;---------------------------------------------------------------- 229 recycled_memory_and_gdt: 230 mov eax,[ram_recycled] 231 sub [ram_alloc],eax 232 mov dword[ram_recycled],0 ;因为我们还没学到多任务,先这样简单地清零 233 234 sgdt [pgdt_base_tmp] 235 sub word[pgdt_base_tmp],16 ;应用程序的LDT,TSS 236 lgdt [pgdt_base_tmp] ;重新加载内核 237 retf 238 ;---------------------------------------------------------------- 239 Cal_User_Mem: ;输入ecx:应用程序用到的内存(字节) 240 add [ram_recycled],ecx 241 ret 242 ;---------------------------------------------------------------- 243 Make_Seg_Descriptor: ;构造段描述符 244 ;输入: 245 ;eax:线性基地址 246 ;ebx:段界限 247 ;ecx:属性 248 ;输出: 249 ;eax:段描述符低32位 250 ;edx:段描述符高32位 251 mov edx,eax 252 and edx,0xffff0000 253 rol edx,8 254 bswap edx 255 or edx,ecx 256 257 shl eax,16 258 or ax,bx 259 and ebx,0x000f0000 260 or edx,ebx 261 retf 262 ;---------------------------------------------------------------- 263 Make_Gate_Descriptor: ;构造门描述符 264 ;输入: 265 ;eax:段内偏移地址 266 ;bx: 段的选择子 267 ;cx: 段的属性 268 ;输出: 269 ;eax:门描述符低32位 270 ;edx:门描述符高32位 271 push ebx 272 push ecx 273 274 mov edx,eax 275 and edx,0xffff0000 ;要高16位 276 or dx,cx 277 278 shl ebx,16 279 and eax,0x0000ffff 280 or eax,ebx 281 282 pop ecx 283 pop ebx 284 285 retf 286 ;---------------------------------------------------------------- 287 Set_New_GDT: ;装载新的全局描述符 288 ;输入:edx:eax描述符 289 ;输出:cx选择子 290 push ds 291 push es 292 293 mov ebx,Core_Data_Segement 294 mov ds,ebx 295 296 mov ebx,All_4GB_Segment 297 mov es,ebx 298 299 sgdt [pgdt_base_tmp] 300 301 movzx ebx,word[pgdt_base_tmp] 302 inc bx ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的 303 ;要用到回绕特性 304 add ebx,[pgdt_base_tmp+0x02] ;得到pgdt的线性基地址 305 306 mov [es:ebx],eax 307 mov [es:ebx+0x04],edx ;装载新的gdt符 308 ;装载描述符要装载到实际位置上 309 310 add word[pgdt_base_tmp],8 ;给gdt的段界限加上8(字节) 311 312 lgdt [pgdt_base_tmp] ;加载gdt到gdtr的位置和实际表的位置无关 313 314 mov ax,[pgdt_base_tmp] ;得到段界限 315 xor dx,dx 316 mov bx,8 ;得到gdt大小 317 div bx 318 mov cx,ax 319 shl cx,3 ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级) 320 321 pop es 322 pop ds 323 retf 324 ;---------------------------------------------------------------- 325 Set_New_LDT_To_TCB: ;装载新的局部描述符 326 ;输入:edx:eax描述符 327 ; : ebx:TCB线性基地址 328 ;输出:cx选择子 329 330 push edi 331 push eax 332 push ebx 333 push edx 334 push ds 335 336 mov ecx,All_4GB_Segment 337 mov ds,ecx 338 339 mov edi,[ebx+0x0c] ;LDT的线性基地址 340 movzx ecx,word[ebx+0x0a] 341 inc cx ;得到实际的LDT的大小(界限还要-1) 342 343 mov [edi+ecx+0x00],eax 344 mov [edi+ecx+0x04],edx 345 346 add cx,8 347 dec cx 348 349 mov [ebx+0x0a],cx 350 351 mov ax,cx 352 xor dx,dx 353 mov cx,8 354 div cx 355 356 shl ax,3 357 mov cx,ax 358 or cx,0x0004 ;LDT,第三位TI位一定是1 359 360 pop ds 361 pop edx 362 pop ebx 363 pop eax 364 pop edi 365 retf 366 ;---------------------------------------------------------------- 367 PrintDword: ;显示edx内容的一个调试函数 368 pushad 369 push ds 370 371 mov eax,Core_Data_Segement 372 mov ds,eax 373 374 mov ebx,bin_hex 375 mov ecx,8 376 377 _query: 378 rol edx,4 379 mov eax,edx 380 and eax,0x0000000f 381 xlat 382 383 push ecx 384 mov cl,al 385 call put_char 386 pop ecx 387 388 loop _query 389 390 pop ds 391 popad 392 393 retf 394 ;--------------------------------------------------------------------- 395 load_program_form_user: ;输入push:用户程序逻辑扇区号 396 ; fs: 指向用户程序头部 397 pushad 398 push ds 399 push es 400 401 mov ecx,0x46 ;另一个TCB链 402 call Sys_Routine_Segement:allocate_memory 403 call Sys_Routine_Segement:append_to_tcb 404 405 mov ebp,esp 406 407 mov eax,Core_Data_Segement 408 mov ds,eax 409 410 mov eax,All_4GB_Segment 411 mov es,eax 412 413 mov edi,[ebp+11*4] ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push 414 415 mov ecx,160 416 call Sys_Routine_Segement:allocate_memory 417 mov [es:edi+0x0c],ecx 418 mov word[es:edi+0x0a],0xffff ;初始化LDT界限位0xffff 419 420 mov esi,[ebp+12*4] ;esi必须是逻辑扇区号 421 mov ebx,core_buf ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了) 422 423 push esi 424 push ds 425 push ebx 426 push cs 427 call Sys_Routine_Segement:ReadHarddisk 428 429 mov eax,[core_buf] ;读取用户程序长度 430 431 mov ebx,eax 432 and ebx,0xfffffe00 ;清空低9位(强制对齐512) 433 add ebx,512 434 test eax,0x000001ff 435 cmovnz eax,ebx ;低9位不为0则使用向上取整的结果 436 437 mov ecx,eax ;eax是整个程序的向上取整的大小 438 call Sys_Routine_Segement:allocate_memory 439 ;先分配内存给整个程序,再分配内存给栈区 440 mov ebx,ecx 441 mov [es:edi+0x06],ecx ;tcb 0x06:程序加载基地址 442 443 xor edx,edx 444 mov ecx,512 ;千万不要改掉ebx 445 div ecx 446 mov ecx,eax 447 448 mov eax,All_4GB_Segment 449 mov ds,eax 450 451 _loop_read: 452 push esi 453 push ds 454 push ebx 455 push cs 456 call Sys_Routine_Segement:ReadHarddisk ;esi还是User_Program_Address 457 inc esi 458 add ebx,512 459 loop _loop_read 460 461 mov esi,edi ;esi: TCB的线性基地址 462 mov edi,[es:esi+0x06] ;程序加载的线性基地址 463 464 ;建立头部描述符 465 mov eax,edi 466 mov ebx,[edi+0x04] 467 dec ebx ;段界限 468 mov ecx,0x0040f200 469 call Sys_Routine_Segement:Make_Seg_Descriptor 470 mov ebx,esi 471 call Sys_Routine_Segement:Set_New_LDT_To_TCB 472 or cx,0x0003 ;特权级3 473 mov [es:esi+0x44],cx ;记得要登记头部的选择子 474 mov [edi+0x04],cx 475 476 ;建立代码段描述符 477 mov eax,edi 478 add eax,[edi+0x14] 479 mov ebx,[edi+0x18] 480 dec ebx 481 mov ecx,0x0040f800 482 call Sys_Routine_Segement:Make_Seg_Descriptor 483 mov ebx,esi 484 call Sys_Routine_Segement:Set_New_LDT_To_TCB 485 or cx,0x0003 486 mov [edi+0x14],cx 487 488 ;建立数据段描述符 489 mov eax,edi 490 add eax,[edi+0x1c] 491 mov ebx,[edi+0x20] 492 dec ebx 493 mov ecx,0x0040f200 494 call Sys_Routine_Segement:Make_Seg_Descriptor 495 mov ebx,esi 496 call Sys_Routine_Segement:Set_New_LDT_To_TCB 497 or cx,0x0003 498 mov [edi+0x1c],cx 499 500 ;建立栈段描述符 501 mov ecx,[edi+0x0c] 502 mov ebx,0x000fffff 503 sub ebx,ecx 504 mov eax,4096 ;4KB粒度 505 mul ecx 506 mov ecx,eax 507 call Sys_Routine_Segement:allocate_memory 508 mov eax,ecx ;eax是栈段的线性基地址 509 mov ecx,0x00c0f600 510 call Sys_Routine_Segement:Make_Seg_Descriptor 511 mov ebx,esi 512 call Sys_Routine_Segement:Set_New_LDT_To_TCB 513 or cx,0x0003 514 mov [edi+0x08],cx 515 516 mov eax,[esi+0x0c] ;LDT线性基地址 517 mov [edi+0x32],eax 518 mov ax,[esi+0x0a] ;LDT段界限 519 mov [edi+0x30],ax 520 521 ;现在开始重定位API符号表 522 ;--------------------------------------------------------------------- 523 mov eax,All_4GB_Segment ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问 524 mov es,eax 525 mov eax,Core_Data_Segement 526 mov ds,eax 527 528 cld 529 mov ecx,[es:edi+0x36] ;得到用户程序符号表的条数 530 add edi,0x3a ;用户符号表的偏移地址是0x3a 531 532 _loop_U_SALT: 533 push edi 534 push ecx 535 536 mov ecx,salt_items_sum 537 mov esi,salt 538 539 _loop_C_SALT: 540 push edi 541 push esi 542 push ecx 543 544 mov ecx,64 ;比较256个字节 545 repe cmpsd 546 jne _re_match ;如果成功匹配,那么esi和edi刚好会在数据区之后的 547 548 mov eax,[esi] ;偏移地址 549 mov [es:edi-256],eax ;把偏移地址填入用户程序的符号区 550 mov ax,[esi+0x04] ;段的选择子 551 552 or ax,0x0002 ;把RPL改为3,代表(内核)赋予应用程序以特权级3 553 mov [es:edi-252],ax ;把段的选择子填入用户程序的段选择区 554 555 _re_match: 556 pop ecx 557 pop esi 558 add esi,salt_length 559 pop edi 560 loop _loop_C_SALT 561 562 pop ecx 563 pop edi 564 add edi,256 565 loop _loop_U_SALT 566 ;--------------------------------------------------------------------- 567 ;----------------------填入临时中转任务门选择子----------------------- 568 mov edi,[ebp+11*4] 569 mov edi,[es:edi+0x06] ;从TCB线性基地址中获得用户程序加载的基地址 570 571 mov ax,[salt_tp] 572 mov [es:edi+0x28],ax ;填充任务门选择子 573 ;--------------------------------------------------------------------- 574 575 576 mov esi,[ebp+11*4] ;重新获得TCB的线性基地址 577 578 mov ecx,Switch_Stack_Size 579 mov eax,ecx 580 mov [es:esi+0x1a],ecx 581 shr dword[es:esi+0x1a],12 ;相当于除以4096 582 call Sys_Routine_Segement:allocate_memory 583 add eax,ecx ;得到最高地址 584 mov [es:esi+0x1e],eax ;登记线性基地址 585 mov ebx,0x000fffff 586 sub ebx,[es:esi+0x1a] 587 mov ecx,0x00c09600 ;特权级0 588 call Sys_Routine_Segement:Make_Seg_Descriptor 589 mov ebx,esi 590 call Sys_Routine_Segement:Set_New_LDT_To_TCB 591 or cx,0x0000 ;RPL为0 592 mov [es:esi+0x22],cx 593 mov dword[es:esi+0x24],0 594 595 mov ecx,Switch_Stack_Size 596 mov eax,ecx 597 mov [es:esi+0x28],ecx 598 shr dword[es:esi+0x28],12 ;相当于除以4096 599 call Sys_Routine_Segement:allocate_memory 600 add eax,ecx ;得到最高地址 601 mov [es:esi+0x2c],eax ;登记线性基地址 602 mov ebx,0x000fffff 603 sub ebx,[es:esi+0x28] 604 mov ecx,0x00c0b600 ;特权级1 605 call Sys_Routine_Segement:Make_Seg_Descriptor 606 mov ebx,esi 607 call Sys_Routine_Segement:Set_New_LDT_To_TCB 608 or cx,0x0001 ;RPL为1 609 mov [es:esi+0x30],cx 610 mov dword[es:esi+0x32],0 611 612 mov ecx,Switch_Stack_Size 613 mov eax,ecx 614 mov [es:esi+0x36],ecx 615 shr dword[es:esi+0x36],12 ;相当于除以4096 616 call Sys_Routine_Segement:allocate_memory 617 add eax,ecx ;得到最高地址 618 mov [es:esi+0x3a],eax ;登记线性基地址 619 mov ebx,0x000fffff 620 sub ebx,[es:esi+0x36] 621 mov ecx,0x00c0d600 ;特权级2 622 call Sys_Routine_Segement:Make_Seg_Descriptor 623 mov ebx,esi 624 call Sys_Routine_Segement:Set_New_LDT_To_TCB 625 or cx,0x0002 ;RPL为2 626 mov [es:esi+0x3e],cx 627 mov dword[es:esi+0x40],0 628 629 ;在GDT中存入LDT信息 630 mov eax,[es:esi+0x0c] 631 movzx ebx,word[es:esi+0x0a] 632 mov ecx,0x00408200 ;LDT描述符,特权级0级 633 call Sys_Routine_Segement:Make_Seg_Descriptor 634 call Sys_Routine_Segement:Set_New_GDT 635 mov [es:esi+0x10],cx ;在TCB放入LDT选择子 636 637 ;-------------------在TCB中登记TSS的信息------------------------------- 638 mov ecx,104 ;创建一个最小尺寸的TSS 639 mov [es:esi+0x12],cx 640 dec word[es:esi+0x12] ;记得-1,要的是段界限 641 call Sys_Routine_Segement:allocate_memory 642 mov [es:esi+0x14],ecx ;TSS基地址 643 644 ;构建TSS信息表 645 mov dword[es:ecx+0x00],0 ;没有前一个任务 646 647 mov edx,[es:esi+0x24] ;0栈段 648 mov [es:ecx+4],edx 649 mov dx,[es:esi+0x22] 650 mov [es:ecx+8],dx 651 652 mov edx,[es:esi+0x32] ;1栈段 653 mov [es:ecx+12],edx 654 mov dx,[es:esi+0x30] 655 mov [es:ecx+16],dx 656 657 mov edx,[es:esi+0x40] ;2栈段 658 mov [es:ecx+20],edx 659 mov dx,[es:esi+0x3e] 660 mov [es:ecx+24],dx 661 662 mov edx,[es:esi+0x10] ;LDT选择子 663 mov [es:ecx+96],edx 664 665 mov dx,[es:esi+0x12] ;I/O偏移 666 mov [es:ecx+102],dx ;是102不是104 667 668 mov word[es:ecx+100],0 ;T=0 669 670 mov edi,[es:esi+0x06] ;用户程序的线性基地址 671 672 mov edx,[es:edi+0x10] ;EIP 673 mov [es:ecx+32],edx 674 675 mov edx,[es:edi+0x14] ;CS 676 mov [es:ecx+76],dx 677 678 mov edx,[es:edi+0x08] ;SS 679 mov [es:ecx+80],dx 680 681 mov edx,[es:edi+0x04] ;DS(是指向用户头部,而不是用户程序的数据区) 682 mov [es:ecx+84],dx 683 684 mov word[es:ecx+72],0 ;ES 685 mov word[es:ecx+88],0 ;FS 686 mov word[es:ecx+92],0 ;GS 687 688 pushfd 689 pop edx 690 mov [es:ecx+36],edx ;EFLAGS 691 692 ;在GDT中存入TSS信息,必须放入GDT,这样才能在当前LDT中加入调用门 693 mov eax,[es:esi+0x14] 694 movzx ebx,word[es:esi+0x12] 695 mov ecx,0x00408900 696 call Sys_Routine_Segement:Make_Seg_Descriptor 697 call Sys_Routine_Segement:Set_New_GDT 698 mov [es:esi+0x18],cx 699 700 ;在当前用户程序的LDT中加入需要加载的程序的任务门,且把任务门的选择子写入fs指向的用户程序头部的相应位置 701 mov eax,0x00000 702 mov bx,[es:esi+0x18] 703 mov cx,0xe500 704 705 call Sys_Routine_Segement:Make_Gate_Descriptor 706 call Sys_Routine_Segement:Set_Task_Gate_To_UsersLDT 707 708 pop es 709 pop ds 710 popad 711 retf 4 ;相当于是stdcall,过程清栈 712 ;--------------------------------------------------------------------- 713 append_to_tcb: ;写入新的TCB链 714 ;输入:ecx新的TCB线性基地址 715 pushad 716 717 push ds 718 push es 719 720 mov eax,All_4GB_Segment 721 mov es,eax 722 723 mov eax,Core_Data_Segement 724 mov ds,eax 725 726 mov eax,[tcb_chain] 727 cmp eax,0x00 728 je _notcb 729 730 _search_tcb: 731 mov edx,[tcb_chain+0x00] 732 mov eax,[es:edx] 733 cmp eax,0x00 734 jne _search_tcb 735 736 mov [es:edx+0x00],ecx 737 jmp _out_tcb_search 738 739 _notcb: 740 mov [tcb_chain],ecx 741 742 _out_tcb_search: 743 pop es 744 pop ds 745 746 popad 747 retf 748 ;--------------------------------------------------------------------- 749 Set_Task_Gate_To_UsersLDT: ;输入:eax:任务门描述符的低32位 750 ; edx:任务门描述符的高32位 751 ;输出:无 752 mov edi,[fs:0x32] ;LDT基地址 753 movzx ebx,word[fs:0x30] ;段界限 754 755 inc bx 756 757 mov [es:edi+ebx+0x00],eax 758 mov [es:edi+ebx+0x04],edx 759 760 add bx,8 761 dec bx ;记得要段界限 762 763 mov [fs:0x30],bx ;重新写入 764 765 mov ax,bx 766 xor dx,dx 767 mov bx,8 768 div bx ;得到LDT选择位置 769 770 shl ax,3 771 or ax,0x0004 ;TI=1,特权级是3 772 mov [fs:0x2e],ax 773 774 retf 775 ;--------------------------------------------------------------------- 776 ;========================================================================= 777 ;===========================内核数据区==================================== 778 ;========================================================================= 779 SECTION Core_Data align=16 vstart=0 780 ;------------------------------------------------------------------------------- 781 pgdt_base_tmp: dw 0 ;这一章的用户程序都是从GDT中加载的 782 dd 0 783 784 ram_alloc: dd 0x00100000 ;下次分配内存时的起始地址(直接暴力从0x00100000开始分配了) 785 ram_recycled dd 0 ;这里储存程序实际用的大小 786 salt: 787 salt_1: db '@Printf' ;@Printf函数(公用例程) 788 times 256-($-salt_1) db 0 789 dd put_string 790 dw Sys_Routine_Segement 791 dw 0 ;参数个数 792 793 salt_2: db '@ReadHarddisk' ;@ReadHarddisk函数(公用例程) 794 times 256-($-salt_2) db 0 795 dd ReadHarddisk 796 dw Sys_Routine_Segement 797 dw 4 ;参数个数 798 799 salt_3: db '@PrintDwordAsHexString' ;@PrintDwordAsHexString函数(公用例程) 800 times 256-($-salt_3) db 0 801 dd PrintDword 802 dw Sys_Routine_Segement 803 dw 0 ;参数个数 804 805 salt_4: db '@Fopen' ;@Fopen函数(内核例程) 806 times 256-($-salt_4) db 0 807 dd load_program_form_user 808 dw Sys_Routine_Segement 809 dw 1 ;参数个数 810 811 salt_length: equ $-salt_4 812 salt_items_sum equ ($-salt)/salt_length ;得到项目总数 813 814 salt_tp: dw 0 ;任务门,专门拿来给程序切换到全局空间的 815 816 message_1 db ' If you seen this message,that means we ' 817 db 'are now in protect mode,and the system ' 818 db 'core is loaded,and the video display ' 819 db 'routine works perfectly.',0x0d,0x0a,0 820 821 message_2 db ' Loading user program...',0 822 823 do_status db 'Done.',0x0d,0x0a,0 824 825 message_3 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 826 db ' User program terminated,control returned.' 827 db 0x0d,0x0a,0x0d,0x0a,0 828 message_4 db ' We have been backed to kernel.',0x0d,0x0a,0 829 message_5 db ' The GDT and memory have benn recycled.',0 830 message_6 db ' From the system wide gate:',0x0d,0x0a,0 831 message_7 db ' Setting the gate discriptor...',0 832 message_In_Gate db ' Hi!My name is Philip:',0x0d,0x0a,0 833 834 bin_hex db '0123456789ABCDEF' 835 ;put_hex_dword子过程用的查找表 836 core_buf times 2048 db 0 ;内核用的缓冲区(2049个字节(2MB)) 837 838 esp_pointer dd 0 ;内核用来临时保存自己的栈指针 839 840 cpu_brnd0 db 0x0d,0x0a,' ',0 841 cpu_brand times 52 db 0 842 cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 843 mem_alloc_fail db 'The Program is too large to load' 844 core_ss dw 0 845 core_sp dd 0 846 ;程序管理器的任务信息 847 prgman_tss dd 0 ;程序管理器的TSS基地址 848 dw 0 ;程序管理器的TSS描述符选择子 849 850 prgman_msg1 db 0x0d,0x0a 851 db '[PROGRAM MANAGER]: Hello! I am Program Manager,' 852 db 'run at CPL=0.Now,create user task and switch ' 853 db 'to it by the CALL instruction...',0x0d,0x0a,0 854 855 prgman_msg2 db 0x0d,0x0a 856 db '[PROGRAM MANAGER]: I am glad to regain control.' 857 db 'Now,create another user task and switch to ' 858 db 'it by the JMP instruction...',0x0d,0x0a,0 859 860 prgman_msg3 db 0x0d,0x0a 861 db '[PROGRAM MANAGER]: I am gain control again,',0 862 863 core_msg0 db 0x0d,0x0a 864 db '[SYSTEM CORE]: Uh...This task initiated with ' 865 db 'CALL instruction or an exeception/ interrupt,' 866 db 'should use IRETD instruction to switch back...' 867 db 0x0d,0x0a,0 868 869 core_msg1 db 0x0d,0x0a 870 db '[SYSTEM CORE]: Uh...This task initiated with ' 871 db 'JMP instruction, should switch to Program ' 872 db 'Manager directly by the JMP instruction...' 873 db 0x0d,0x0a,0 874 core_stop db 'HALT...',0 875 876 tcb_chain dd 0 ;任务控制块链头指针 877 ;========================================================================= 878 ;===========================内核代码区==================================== 879 ;========================================================================= 880 SECTION Core_Code align=16 vstart=0 881 load_program: ;输入push1:逻辑扇区号 882 ; push2: 线性基地址 883 pushad 884 push ds 885 push es 886 887 mov ebp,esp ;别忘了把参数传给ebp 888 889 mov eax,Core_Data_Segement 890 mov ds,eax ;切换到内核数据段 891 892 mov eax,All_4GB_Segment 893 mov es,eax 894 895 mov edi,[ebp+11*4] ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push 896 897 mov ecx,160 898 call Sys_Routine_Segement:allocate_memory 899 mov [es:edi+0x0c],ecx 900 mov word[es:edi+0x0a],0xffff ;初始化LDT界限位0xffff 901 902 mov esi,[ebp+12*4] ;esi必须是逻辑扇区号 903 mov ebx,core_buf ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了) 904 905 push esi 906 push ds 907 push ebx 908 push cs 909 call Sys_Routine_Segement:ReadHarddisk 910 911 mov eax,[core_buf] ;读取用户程序长度 912 913 mov ebx,eax 914 and ebx,0xfffffe00 ;清空低9位(强制对齐512) 915 add ebx,512 916 test eax,0x000001ff 917 cmovnz eax,ebx ;低9位不为0则使用向上取整的结果 918 919 mov ecx,eax ;eax是整个程序的向上取整的大小 920 call Sys_Routine_Segement:allocate_memory 921 ;先分配内存给整个程序,再分配内存给栈区 922 mov ebx,ecx 923 mov [es:edi+0x06],ecx ;tcb 0x06:程序加载基地址 924 925 xor edx,edx 926 mov ecx,512 ;千万不要改掉ebx 927 div ecx 928 mov ecx,eax 929 930 mov eax,All_4GB_Segment ;切换到4GB段区域(平坦模式) 931 mov ds,eax 932 933 _loop_read_u: 934 push esi 935 push ds 936 push ebx 937 push cs 938 call Sys_Routine_Segement:ReadHarddisk ;esi还是User_Program_Address 939 inc esi 940 add ebx,512 941 loop _loop_read_u 942 943 mov esi,edi ;esi: TCB的线性基地址 944 mov edi,[es:esi+0x06] ;程序加载的线性基地址 945 946 ;建立头部描述符 947 mov eax,edi 948 mov ebx,[edi+0x04] 949 dec ebx ;段界限 950 mov ecx,0x0040f200 951 call Sys_Routine_Segement:Make_Seg_Descriptor 952 mov ebx,esi 953 call Sys_Routine_Segement:Set_New_LDT_To_TCB 954 or cx,0x0003 ;特权级3 955 mov [es:esi+0x44],cx ;记得要登记头部的选择子 956 mov [edi+0x04],cx 957 958 ;建立代码段描述符 959 mov eax,edi 960 add eax,[edi+0x14] 961 mov ebx,[edi+0x18] 962 dec ebx 963 mov ecx,0x0040f800 964 call Sys_Routine_Segement:Make_Seg_Descriptor 965 mov ebx,esi 966 call Sys_Routine_Segement:Set_New_LDT_To_TCB 967 or cx,0x0003 968 mov [edi+0x14],cx 969 970 ;建立数据段描述符 971 mov eax,edi 972 add eax,[edi+0x1c] 973 mov ebx,[edi+0x20] 974 dec ebx 975 mov ecx,0x0040f200 976 call Sys_Routine_Segement:Make_Seg_Descriptor 977 mov ebx,esi 978 call Sys_Routine_Segement:Set_New_LDT_To_TCB 979 or cx,0x0003 980 mov [edi+0x1c],cx 981 982 ;建立栈段描述符 983 mov ecx,[edi+0x0c] 984 mov ebx,0x000fffff 985 sub ebx,ecx 986 mov eax,4096 ;4KB粒度 987 mul ecx 988 mov ecx,eax 989 call Sys_Routine_Segement:allocate_memory 990 mov eax,ecx ;eax是栈段的线性基地址 991 mov ecx,0x00c0f600 992 call Sys_Routine_Segement:Make_Seg_Descriptor 993 mov ebx,esi 994 call Sys_Routine_Segement:Set_New_LDT_To_TCB 995 or cx,0x0003 996 mov [edi+0x08],cx 997 998 mov eax,[esi+0x0c] ;LDT线性基地址 999 mov [edi+0x32],eax 1000 mov ax,[esi+0x0a] ;LDT段界限 1001 mov [edi+0x30],ax 1002 1003 ;现在开始重定位API符号表 1004 ;--------------------------------------------------------------------- 1005 mov eax,All_4GB_Segment ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问 1006 mov es,eax 1007 mov eax,Core_Data_Segement 1008 mov ds,eax 1009 1010 cld 1011 mov ecx,[es:edi+0x36] ;得到用户程序符号表的条数 1012 add edi,0x3a ;用户符号表的偏移地址是0x3a 1013 1014 _loop_U_SALT_u: 1015 push edi 1016 push ecx 1017 1018 mov ecx,salt_items_sum 1019 mov esi,salt 1020 1021 _loop_C_SALT_u: 1022 push edi 1023 push esi 1024 push ecx 1025 1026 mov ecx,64 ;比较256个字节 1027 repe cmpsd 1028 jne _re_match_u ;如果成功匹配,那么esi和edi刚好会在数据区之后的 1029 1030 mov eax,[esi] ;偏移地址 1031 mov [es:edi-256],eax ;把偏移地址填入用户程序的符号区 1032 mov ax,[esi+0x04] ;段的选择子 1033 1034 or ax,0x0002 ;把RPL改为3,代表(内核)赋予应用程序以特权级3 1035 mov [es:edi-252],ax ;把段的选择子填入用户程序的段选择区 1036 1037 _re_match_u: 1038 pop ecx 1039 pop esi 1040 add esi,salt_length 1041 pop edi 1042 loop _loop_C_SALT_u 1043 1044 pop ecx 1045 pop edi 1046 add edi,256 1047 loop _loop_U_SALT_u 1048 ;--------------------------------------------------------------------- 1049 ;----------------------填入临时中转任务门选择子----------------------- 1050 mov edi,[ebp+11*4] 1051 mov edi,[es:edi+0x06] ;从TCB线性基地址中获得用户程序加载的基地址 1052 1053 mov ax,[salt_tp] 1054 mov [es:edi+0x28],ax ;填充任务门选择子 1055 ;--------------------------------------------------------------------- 1056 mov esi,[ebp+11*4] ;重新获得TCB的线性基地址 1057 1058 ;现在设置所有的特权级栈段,并且把特权级栈段放到TCB中(为了等一下设置TSS) 1059 ;设置TSS特权0级栈段(暂存在TCB中) 1060 mov ecx,Switch_Stack_Size 1061 mov eax,ecx 1062 mov [es:esi+0x1a],ecx 1063 shr dword[es:esi+0x1a],12 ;相当于除以4096 1064 call Sys_Routine_Segement:allocate_memory 1065 add eax,ecx ;得到最高地址 1066 mov [es:esi+0x1e],eax ;登记线性基地址 1067 mov ebx,0x000fffff 1068 sub ebx,[es:esi+0x1a] 1069 mov ecx,0x00c09600 ;特权级0 1070 call Sys_Routine_Segement:Make_Seg_Descriptor 1071 mov ebx,esi 1072 call Sys_Routine_Segement:Set_New_LDT_To_TCB 1073 or cx,0x0000 ;RPL为0 1074 mov [es:esi+0x22],cx 1075 mov dword[es:esi+0x24],0 1076 1077 ;设置TSS特权1级栈段(暂存在TCB中) 1078 mov ecx,Switch_Stack_Size 1079 mov eax,ecx 1080 mov [es:esi+0x28],ecx 1081 shr dword[es:esi+0x28],12 ;相当于除以4096 1082 call Sys_Routine_Segement:allocate_memory 1083 add eax,ecx ;得到最高地址 1084 mov [es:esi+0x2c],eax ;登记线性基地址 1085 mov ebx,0x000fffff 1086 sub ebx,[es:esi+0x28] 1087 mov ecx,0x00c0b600 ;特权级1 1088 call Sys_Routine_Segement:Make_Seg_Descriptor 1089 mov ebx,esi 1090 call Sys_Routine_Segement:Set_New_LDT_To_TCB 1091 or cx,0x0001 ;RPL为1 1092 mov [es:esi+0x30],cx 1093 mov dword[es:esi+0x32],0 1094 1095 ;设置TSS特权2级栈段(暂存在TCB中) 1096 mov ecx,Switch_Stack_Size 1097 mov eax,ecx 1098 mov [es:esi+0x36],ecx 1099 shr dword[es:esi+0x36],12 ;相当于除以4096 1100 call Sys_Routine_Segement:allocate_memory 1101 add eax,ecx ;得到最高地址 1102 mov [es:esi+0x3a],eax ;登记线性基地址 1103 mov ebx,0x000fffff 1104 sub ebx,[es:esi+0x36] 1105 mov ecx,0x00c0d600 ;特权级2 1106 call Sys_Routine_Segement:Make_Seg_Descriptor 1107 mov ebx,esi 1108 call Sys_Routine_Segement:Set_New_LDT_To_TCB 1109 or cx,0x0002 ;RPL为2 1110 mov [es:esi+0x3e],cx 1111 mov dword[es:esi+0x40],0 1112 1113 ;在GDT中存入LDT信息 1114 mov eax,[es:esi+0x0c] 1115 movzx ebx,word[es:esi+0x0a] 1116 mov ecx,0x00408200 ;LDT描述符,特权级0级 1117 call Sys_Routine_Segement:Make_Seg_Descriptor 1118 call Sys_Routine_Segement:Set_New_GDT 1119 mov [es:esi+0x10],cx ;在TCB放入LDT选择子 1120 1121 ;在TCB中登记TSS的信息 1122 mov ecx,104 ;创建一个最小尺寸的TSS 1123 mov [es:esi+0x12],cx 1124 dec word[es:esi+0x12] ;记得-1,要的是段界限 1125 call Sys_Routine_Segement:allocate_memory 1126 mov [es:esi+0x14],ecx ;TSS基地址 1127 1128 ;构建TSS信息表 1129 mov dword[es:ecx+0x00],0 ;没有前一个任务 1130 1131 mov edx,[es:esi+0x24] ;0栈段 1132 mov [es:ecx+4],edx 1133 mov dx,[es:esi+0x22] 1134 mov [es:ecx+8],dx 1135 1136 mov edx,[es:esi+0x32] ;1栈段 1137 mov [es:ecx+12],edx 1138 mov dx,[es:esi+0x30] 1139 mov [es:ecx+16],dx 1140 1141 mov edx,[es:esi+0x40] ;2栈段 1142 mov [es:ecx+20],edx 1143 mov dx,[es:esi+0x3e] 1144 mov [es:ecx+24],dx 1145 1146 mov edx,[es:esi+0x10] ;LDT选择子 1147 mov [es:ecx+96],edx 1148 1149 mov dx,[es:esi+0x12] ;I/O偏移 1150 mov [es:ecx+102],dx ;是102不是104 1151 1152 mov word[es:ecx+100],0 ;T=0 1153 1154 mov edi,[es:esi+0x06] ;用户程序的线性基地址 1155 1156 mov edx,[es:edi+0x10] ;EIP 1157 mov [es:ecx+32],edx 1158 1159 mov edx,[es:edi+0x14] ;CS 1160 mov [es:ecx+76],dx 1161 1162 mov edx,[es:edi+0x08] ;SS 1163 mov [es:ecx+80],dx 1164 1165 mov edx,[es:edi+0x04] ;DS(是指向用户头部,而不是用户程序的数据区) 1166 mov [es:ecx+84],dx 1167 1168 mov word[es:ecx+72],0 ;ES 1169 mov word[es:ecx+88],0 ;FS 1170 mov word[es:ecx+92],0 ;GS 1171 1172 pushfd 1173 pop edx 1174 mov [es:ecx+36],edx ;EFLAGS 1175 1176 ;在GDT中存入TSS信息 1177 mov eax,[es:esi+0x14] 1178 movzx ebx,word[es:esi+0x12] 1179 mov ecx,0x00408900 1180 call Sys_Routine_Segement:Make_Seg_Descriptor 1181 call Sys_Routine_Segement:Set_New_GDT 1182 mov [es:esi+0x18],cx 1183 1184 pop es 1185 pop ds 1186 popad 1187 ret 8 ;相当于是stdcall,过程清栈 1188 ;--------------------------------------------------------------------- 1189 start: 1190 mov eax,Core_Data_Segement 1191 mov ds,eax 1192 1193 mov ebx,message_1 1194 call Sys_Routine_Segement:put_string 1195 1196 _@load: 1197 mov ebx,message_7 1198 call Sys_Routine_Segement:put_string 1199 ;----------------------------安装门------------------------------------ 1200 mov edi,salt 1201 mov ecx,salt_items_sum 1202 _set_gate: 1203 push ecx 1204 mov eax,[edi+256] 1205 mov bx,[edi+260] ;选择子 1206 mov cx,0xec00 ;门是特权级是3的门,那么任何程序都能调用 1207 or cx,[edi+262] ;加上参数个数 1208 1209 call Sys_Routine_Segement:Make_Gate_Descriptor 1210 call Sys_Routine_Segement:Set_New_GDT 1211 mov [edi+260],cx ;回填选择子 1212 add edi,salt_length 1213 pop ecx 1214 loop _set_gate 1215 ;---------------------------------------------------------------------- 1216 mov ebx,do_status 1217 call far [salt_1+256] 1218 mov ebx,message_6 1219 call far [salt_1+256] 1220 mov ebx,message_In_Gate 1221 call far [salt_1+256] ;调用门显示字符信息(忽略偏移地址(前4字节)) 1222 1223 mov ebx,message_4 1224 call far [salt_1+256] 1225 mov ebx,message_2 1226 call far [salt_1+256] 1227 1228 mov eax,All_4GB_Segment 1229 mov es,eax 1230 1231 mov ecx,104 1232 call Sys_Routine_Segement:allocate_memory 1233 mov [prgman_tss],ecx ;保留线性基地址 1234 1235 mov word[es:ecx+100],0 ;TI=0 1236 mov word[es:ecx+96],0 ;任务允许没有自己的LDT 1237 mov dword[es:ecx+28],0 ;设置CR3 1238 mov word[es:ecx+0],0 ;没有前一个任务 1239 mov word[es:ecx+102],103 ;任务管理器不需要I/O映射,要大于等于界限 1240 1241 mov eax,ecx 1242 mov ebx,103 ;TSS段界限 1243 mov ecx,0x00408900 1244 call Sys_Routine_Segement:Make_Seg_Descriptor 1245 call Sys_Routine_Segement:Set_New_GDT 1246 mov [prgman_tss+0x04],cx 1247 1248 ltr cx ;启动任务 1249 ;------------------安装用户管理程序的临时返回任务门-------------------- 1250 mov eax,0x0000 ;TSS不需要偏移地址 1251 mov bx,[prgman_tss+0x04] ;TSS的选择子 1252 mov cx,0xe500 1253 1254 call Sys_Routine_Segement:Make_Gate_Descriptor 1255 call Sys_Routine_Segement:Set_New_GDT 1256 mov [salt_tp],cx ;填入临时中转任务门选择子,注意不需要加260了 1257 ;---------------------------------------------------------------------- 1258 mov ebx,prgman_msg1 1259 call Sys_Routine_Segement:put_string 1260 1261 1262 ;----------------------用户管理程序---------------------------- 1263 mov ecx,0x46 ;TCB链大小 1264 call Sys_Routine_Segement:allocate_memory 1265 call Sys_Routine_Segement:append_to_tcb 1266 1267 push User_Program_AddressA 1268 push ecx 1269 call load_program 1270 1271 jmp far [es:ecx+0x14] ;打开程序A,跳转方式打开 1272 1273 mov eax,Core_Data_Segement 1274 mov ds,eax 1275 1276 mov ebx,prgman_msg3 1277 call Sys_Routine_Segement:put_string 1278 mov ebx,core_stop 1279 call Sys_Routine_Segement:put_string 1280 1281 cli 1282 hlt 1283 ;---------------------------------------------------------------------- 1284 ;========================================================================= 1285 SECTION core_trail 1286 ;---------------------------------------------------------------- 1287 Program_end:
用户程序改动挺大的。
1 ;==============================用户程序A======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序总长度#0x00 5 6 head_len dd header_end ;程序头部的长度#0x04 7 8 stack_seg dd 0 ;用于接收堆栈段选择子#0x08 9 stack_len dd 1 ;程序建议的堆栈大小#0x0c 10 ;以4KB为单位 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代码段位置#0x14 14 code_len dd code_end ;代码段长度#0x18 15 16 data_seg dd section.data.start ;数据段位置#0x1c 17 data_len dd data_end ;数据段长度#0x20 18 ;------------------------------------------------------------------------------- 19 TpBack: dd 0 ;管理程序任务门的偏移地址没用,直接填充就可以了 20 ;#0x24 21 dw 0 ;管理程序任务门的选择子 22 ;#0x28 23 24 LoadPos: dd 0 ;用户程序想加载程序的位置,直接填充就可以了,任务门用不到 25 ;#0x2a 26 dw 0 ;另一个用户程序任务门的选择子 27 ;#0x2e 28 29 Users_LDT_Add dw 0 ;用户程序自己的LDT段界限 30 ;#0x30 31 dd 0 ;用户程序自己的LDT基地址 32 ;#0x32 33 ;------------------------------------------------------------------------------- 34 ;符号地址检索表 35 salt_items dd (u_salt_end-salt)/256 ;#0x36 36 37 salt: ;#0x3a 38 Printf: db '@Printf' 39 times 256-($-Printf) db 0 40 41 TerminateProgram: db '@TerminateProgram' 42 times 256-($-TerminateProgram) db 0 43 44 ReadHarddisk: db '@ReadHarddisk' 45 times 256-($-ReadHarddisk) db 0 46 47 Fopen: db '@Fopen' 48 times 256-($-Fopen) db 0 49 50 u_salt_end: 51 header_end: 52 ;=============================================================================== 53 SECTION data align=16 vstart=0 54 55 message_1 db 0x0d,0x0a 56 db '[USER TASKA]: Hi! I am task PhilipA',0x0d,0x0a,0 57 58 message_2 db '[USER TASKA]: Now I will load PhilipB',0x0d,0x0a,0 59 data_end: 60 ;=============================================================================== 61 [bits 32] 62 ;=============================================================================== 63 SECTION code align=16 vstart=0 64 start: 65 User_Program_AddressB equ 80 ;用户程序所在逻辑扇区 66 mov eax,ds 67 mov fs,eax 68 69 mov eax,[data_seg] 70 mov ds,eax 71 72 mov ebx,message_1 73 call far [fs:Printf] 74 75 mov ebx,message_2 76 call far [fs:Printf] 77 78 push User_Program_AddressB 79 call far [fs:Fopen] 80 81 jmp far [fs:LoadPos] ;直接加载想要加载的用户程序B 82 83 jmp far [fs:TpBack] ;回到任务管理程序 84 code_end: 85 86 ;=============================================================================== 87 SECTION trail 88 ;------------------------------------------------------------------------------- 89 program_end:
1 ;==============================用户程序B======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序总长度#0x00 5 6 head_len dd header_end ;程序头部的长度#0x04 7 8 stack_seg dd 0 ;用于接收堆栈段选择子#0x08 9 stack_len dd 1 ;程序建议的堆栈大小#0x0c 10 ;以4KB为单位 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代码段位置#0x14 14 code_len dd code_end ;代码段长度#0x18 15 16 data_seg dd section.data.start ;数据段位置#0x1c 17 data_len dd data_end ;数据段长度#0x20 18 ;------------------------------------------------------------------------------- 19 TpBack: dd 0 ;管理程序任务门的偏移地址没用,直接填充就可以了 20 ;#0x24 21 dw 0 ;管理程序任务门的选择子 22 ;#0x28 23 24 LoadPos: dd 0 ;用户程序想加载程序的位置,直接填充就可以了,任务门用不到 25 ;#0x2a 26 dw 0 ;另一个用户程序任务门的选择子 27 ;#0x2e 28 29 Users_LDT_Add dw 0 ;用户程序自己的LDT段界限 30 ;#0x30 31 dd 0 ;用户程序自己的LDT基地址 32 ;#0x32 33 ;------------------------------------------------------------------------------- 34 ;符号地址检索表 35 salt_items dd (u_salt_end-salt)/256 ;#0x36 36 37 salt: ;#0x3a 38 Printf: db '@Printf' 39 times 256-($-Printf) db 0 40 41 TerminateProgram: db '@TerminateProgram' 42 times 256-($-TerminateProgram) db 0 43 44 ReadHarddisk: db '@ReadHarddisk' 45 times 256-($-ReadHarddisk) db 0 46 47 Fopen: db '@Fopen' 48 times 256-($-Fopen) db 0 49 u_salt_end: 50 header_end: 51 ;=============================================================================== 52 SECTION data align=16 vstart=0 53 54 message_1 db '[USER TASKB]: Hi! I am PhilipB',0x0d,0x0a 55 db '[USER TASKB]: Now I will back to program manager',0x0d,0x0a,0 56 data_end: 57 58 ;=============================================================================== 59 [bits 32] 60 ;=============================================================================== 61 SECTION code align=16 vstart=0 62 start: 63 mov eax,ds 64 mov fs,eax 65 66 mov eax,[data_seg] 67 mov ds,eax 68 69 mov ebx,message_1 70 call far [fs:Printf] 71 jmp far [fs:TpBack] ;直接回任务管理程序 72 code_end: 73 74 ;=============================================================================== 75 SECTION trail 76 ;------------------------------------------------------------------------------- 77 program_end:
4. 习题3
习题三本来是书上没有的,但是我自己加上去了,其实我还是对任务门的机制很感兴趣的,所以我把第一题写了一个新的。
1 ;===============================内核程序================================= 2 ;定义内核所要用到的选择子 3 All_4GB_Segment equ 0x0008 ;4GB的全内存区域 4 Stack_Segement equ 0x0018 ;内核栈区 5 Print_Segement equ 0x0020 ;显存映射区 6 Sys_Routine_Segement equ 0x0028 ;公用例程段 7 Core_Data_Segement equ 0x0030 ;内核数据区 8 Core_Code_Segement equ 0x0038 ;内核代码段 9 ;---------------------------------------------------------------- 10 User_Program_AddressA equ 50 ;用户程序所在逻辑扇区 11 User_Program_AddressB equ 80 ;用户程序所在逻辑扇区 12 Switch_Stack_Size equ 4096 ;切换栈段的大小 13 ;=============================内核程序头部=============================== 14 SECTION header vstart=0 15 Program_Length dd Program_end ;内核总长度 16 Sys_Routine_Seg dd section.Sys_Routine.start ;公用例程段线性地址 17 Core_Data_Seg dd section.Core_Data.start ;内核数据区线性地址 18 Core_Code_Seg dd section.Core_Code.start ;内核代码区线性地址 19 Code_Entry dd start ;注意偏移地址一定是32位的 20 dw Core_Code_Segement 21 ;---------------------------------------------------------------- 22 [bits 32] 23 ;========================================================================= 24 ;============================公用例程区=================================== 25 ;========================================================================= 26 SECTION Sys_Routine align=16 vstart=0 27 ReadHarddisk: ;push1:28位磁盘号(esi) 28 ;push2:应用程序数据段选择子(ax->ds) 29 ;push3: 偏移地址(ebx) 30 ;push4: 应用程序代码段选择子(dx) 31 pushad 32 push ds 33 push es 34 35 mov ebp,esp 36 37 mov esi,[ebp+15*4] 38 movzx eax,word[ebp+14*4] 39 mov ebx,[ebp+13*4] 40 movzx edx,word[ebp+12*4] 41 42 arpl ax,dx 43 mov ds,ax 44 45 mov dx,0x1f2 46 mov al,0x01 ;读一个扇区 47 out dx,al 48 49 inc edx ;0-7位 50 mov eax,esi 51 out dx,al 52 53 inc edx ;8-15位 54 mov al,ah 55 out dx,al 56 57 inc edx ;16-23位 58 shr eax,16 59 out dx,al 60 61 inc edx ;24-28位,主硬盘,LBA模式 62 mov al,ah 63 and al,0x0f 64 or al,0xe0 65 out dx,al 66 67 inc edx 68 mov al,0x20 69 out dx,al 70 71 _wait: 72 in al,dx 73 and al,0x88 74 cmp al,0x08 75 jne _wait 76 77 mov dx,0x1f0 78 mov ecx,256 79 80 _read: 81 in ax,dx 82 mov [ebx],ax 83 add ebx,2 84 loop _read 85 86 pop es 87 pop ds 88 popad 89 retf 16 ;4个数据 90 ;---------------------------------------------------------------- 91 put_string: ;ebx:偏移地址 92 pushad 93 push ds 94 push es 95 96 _print: 97 mov cl,[ebx] 98 cmp cl,0 99 je _exit 100 call put_char 101 inc ebx 102 jmp _print 103 _exit: 104 pop es 105 pop ds 106 popad 107 retf ;段间返回 108 ;-------------------------------------------------------------- 109 put_char: ;cl就是要显示的字符 110 push ebx 111 push es 112 push ds 113 114 mov dx,0x3d4 115 mov al,0x0e ;高8位 116 out dx,al 117 mov dx,0x3d5 118 in al,dx 119 mov ah,al ;先把高8位存起来 120 mov dx,0x3d4 121 mov al,0x0f ;低8位 122 out dx,al 123 mov dx,0x3d5 124 in al,dx ;现在ax就是当前光标的位置 125 126 _judge: 127 cmp cl,0x0a 128 je _set_0x0a 129 cmp cl,0x0d 130 je _set_0x0d 131 _print_visible: 132 mov bx,ax 133 mov eax,Print_Segement 134 mov es,eax 135 shl bx,1 ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍 136 mov [es:bx],cl ;注意这里是屏幕! 137 mov byte[es:bx+1],0x07 138 add bx,2 139 shr bx,1 140 jmp _roll_screen 141 _set_0x0d: ;回车 142 mov bl,80 143 div bl 144 mul bl 145 mov bx,ax 146 jmp _set_cursor 147 _set_0x0a: ;换行 148 mov bx,ax 149 add bx,80 150 jmp _roll_screen 151 _roll_screen: 152 cmp bx,2000 153 jl _set_cursor 154 mov eax,Print_Segement 155 mov ds,eax 156 mov es,eax 157 158 cld 159 mov edi,0x00 160 mov esi,0xa0 161 mov ecx,1920 162 rep movsw 163 _cls: 164 mov bx,3840 165 mov ecx,80 166 _print_blank: 167 mov word[es:bx],0x0720 168 add bx,2 169 loop _print_blank 170 mov bx,1920 ;别总是忘了光标的位置! 171 _set_cursor: ;改变后的光标位置在bx上 172 mov dx,0x3d4 173 mov al,0x0f ;低8位 174 out dx,al 175 176 mov al,bl 177 mov dx,0x3d5 178 out dx,al 179 180 mov dx,0x3d4 181 mov al,0x0e ;高8位 182 out dx,al 183 184 mov al,bh 185 mov dx,0x3d5 186 out dx,al 187 188 pop ds 189 pop es 190 pop ebx 191 ret 192 ;---------------------------------------------------------------- 193 allocate_memory: ;简易内存分配策略 194 ;输入ecx:想要分配的总字节数 195 ;输出ecx:分配的线性基地址 196 push ds 197 push eax 198 push ebx 199 call Cal_User_Mem 200 201 mov eax,Core_Data_Segement 202 mov ds,eax 203 mov eax,[ram_alloc] 204 mov edx,eax ;edx暂存一下eax 205 add eax,ecx 206 207 cmp eax,edx ;发现新分配的现地址比原来的还小,说明已经溢出 208 jge _alloc 209 mov ebx,mem_alloc_fail 210 call Sys_Routine_Segement:put_string 211 mov ecx,0 ;分配为0说明已经分配失败 212 jmp _exit1 213 _alloc: 214 215 mov ebx,eax 216 and ebx,0xfffffffc 217 add ebx,4 ;强行向上取整 218 test eax,0x00000003 219 cmovnz eax,ebx 220 mov ecx,[ram_alloc] ;要返回要分配的初始地址 221 mov [ram_alloc],eax ;下一次分配的线性基地址 222 223 _exit1: 224 pop ebx 225 pop eax 226 pop ds 227 228 retf 229 ;---------------------------------------------------------------- 230 recycled_memory_and_gdt: 231 mov eax,[ram_recycled] 232 sub [ram_alloc],eax 233 mov dword[ram_recycled],0 ;因为我们还没学到多任务,先这样简单地清零 234 235 sgdt [pgdt_base_tmp] 236 sub word[pgdt_base_tmp],16 ;应用程序的LDT,TSS 237 lgdt [pgdt_base_tmp] ;重新加载内核 238 retf 239 ;---------------------------------------------------------------- 240 Cal_User_Mem: ;输入ecx:应用程序用到的内存(字节) 241 add [ram_recycled],ecx 242 ret 243 ;---------------------------------------------------------------- 244 Make_Seg_Descriptor: ;构造段描述符 245 ;输入: 246 ;eax:线性基地址 247 ;ebx:段界限 248 ;ecx:属性 249 ;输出: 250 ;eax:段描述符低32位 251 ;edx:段描述符高32位 252 mov edx,eax 253 and edx,0xffff0000 254 rol edx,8 255 bswap edx 256 or edx,ecx 257 258 shl eax,16 259 or ax,bx 260 and ebx,0x000f0000 261 or edx,ebx 262 retf 263 ;---------------------------------------------------------------- 264 Make_Gate_Descriptor: ;构造门描述符 265 ;输入: 266 ;eax:段内偏移地址 267 ;bx: 段的选择子 268 ;cx: 段的属性 269 ;输出: 270 ;eax:门描述符低32位 271 ;edx:门描述符高32位 272 push ebx 273 push ecx 274 275 mov edx,eax 276 and edx,0xffff0000 ;要高16位 277 or dx,cx 278 279 shl ebx,16 280 and eax,0x0000ffff 281 or eax,ebx 282 283 pop ecx 284 pop ebx 285 286 retf 287 ;---------------------------------------------------------------- 288 Set_New_GDT: ;装载新的全局描述符 289 ;输入:edx:eax描述符 290 ;输出:cx选择子 291 push ds 292 push es 293 294 mov ebx,Core_Data_Segement 295 mov ds,ebx 296 297 mov ebx,All_4GB_Segment 298 mov es,ebx 299 300 sgdt [pgdt_base_tmp] 301 302 movzx ebx,word[pgdt_base_tmp] 303 inc bx ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的 304 ;要用到回绕特性 305 add ebx,[pgdt_base_tmp+0x02] ;得到pgdt的线性基地址 306 307 mov [es:ebx],eax 308 mov [es:ebx+0x04],edx ;装载新的gdt符 309 ;装载描述符要装载到实际位置上 310 311 add word[pgdt_base_tmp],8 ;给gdt的段界限加上8(字节) 312 313 lgdt [pgdt_base_tmp] ;加载gdt到gdtr的位置和实际表的位置无关 314 315 mov ax,[pgdt_base_tmp] ;得到段界限 316 xor dx,dx 317 mov bx,8 ;得到gdt大小 318 div bx 319 mov cx,ax 320 shl cx,3 ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级) 321 322 pop es 323 pop ds 324 retf 325 ;---------------------------------------------------------------- 326 Set_New_LDT_To_TCB: ;装载新的局部描述符 327 ;输入:edx:eax描述符 328 ; : ebx:TCB线性基地址 329 ;输出:cx选择子 330 331 push edi 332 push eax 333 push ebx 334 push edx 335 push ds 336 337 mov ecx,All_4GB_Segment 338 mov ds,ecx 339 340 mov edi,[ebx+0x0c] ;LDT的线性基地址 341 movzx ecx,word[ebx+0x0a] 342 inc cx ;得到实际的LDT的大小(界限还要-1) 343 344 mov [edi+ecx+0x00],eax 345 mov [edi+ecx+0x04],edx 346 347 add cx,8 348 dec cx 349 350 mov [ebx+0x0a],cx 351 352 mov ax,cx 353 xor dx,dx 354 mov cx,8 355 div cx 356 357 shl ax,3 358 mov cx,ax 359 or cx,0x0004 ;LDT,第三位TI位一定是1 360 361 pop ds 362 pop edx 363 pop ebx 364 pop eax 365 pop edi 366 retf 367 ;---------------------------------------------------------------- 368 PrintDword: ;显示edx内容的一个调试函数 369 pushad 370 push ds 371 372 mov eax,Core_Data_Segement 373 mov ds,eax 374 375 mov ebx,bin_hex 376 mov ecx,8 377 378 _query: 379 rol edx,4 380 mov eax,edx 381 and eax,0x0000000f 382 xlat 383 384 push ecx 385 mov cl,al 386 call put_char 387 pop ecx 388 389 loop _query 390 391 pop ds 392 popad 393 394 retf 395 ;========================================================================= 396 ;===========================内核数据区==================================== 397 ;========================================================================= 398 SECTION Core_Data align=16 vstart=0 399 ;------------------------------------------------------------------------------- 400 pgdt_base_tmp: dw 0 ;这一章的用户程序都是从GDT中加载的 401 dd 0 402 403 ram_alloc: dd 0x00100000 ;下次分配内存时的起始地址(直接暴力从0x00100000开始分配了) 404 ram_recycled dd 0 ;这里储存程序实际用的大小 405 salt: 406 salt_1: db '@Printf' ;@Printf函数(公用例程) 407 times 256-($-salt_1) db 0 408 dd put_string 409 dw Sys_Routine_Segement 410 dw 0 ;参数个数 411 412 salt_2: db '@ReadHarddisk' ;@ReadHarddisk函数(公用例程) 413 times 256-($-salt_2) db 0 414 dd ReadHarddisk 415 dw Sys_Routine_Segement 416 dw 4 ;参数个数 417 418 salt_3: db '@PrintDwordAsHexString' ;@PrintDwordAsHexString函数(公用例程) 419 times 256-($-salt_3) db 0 420 dd PrintDword 421 dw Sys_Routine_Segement 422 dw 0 ;参数个数 423 424 salt_length: equ $-salt_3 425 salt_items_sum equ ($-salt)/salt_length ;得到项目总数 426 427 salt_tp: dw 0 ;任务门,专门拿来给程序切换到全局空间的 428 429 message_1 db ' If you seen this message,that means we ' 430 db 'are now in protect mode,and the system ' 431 db 'core is loaded,and the video display ' 432 db 'routine works perfectly.',0x0d,0x0a,0 433 434 message_2 db ' Loading user program...',0 435 436 do_status db 'Done.',0x0d,0x0a,0 437 438 message_3 db 0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 439 db ' User program terminated,control returned.' 440 db 0x0d,0x0a,0x0d,0x0a,0 441 message_4 db ' We have been backed to kernel.',0x0d,0x0a,0 442 message_5 db ' The GDT and memory have benn recycled.',0 443 message_6 db ' From the system wide gate:',0x0d,0x0a,0 444 message_7 db ' Setting the gate discriptor...',0 445 message_In_Gate db ' Hi!My name is Philip:',0x0d,0x0a,0 446 447 bin_hex db '0123456789ABCDEF' 448 ;put_hex_dword子过程用的查找表 449 core_buf times 2048 db 0 ;内核用的缓冲区(2049个字节(2MB)) 450 451 esp_pointer dd 0 ;内核用来临时保存自己的栈指针 452 453 cpu_brnd0 db 0x0d,0x0a,' ',0 454 cpu_brand times 52 db 0 455 cpu_brnd1 db 0x0d,0x0a,0x0d,0x0a,0 456 mem_alloc_fail db 'The Program is too large to load' 457 core_ss dw 0 458 core_sp dd 0 459 ;程序管理器的任务信息 460 prgman_tss dd 0 ;程序管理器的TSS基地址 461 dw 0 ;程序管理器的TSS描述符选择子 462 463 prgman_msg1 db 0x0d,0x0a 464 db '[PROGRAM MANAGER]: Hello! I am Program Manager,' 465 db 'run at CPL=0.Now,create user task and switch ' 466 db 'to it by the CALL instruction...',0x0d,0x0a,0 467 468 prgman_msg2 db 0x0d,0x0a 469 db '[PROGRAM MANAGER]: I am glad to regain control.' 470 db 'Now,create another user task and switch to ' 471 db 'it by the JMP instruction...',0x0d,0x0a,0 472 473 prgman_msg3 db 0x0d,0x0a 474 db '[PROGRAM MANAGER]: I am gain control again,',0 475 476 core_msg0 db 0x0d,0x0a 477 db '[SYSTEM CORE]: Uh...This task initiated with ' 478 db 'CALL instruction or an exeception/ interrupt,' 479 db 'should use IRETD instruction to switch back...' 480 db 0x0d,0x0a,0 481 482 core_msg1 db 0x0d,0x0a 483 db '[SYSTEM CORE]: Uh...This task initiated with ' 484 db 'JMP instruction, should switch to Program ' 485 db 'Manager directly by the JMP instruction...' 486 db 0x0d,0x0a,0 487 core_stop db 'HALT...',0 488 489 tcb_chain dd 0 ;任务控制块链头指针 490 ;========================================================================= 491 ;===========================内核代码区==================================== 492 ;========================================================================= 493 SECTION Core_Code align=16 vstart=0 494 ;--------------------------------------------------------------------- 495 append_to_tcb: ;写入新的TCB链 496 ;输入:ecx新的TCB线性基地址 497 pushad 498 499 push ds 500 push es 501 502 mov eax,All_4GB_Segment 503 mov es,eax 504 505 mov eax,Core_Data_Segement 506 mov ds,eax 507 508 mov eax,[tcb_chain] 509 cmp eax,0x00 510 je _notcb 511 512 _search_tcb: 513 mov edx,[tcb_chain+0x00] 514 mov eax,[es:edx] 515 cmp eax,0x00 516 jne _search_tcb 517 518 mov [es:edx+0x00],ecx 519 jmp _out_tcb_search 520 521 _notcb: 522 mov [tcb_chain],ecx 523 524 _out_tcb_search: 525 pop es 526 pop ds 527 528 popad 529 ret 530 ;--------------------------------------------------------------------- 531 load_program: ;输入push1:逻辑扇区号 532 ; push2: 线性基地址 533 pushad 534 push ds 535 push es 536 537 mov ebp,esp ;别忘了把参数传给ebp 538 539 mov eax,Core_Data_Segement 540 mov ds,eax ;切换到内核数据段 541 542 mov eax,All_4GB_Segment 543 mov es,eax 544 545 mov edi,[ebp+11*4] ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push 546 547 mov ecx,160 548 call Sys_Routine_Segement:allocate_memory 549 mov [es:edi+0x0c],ecx 550 mov word[es:edi+0x0a],0xffff ;初始化LDT界限位0xffff 551 552 mov esi,[ebp+12*4] ;esi必须是逻辑扇区号 553 mov ebx,core_buf ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了) 554 555 push esi 556 push ds 557 push ebx 558 push cs 559 call Sys_Routine_Segement:ReadHarddisk 560 561 mov eax,[core_buf] ;读取用户程序长度 562 563 mov ebx,eax 564 and ebx,0xfffffe00 ;清空低9位(强制对齐512) 565 add ebx,512 566 test eax,0x000001ff 567 cmovnz eax,ebx ;低9位不为0则使用向上取整的结果 568 569 mov ecx,eax ;eax是整个程序的向上取整的大小 570 call Sys_Routine_Segement:allocate_memory 571 ;先分配内存给整个程序,再分配内存给栈区 572 mov ebx,ecx 573 mov [es:edi+0x06],ecx ;tcb 0x06:程序加载基地址 574 575 xor edx,edx 576 mov ecx,512 ;千万不要改掉ebx 577 div ecx 578 mov ecx,eax 579 580 mov eax,All_4GB_Segment ;切换到4GB段区域(平坦模式) 581 mov ds,eax 582 583 _loop_read: 584 push esi 585 push ds 586 push ebx 587 push cs 588 call Sys_Routine_Segement:ReadHarddisk ;esi还是User_Program_Address 589 inc esi 590 add ebx,512 591 loop _loop_read 592 593 mov esi,edi ;esi: TCB的线性基地址 594 mov edi,[es:esi+0x06] ;程序加载的线性基地址 595 596 ;建立头部描述符 597 mov eax,edi 598 mov ebx,[edi+0x04] 599 dec ebx ;段界限 600 mov ecx,0x0040f200 601 call Sys_Routine_Segement:Make_Seg_Descriptor 602 mov ebx,esi 603 call Sys_Routine_Segement:Set_New_LDT_To_TCB 604 or cx,0x0003 ;特权级3 605 mov [es:esi+0x44],cx ;记得要登记头部的选择子 606 mov [edi+0x04],cx 607 608 ;建立代码段描述符 609 mov eax,edi 610 add eax,[edi+0x14] 611 mov ebx,[edi+0x18] 612 dec ebx 613 mov ecx,0x0040f800 614 call Sys_Routine_Segement:Make_Seg_Descriptor 615 mov ebx,esi 616 call Sys_Routine_Segement:Set_New_LDT_To_TCB 617 or cx,0x0003 618 mov [edi+0x14],cx 619 620 ;建立数据段描述符 621 mov eax,edi 622 add eax,[edi+0x1c] 623 mov ebx,[edi+0x20] 624 dec ebx 625 mov ecx,0x0040f200 626 call Sys_Routine_Segement:Make_Seg_Descriptor 627 mov ebx,esi 628 call Sys_Routine_Segement:Set_New_LDT_To_TCB 629 or cx,0x0003 630 mov [edi+0x1c],cx 631 632 ;建立栈段描述符 633 mov ecx,[edi+0x0c] 634 mov ebx,0x000fffff 635 sub ebx,ecx 636 mov eax,4096 ;4KB粒度 637 mul ecx 638 mov ecx,eax 639 call Sys_Routine_Segement:allocate_memory 640 mov eax,ecx ;eax是栈段的线性基地址 641 mov ecx,0x00c0f600 642 call Sys_Routine_Segement:Make_Seg_Descriptor 643 mov ebx,esi 644 call Sys_Routine_Segement:Set_New_LDT_To_TCB 645 or cx,0x0003 646 mov [edi+0x08],cx 647 648 ;现在开始重定位API符号表 649 ;--------------------------------------------------------------------- 650 mov eax,All_4GB_Segment ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问 651 mov es,eax 652 mov eax,Core_Data_Segement 653 mov ds,eax 654 655 cld 656 mov ecx,[es:edi+0x24] ;得到用户程序符号表的条数 657 add edi,0x28 ;用户符号表的偏移地址是0x28 658 659 _loop_U_SALT: 660 push edi 661 push ecx 662 663 mov ecx,salt_items_sum 664 mov esi,salt 665 666 _loop_C_SALT: 667 push edi 668 push esi 669 push ecx 670 671 mov ecx,64 ;比较256个字节 672 repe cmpsd 673 jne _re_match ;如果成功匹配,那么esi和edi刚好会在数据区之后的 674 675 mov eax,[esi] ;偏移地址 676 mov [es:edi-256],eax ;把偏移地址填入用户程序的符号区 677 mov ax,[esi+0x04] ;段的选择子 678 679 or ax,0x0002 ;把RPL改为3,代表(内核)赋予应用程序以特权级3 680 mov [es:edi-252],ax ;把段的选择子填入用户程序的段选择区 681 682 _re_match: 683 pop ecx 684 pop esi 685 add esi,salt_length 686 pop edi 687 loop _loop_C_SALT 688 689 pop ecx 690 pop edi 691 add edi,256 692 loop _loop_U_SALT 693 ;--------------------------------------------------------------------- 694 ;----------------------填入临时中转任务门选择子----------------------- 695 mov edi,[ebp+11*4] 696 mov edi,[es:edi+0x06] ;从TCB线性基地址中获得用户程序加载的基地址 697 698 mov ax,[salt_tp] 699 mov [es:edi+0x24+776],ax ;填充任务门选择子 700 ;--------------------------------------------------------------------- 701 mov esi,[ebp+11*4] ;重新获得TCB的线性基地址 702 703 ;现在设置所有的特权级栈段,并且把特权级栈段放到TCB中(为了等一下设置TSS) 704 ;设置TSS特权0级栈段(暂存在TCB中) 705 mov ecx,Switch_Stack_Size 706 mov eax,ecx 707 mov [es:esi+0x1a],ecx 708 shr dword[es:esi+0x1a],12 ;相当于除以4096 709 call Sys_Routine_Segement:allocate_memory 710 add eax,ecx ;得到最高地址 711 mov [es:esi+0x1e],eax ;登记线性基地址 712 mov ebx,0x000fffff 713 sub ebx,[es:esi+0x1a] 714 mov ecx,0x00c09600 ;特权级0 715 call Sys_Routine_Segement:Make_Seg_Descriptor 716 mov ebx,esi 717 call Sys_Routine_Segement:Set_New_LDT_To_TCB 718 or cx,0x0000 ;RPL为0 719 mov [es:esi+0x22],cx 720 mov dword[es:esi+0x24],0 721 722 ;设置TSS特权1级栈段(暂存在TCB中) 723 mov ecx,Switch_Stack_Size 724 mov eax,ecx 725 mov [es:esi+0x28],ecx 726 shr dword[es:esi+0x28],12 ;相当于除以4096 727 call Sys_Routine_Segement:allocate_memory 728 add eax,ecx ;得到最高地址 729 mov [es:esi+0x2c],eax ;登记线性基地址 730 mov ebx,0x000fffff 731 sub ebx,[es:esi+0x28] 732 mov ecx,0x00c0b600 ;特权级1 733 call Sys_Routine_Segement:Make_Seg_Descriptor 734 mov ebx,esi 735 call Sys_Routine_Segement:Set_New_LDT_To_TCB 736 or cx,0x0001 ;RPL为1 737 mov [es:esi+0x30],cx 738 mov dword[es:esi+0x32],0 739 740 ;设置TSS特权2级栈段(暂存在TCB中) 741 mov ecx,Switch_Stack_Size 742 mov eax,ecx 743 mov [es:esi+0x36],ecx 744 shr dword[es:esi+0x36],12 ;相当于除以4096 745 call Sys_Routine_Segement:allocate_memory 746 add eax,ecx ;得到最高地址 747 mov [es:esi+0x3a],eax ;登记线性基地址 748 mov ebx,0x000fffff 749 sub ebx,[es:esi+0x36] 750 mov ecx,0x00c0d600 ;特权级2 751 call Sys_Routine_Segement:Make_Seg_Descriptor 752 mov ebx,esi 753 call Sys_Routine_Segement:Set_New_LDT_To_TCB 754 or cx,0x0002 ;RPL为2 755 mov [es:esi+0x3e],cx 756 mov dword[es:esi+0x40],0 757 758 ;在GDT中存入LDT信息 759 mov eax,[es:esi+0x0c] 760 movzx ebx,word[es:esi+0x0a] 761 mov ecx,0x00408200 ;LDT描述符,特权级0级 762 call Sys_Routine_Segement:Make_Seg_Descriptor 763 call Sys_Routine_Segement:Set_New_GDT 764 mov [es:esi+0x10],cx ;在TCB放入LDT选择子 765 766 ;在TCB中登记TSS的信息 767 mov ecx,104 ;创建一个最小尺寸的TSS 768 mov [es:esi+0x12],cx 769 dec word[es:esi+0x12] ;记得-1,要的是段界限 770 call Sys_Routine_Segement:allocate_memory 771 mov [es:esi+0x14],ecx ;TSS基地址 772 773 ;构建TSS信息表 774 mov dword[es:ecx+0x00],0 ;没有前一个任务 775 776 mov edx,[es:esi+0x24] ;0栈段 777 mov [es:ecx+4],edx 778 mov dx,[es:esi+0x22] 779 mov [es:ecx+8],dx 780 781 mov edx,[es:esi+0x32] ;1栈段 782 mov [es:ecx+12],edx 783 mov dx,[es:esi+0x30] 784 mov [es:ecx+16],dx 785 786 mov edx,[es:esi+0x40] ;2栈段 787 mov [es:ecx+20],edx 788 mov dx,[es:esi+0x3e] 789 mov [es:ecx+24],dx 790 791 mov edx,[es:esi+0x10] ;LDT选择子 792 mov [es:ecx+96],edx 793 794 mov dx,[es:esi+0x12] ;I/O偏移 795 mov [es:ecx+102],dx ;是102不是104 796 797 mov word[es:ecx+100],0 ;T=0 798 799 mov edi,[es:esi+0x06] ;用户程序的线性基地址 800 801 mov edx,[es:edi+0x10] ;EIP 802 mov [es:ecx+32],edx 803 804 mov edx,[es:edi+0x14] ;CS 805 mov [es:ecx+76],dx 806 807 mov edx,[es:edi+0x08] ;SS 808 mov [es:ecx+80],dx 809 810 mov edx,[es:edi+0x04] ;DS(是指向用户头部,而不是用户程序的数据区) 811 mov [es:ecx+84],dx 812 813 mov word[es:ecx+72],0 ;ES 814 mov word[es:ecx+88],0 ;FS 815 mov word[es:ecx+92],0 ;GS 816 817 pushfd 818 pop edx 819 mov [es:ecx+36],edx ;EFLAGS 820 821 ;在GDT中存入TSS信息 822 mov eax,[es:esi+0x14] 823 movzx ebx,word[es:esi+0x12] 824 mov ecx,0x00408900 825 call Sys_Routine_Segement:Make_Seg_Descriptor 826 call Sys_Routine_Segement:Set_New_GDT 827 mov [es:esi+0x18],cx 828 829 pop es 830 pop ds 831 popad 832 ret 8 ;相当于是stdcall,过程清栈 833 ;--------------------------------------------------------------------- 834 start: 835 mov eax,Core_Data_Segement 836 mov ds,eax 837 838 mov ebx,message_1 839 call Sys_Routine_Segement:put_string 840 841 mov eax,0 842 cpuid 843 cmp eax,0x80000004 ;判断是否有0x80000002-0x80000004功能 844 jl _@load 845 846 ;显示处理器品牌信息,从80486的后期版本开始引入 847 mov eax,0x80000002 848 cpuid 849 mov [cpu_brand+0x00],eax 850 mov [cpu_brand+0x04],ebx 851 mov [cpu_brand+0x08],ecx 852 mov [cpu_brand+0x0c],edx 853 854 mov eax,0x80000003 855 cpuid 856 mov [cpu_brand+0x10],eax 857 mov [cpu_brand+0x14],ebx 858 mov [cpu_brand+0x18],ecx 859 mov [cpu_brand+0x1c],edx 860 861 mov eax,0x80000004 862 cpuid 863 mov [cpu_brand+0x20],eax 864 mov [cpu_brand+0x24],ebx 865 mov [cpu_brand+0x28],ecx 866 mov [cpu_brand+0x2c],edx 867 868 mov ebx,cpu_brnd0 869 call Sys_Routine_Segement:put_string 870 mov ebx,cpu_brand 871 call Sys_Routine_Segement:put_string 872 mov ebx,cpu_brnd1 873 call Sys_Routine_Segement:put_string 874 875 _@load: 876 mov ebx,message_7 877 call Sys_Routine_Segement:put_string 878 ;----------------------------安装门------------------------------------ 879 mov edi,salt 880 mov ecx,salt_items_sum 881 _set_gate: 882 push ecx 883 mov eax,[edi+256] 884 mov bx,[edi+260] ;选择子 885 mov cx,0xec00 ;门是特权级是3的门,那么任何程序都能调用 886 or cx,[edi+262] ;加上参数个数 887 888 call Sys_Routine_Segement:Make_Gate_Descriptor 889 call Sys_Routine_Segement:Set_New_GDT 890 mov [edi+260],cx ;回填选择子 891 add edi,salt_length 892 pop ecx 893 loop _set_gate 894 ;---------------------------------------------------------------------- 895 mov ebx,do_status 896 call far [salt_1+256] 897 mov ebx,message_6 898 call far [salt_1+256] 899 mov ebx,message_In_Gate 900 call far [salt_1+256] ;调用门显示字符信息(忽略偏移地址(前4字节)) 901 902 mov ebx,message_4 903 call far [salt_1+256] 904 mov ebx,message_2 905 call far [salt_1+256] 906 907 mov eax,All_4GB_Segment 908 mov es,eax 909 910 mov ecx,104 911 call Sys_Routine_Segement:allocate_memory 912 mov [prgman_tss],ecx ;保留线性基地址 913 914 mov word[es:ecx+100],0 ;TI=0 915 mov word[es:ecx+102],103 ;任务管理器不需要I/O映射,要大于等于界限 916 mov word[es:ecx+96],0 ;任务允许没有自己的LDT 917 mov dword[es:ecx+28],0 ;设置CR3 918 mov word[es:ecx+0],0 ;没有前一个任务 919 920 921 mov eax,ecx 922 mov ebx,103 ;TSS段界限 923 mov ecx,0x00408900 924 call Sys_Routine_Segement:Make_Seg_Descriptor 925 call Sys_Routine_Segement:Set_New_GDT 926 mov [prgman_tss+0x04],cx 927 928 ltr cx ;启动任务 929 ;------------------安装用户管理程序的临时返回任务门-------------------- 930 mov eax,0x0000 ;TSS不需要偏移地址 931 mov bx,[prgman_tss+0x04] ;TSS的选择子 932 mov cx,0xe500 933 934 call Sys_Routine_Segement:Make_Gate_Descriptor 935 call Sys_Routine_Segement:Set_New_GDT 936 mov [salt_tp],cx ;填入临时中转任务门选择子,注意不需要加260了 937 ;---------------------------------------------------------------------- 938 mov ebx,prgman_msg1 939 call Sys_Routine_Segement:put_string 940 941 942 ;----------------------用户管理程序---------------------------- 943 mov ecx,0x46 ;TCB链大小 944 call Sys_Routine_Segement:allocate_memory 945 call append_to_tcb 946 947 push User_Program_AddressA 948 push ecx 949 call load_program 950 951 jmp far [es:ecx+0x14] ;初次打开程序A,一定要用跳转方式打开,不能call,否则不能返回了 952 ;------------------------------------------------ 953 mov ebx,prgman_msg2 954 call Sys_Routine_Segement:put_string 955 956 mov ecx,0x46 ;TCB链大小 957 call Sys_Routine_Segement:allocate_memory 958 call append_to_tcb 959 960 push User_Program_AddressB 961 push ecx 962 call load_program 963 jmp far [es:ecx+0x14] ;初次打开程序B,一定要用跳转方式打开,不能call,否则不能返回了 964 ;------------------------------------------------ 965 mov eax,Core_Data_Segement 966 mov ds,eax 967 mov eax,All_4GB_Segment 968 mov es,eax 969 970 mov ebx,prgman_msg3 971 call Sys_Routine_Segement:put_string 972 973 mov ecx,[tcb_chain] ;任务A 974 call far [es:ecx+0x14] 975 ;------------------------------------------------ 976 mov eax,Core_Data_Segement 977 mov ds,eax 978 mov eax,All_4GB_Segment 979 mov es,eax 980 981 mov ebx,prgman_msg3 982 call Sys_Routine_Segement:put_string 983 984 mov ecx,[tcb_chain] 985 mov ecx,[es:ecx+0x00] ;任务B 986 call far [es:ecx+0x14] 987 ;------------------------------------------------ 988 mov eax,Core_Data_Segement 989 mov ds,eax 990 991 mov ebx,prgman_msg3 992 call Sys_Routine_Segement:put_string 993 mov ebx,core_stop 994 call Sys_Routine_Segement:put_string 995 996 cli 997 hlt 998 ;---------------------------------------------------------------------- 999 ;========================================================================= 1000 SECTION core_trail 1001 ;---------------------------------------------------------------- 1002 Program_end:
1 ;==============================用户程序B======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序总长度#0x00 5 6 head_len dd header_end ;程序头部的长度#0x04 7 8 stack_seg dd 0 ;用于接收堆栈段选择子#0x08 9 stack_len dd 1 ;程序建议的堆栈大小#0x0c 10 ;以4KB为单位 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代码段位置#0x14 14 code_len dd code_end ;代码段长度#0x18 15 16 data_seg dd section.data.start ;数据段位置#0x1c 17 data_len dd data_end ;数据段长度#0x20 18 ;------------------------------------------------------------------------------- 19 ;符号地址检索表 20 salt_items dd (u_salt_end-salt)/256 ;#0x24 21 22 salt: ;#0x28 23 Printf: db '@Printf' 24 times 256-($-Printf) db 0 25 26 TerminateProgram:db '@TerminateProgram' 27 times 256-($-TerminateProgram) db 0 28 29 ReadHarddisk: db '@ReadHarddisk' 30 times 256-($-ReadHarddisk) db 0 31 32 u_salt_end: 33 TpBack: dd 0 ;任务门的偏移地址没用,直接填充就可以了 34 dw 0 ;任务门的选择子#0x24+776 35 header_end: 36 ;=============================================================================== 37 SECTION data align=16 vstart=0 38 39 message_1 db 0x0d,0x0a 40 db '[USER TASKB]: Hi! I am task B',0x0d,0x0a,0 41 42 message_2 db 0x0d,0x0a 43 db '[USER TASKB]: Hi! I am task B, I have been backed',0x0d,0x0a,0 44 data_end: 45 46 ;=============================================================================== 47 [bits 32] 48 ;=============================================================================== 49 SECTION code align=16 vstart=0 50 start: 51 mov eax,ds 52 mov fs,eax 53 54 mov eax,[data_seg] 55 mov ds,eax 56 57 mov ebx,message_1 58 call far [fs:Printf] 59 jmp far [fs:TpBack] ;任务切换 60 61 mov ebx,message_2 62 call far [fs:Printf] 63 iretd 64 code_end: 65 66 ;=============================================================================== 67 SECTION trail 68 ;------------------------------------------------------------------------------- 69 program_end:
1 ;==============================用户程序A======================================= 2 SECTION header vstart=0 3 4 program_length dd program_end ;程序总长度#0x00 5 6 head_len dd header_end ;程序头部的长度#0x04 7 8 stack_seg dd 0 ;用于接收堆栈段选择子#0x08 9 stack_len dd 1 ;程序建议的堆栈大小#0x0c 10 ;以4KB为单位 11 12 prgentry dd start ;程序入口#0x10 13 code_seg dd section.code.start ;代码段位置#0x14 14 code_len dd code_end ;代码段长度#0x18 15 16 data_seg dd section.data.start ;数据段位置#0x1c 17 data_len dd data_end ;数据段长度#0x20 18 ;------------------------------------------------------------------------------- 19 ;符号地址检索表 20 salt_items dd (u_salt_end-salt)/256 ;#0x24 21 22 salt: ;#0x28 23 Printf: db '@Printf' 24 times 256-($-Printf) db 0 25 26 TerminateProgram:db '@TerminateProgram' 27 times 256-($-TerminateProgram) db 0 28 29 ReadHarddisk: db '@ReadHarddisk' 30 times 256-($-ReadHarddisk) db 0 31 32 u_salt_end: 33 TpBack: dd 0 ;任务门的偏移地址没用,直接填充就可以了 34 dw 0 ;任务门的选择子#0x24+776 35 header_end: 36 ;=============================================================================== 37 SECTION data align=16 vstart=0 38 39 message_1 db 0x0d,0x0a 40 db '[USER TASKA]: Hi! I am task A',0x0d,0x0a,0 41 42 message_2 db 0x0d,0x0a 43 db '[USER TASKA]: Hi! I am task A, I have been backed',0x0d,0x0a,0 44 data_end: 45 ;=============================================================================== 46 [bits 32] 47 ;=============================================================================== 48 SECTION code align=16 vstart=0 49 start: 50 mov eax,ds 51 mov fs,eax 52 53 mov eax,[data_seg] 54 mov ds,eax 55 56 mov ebx,message_1 57 call far [fs:Printf] 58 jmp far [fs:TpBack] ;任务切换 59 60 mov ebx,message_2 61 call far [fs:Printf] 62 iretd 63 code_end: 64 65 ;=============================================================================== 66 SECTION trail 67 ;------------------------------------------------------------------------------- 68 program_end:
程序上我没有回收内存,我是觉得我自己就是做个简单的也不是很好,索性不做了。