学习笔记
《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f
子程序load_relocate_program 结构
子程序load_relocate_program 完整源码
;-------------------------------------------------------------------------------
load_relocate_program: ;加载并重定位用户程序
;输入: PUSH 逻辑扇区号
; PUSH 任务控制块基地址
;输出:无
pushad ;全部双字通用寄存器
push ds
push es
mov ebp,esp ;为访问 通过堆栈传递的参数 做准备
mov ecx,mem_0_4_gb_seg_sel
mov es,ecx
mov esi,[ebp+11*4] ;ebp默认使用段寄存器SS 取出TCB的基地址
;-------------------------------------------------------------------
;1、初始化LDT
;-------------------------------------------------------------------
mov ecx,160
call sys_routine_seg_sel:allocate_memory ;①分配内存,加载LDT
mov [es:esi+0x0c],ecx ;②登记LDT基地址到TCB
mov word [es:esi+0x0a],0xffff ;③登记LDT初始的界限值到TCB
;------------------------------------------------------------------
;2、加载用户程序
;------------------------------------------------------------------
;以下开始加载用户程序
mov eax,core_data_seg_sel
mov ds,eax ;DS切换到内核程序数据段
mov eax,[ebp+12*4] ;取出用户程序起始逻辑扇区号
mov ebx,core_buf ;缓冲区用于存放用户程序头部段
call sys_routine_seg_sel:read_hard_disk_0
;以下判断整个用户程序有多大
mov eax,[core_buf] ;程序尺寸
mov ebx,eax
and ebx,0xfffffe00 ;使之512字节对齐
add ebx,512
test eax,0x000001ff
cmovnz eax,ebx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory ;①分配内存,加载用户程序
mov [es:esi+0x06],ecx ;②登记程序加载基地址到TCB中
mov ebx,ecx
xor edx,edx
mov ecx,512
div ecx
mov ecx,eax ;总扇区数
mov eax,mem_0_4_gb_seg_sel
mov ds,eax
mov eax,[ebp+12*4] ;起始扇区号
.b1:
call sys_routine_seg_sel:read_hard_disk_0
inc eax
loop .b1 ;循环读,知道读完整个用户程序
;----------------------------------------------------------------------
;4、创建局部描述符表LDT
;----------------------------------------------------------------------
mov edi,[es:esi+0x06] ;获得程序加载基地址
;DS 已切换到0~4GB内存空间
;ES 已切换到0~4GB内存空间
;esi 指向TCB基地址
;edi 指向用户程序加载基地址
;========================================================================
;①
;========================================================================
;建立用户程序头部段描述符
mov eax,edi
mov ebx,[edi+0x04] ;段长度
dec ebx ;段界限
mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级 DPL=3
call sys_routine_seg_sel:make_seg_descriptor
;登记头部段描述符登记到LDT
mov ebx,esi ;TCB基地址
call fill_descriptor_in_ldt
;登记头部段选择子到TCB 以及 用户程序位于内存的头部段内
or cx,0000_0000_0000_0011B ;设置头部段选择子的RPL=3
mov [es:esi+0x44],cx ;登记头部段选择子到TCB
mov [edi+0x04],cx ;回写到用户程序位于内存的头部段内
;=========================================================================
;②
;=========================================================================
;建立用户程序代码段描述符
mov eax,edi
add eax,[edi+0x14] ;代码段起始线性地址
mov ebx,[edi+0x18] ;段长度
dec ebx ;段界限
mov ecx,0x0040f800 ;字节粒度的代码段描述符,特权级DPL=3
call sys_routine_seg_sel:make_seg_descriptor
;登记代码段描述符到LDT
mov ebx,esi ;TCB基地址
call fill_descriptor_in_ldt
;登记代码段选择子到用户程序头部
or cx,0000_0000_0000_0011B ;设置代码段选择子的特权级RPL=3
mov [edi+0x14],cx ;回写
;=========================================================================
;③
;=========================================================================
;建立用户程序数据段描述符
mov eax,edi
add eax,[edi+0x1c] ;数据段起始线性地址
mov ebx,[edi+0x20] ;段长度
dec ebx ;段界限
mov ecx,0x0040f200 ;字节粒度的数据段描述符,特权级DPL=3
call sys_routine_seg_sel:make_seg_descriptor
;登记数据段描述符到LDT
mov ebx,esi
call fill_descriptor_in_ldt
;登记数据段选择子到用户程序头部
or cx,0000_0000_0000_0011B ;设置数据段选择子特权级,RPL=3
mov [edi+0x1c],cx ;回写
;=========================================================================
;④
;=========================================================================
;为用户程序堆栈段分配内存
mov ecx,[edi+0x0c]
mov ebx,0x000fffff ;4KB倍率
sub ebx,ecx ;得到段界限
mov eax,4096
mul ecx
mov ecx,eax
call sys_routine_seg_sel:allocate_memory
;=========================================================================
;⑤
;=========================================================================
;建立用户程序堆栈段描述符
add eax,ecx ;得到堆栈的高端物理地址
mov ecx,0x00c0f600 ;字节粒度的堆栈段描述符,特权级DPL=3
call sys_routine_seg_sel:make_seg_descriptor
;登记堆栈段描述符到LDT
mov ebx,esi
call fill_descriptor_in_ldt
;登记堆栈段选择子到用户程序头部
or cx,0000_0000_0000_0011B ;设置堆栈段选择子特权级RPL=3
mov [edi+0x08],cx ;回写
;--------------------------------------------------------------------------
;5、重定位U-SALT表
;--------------------------------------------------------------------------
mov eax,mem_0_4_gb_seg_sel
mov es,eax
mov eax,core_data_seg_sel
mov ds,eax
cld
mov ecx,[es:edi+0x24] ;U-SALT条目数
add edi,0x28
.b2:
push ecx
push edi
mov ecx,salt_items
mov esi,salt
.b3:
push edi
push esi
push ecx
mov ecx,64
repe cmpsd
jnz .b4
mov eax,[esi]
mov [es:edi-256],eax ;回写偏移地址
mov ax,[esi+4]
or ax,0000_0000_0000_0011B ;调用门选择子 特权级RPL=3
mov [es:edi-252],ax ;回填调用门选择子
.b4:
pop ecx
pop esi
add esi,salt_item_len
pop edi
loop .b3
pop edi
add edi,256
pop ecx
loop .b2
;-----------------------------------------------------------------------------
;6、创建0、1、2特权级的栈
;-----------------------------------------------------------------------------
mov esi,[ebp+11*4] ;从堆栈中取得TCB线性地址
;==============================================================================
;创建0特权级堆栈
;==============================================================================
;①登记0特权级堆栈尺寸到TCB
mov ecx,4096
mov eax,ecx
mov [es:esi+0x1a],ecx
shr dword [es:esi+0x1a],12
;②分配内存,加载0特权级堆栈
call sys_routine_seg_sel:allocate_memory
;③计算高端地址作为0特权级堆栈段基地址
add eax,ecx
;④登记0特权级堆栈段基地址到TCB
mov [es:esi+0x1e],eax
;⑤登记0特权级堆栈段描述符到LDT
mov ebx,0xffffe ;段长度
mov ecx,0x00c09600 ;4KB粒度 读写 特权级0
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
;⑥登记0特权级堆栈段选择子到TCB
or cx,0000_0000_0000_0000B ;设置选择子的特权级 RPL=0
mov [es:esi+0x22],cx ;登记0特权级堆栈段选择子到TCB
;⑦登记0特权级堆栈初始ESP到TCB
mov dword [es:esi+0x24],0
;==================================================================
;创建1特权级堆栈
;==================================================================
;①登记1特权级堆栈尺寸到TCB
mov ecx,4096
mov eax,ecx
mov [es:esi+0x28],ecx
shr dword [es:esi+0x28],12
;②分配内存,加载1特权级堆栈
call sys_routine_seg_sel:allocate_memory
;③计算高端地址作为1特权级堆栈段基地址
add eax,ecx
;④登记1特权级堆栈段基地址到TCB
mov [es:esi+0x2c],eax
;⑤登记1特权级堆栈段描述符到LDT
mov ebx,0xffffe ;段长度
mov ecx,0x00c0b600 ;4KB粒度 读写 特权级1
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
;⑥登记1特权级堆栈段选择子到TCB
or cx,0000_0000_0000_0001B ;设置选择子的特权级 RPL=1
mov [es:esi+0x30],cx ;登记1特权级堆栈段选择子到TCB
;⑦登记1特权级堆栈初始ESP到TCB
mov dword [es:esi+0x32],0
;==================================================================
;创建2特权级堆栈
;==================================================================
;①登记2特权级堆栈尺寸到TCB
mov ecx,4096
mov eax,ecx
mov [es:esi+0x36],ecx
shr dword [es:esi+0x36],12
;②分配内存,加载1特权级堆栈
call sys_routine_seg_sel:allocate_memory
;③计算高端地址作为2特权级堆栈段基地址
add eax,ecx
;④登记2特权级堆栈段基地址到TCB
mov [es:esi+0x3a],eax
;⑤登记2特权级堆栈段描述符到LDT
mov ebx,0xffffe ;段长度
mov ecx,0x00c0d600 ;4KB粒度 读写 特权级2
call sys_routine_seg_sel:make_seg_descriptor
mov ebx,esi
call fill_descriptor_in_ldt
;⑥登记2特权级堆栈段选择子到TCB
or cx,0000_0000_0000_0010B ;设置选择子的特权级 RPL=2
mov [es:esi+0x3e],cx ;登记1特权级堆栈段选择子到TCB
;⑦登记2特权级堆栈初始ESP到TCB
mov dword [es:esi+0x40],0
;-----------------------------------------------------------------------
;7、安装 LDT描述符 到GDT
;-----------------------------------------------------------------------
;①创建LDT描述符
mov eax,[es:esi+0x0c] ;LDT起始线性基地址
movzx ebx,word [es:esi+0x0a] ;LDT当前界限值
mov ecx,0x00408200 ;LDT描述符,特权级 DPL=0
call sys_routine_seg_sel:make_seg_descriptor
;②安装 LDT描述符 到GDT
call sys_routine_seg_sel:set_up_gdt_descriptor
;③将返回的 LDT选择子 登记到TCB
mov [es:esi+0x10],cx ;登记LDT选择子到TCB中
;-----------------------------------------------------------------------
;8、创建任务状态段TSS
;-----------------------------------------------------------------------
;①登记TSS界限值到TCB
mov ecx,104 ;tss基本尺寸
mov [es:esi+0x12],cx
dec word [es:esi+0x12]
;②分配内存,加载TSS
call sys_routine_seg_sel:allocate_memory
;③登记TSS基地址到TCB
mov [es:esi+0x14],ecx
;-----------------------------------------------------------------------
;9、登记基本的TSS表格内容
;-----------------------------------------------------------------------
mov word [es:ecx+0],0 ;没有前一个任务
;①登记N特权级堆栈初始ESP、段选择子到TSS
mov edx,[es:esi+0x24]
mov [es:ecx+4],edx ;ESP0
mov edx,[es:esi+0x22]
mov [es:ecx+8],edx ;SS0
mov edx,[es:esi+0x32]
mov [es:ecx+12],edx ;ESP1
mov edx,[es:esi+0x30]
mov [es:ecx+16],edx ;SS1
mov edx,[es:esi+0x40]
mov [es:ecx+20],edx ;ESP2
mov edx,[es:esi+0x3e]
mov [es:ecx+24],edx ;SS2
;②登记当前任务的LDT选择子到TSS
mov dx,[es:esi+0x10]
mov [es:ecx+96],dx
;③登记当前任务的I/O位图偏移到TSS
mov dx,[es:esi+0x12]
mov [es:ecx+102],dx
;④设置TSS的T位值为零,表明这是唯一的任务
mov word [es:ecx+100],0
;-----------------------------------------------------------------------
;10、安装TSS描述符到GDT
;-----------------------------------------------------------------------
;①创建TSS描述符
mov eax,[es:esi+0x14] ;TSS起始线性地址
movzx ebx,word [es:esi+0x12] ;TSS段长度
mov ecx,0x00408900 ;TSS描述符 特权级DPL=0
call sys_routine_seg_sel:make_seg_descriptor
;②安装TSS描述符到GDT
call sys_routine_seg_sel:set_up_gdt_descriptor
;③将返回的TSS选择子登记到TCB
mov [es:esi+0x18],cx ;登记TSS选择子到TCB
;-----------------------------------------------------------------------
;11、恢复压栈的寄存器、弹出参数、返回调用
;-----------------------------------------------------------------------
pop es ;恢复到调用此过程前的es段
pop ds ;恢复到调用此过程前的ds段
popad
ret 8 ;丢弃调用本过程前压入的参数
;-------------------------------------------------------------------------------