找回密码
 立即注册
搜索
热搜: 活动 通知
查看: 819|回复: 7

符号名匹配的其他方法

[复制链接]

3

主题

11

回帖

69

积分

注册会员

积分
69
发表于 2024-3-6 10:58:17 | 显示全部楼层 |阅读模式
本帖最后由 Unis 于 2024-3-6 11:23 编辑

《程序的动态加载和执行》这一章(第一版第13章,第二版第15章),符号名匹配这里,我在照着示例代码写的时候,发现按照代码逻辑,当符号名匹配成功时,如果当前的 C-SALT 没有遍历完,那么第二层循环仍会继续执行,不过通常匹配成功后应该跳出第二层循环。当然要实现这个逻辑也简单,无非就是匹配成功后 pop 恢复寄存器的值然后跳转,不过这就成了 loop 里又有 jmp,总觉得别扭。

在后面的章节里,介绍了使用栈传递参数,正好最近又简单了解了一下 C 的反汇编,所以自己写的时候就换成了这种方法。不过我并没有使用栈,而是在内核数据区开辟了一小块“变量空间”。

我的目的就是去掉原来代码中的 push/pop,把需要入栈保存的内容都放在变量空间里,那么按照这个出发点,需要定义4个变量:U-SALT表项首地址(原EDI),C-SALT表项首地址(原ESI),循环变量(原ECX,需要两个)。

当然这种方法相比原来的代码,把 push/pop 都换成了内存相关指令,多出了许多字节。其实说白了就是一个两层 do-while 循环的汇编版。

  1. MAX_API_LEN    equ  256


  2. ; 内核数据段
  3. ; ====================================================
  4. ; 变量空间
  5. pVars           times 10 dd 0

  6. apis             C-SALT(具体内容略)

  7. nApiTabLen  dd ($-apis)/(MAX_API_LEN+6)
  8. ; ====================================================


  9. ; 内核代码段段
  10. ; ====================================================
  11. ; 其他代码略

  12. ; 符号名的匹配过程是一个三层循环
  13. ; 最内层循环进行实际的匹配,即按顺序比较符号名的各个字节
  14. ; 中间层循环遍历 C-SALT,最外层循环遍历 U-SALT
  15.     mov ebx, pVars
  16.     mov dword [ebx], 0x28                ; U-SALT 第一个表项首地址
  17.                                                  ; (起始位置偏移地址)
  18.     mov dword [ebx+0x04], 1            ; 遍历 U-SALT 的循环变量
  19.   .traverse_invoked_api:

  20.     mov dword [ebx+0x08], apis    ; C-SALT 第一个表项首地址
  21.     mov dword [ebx+0x0c], 1        ; 遍历 C-SALT 的循环变量
  22.   .traverse_api_list:
  23. .compare_api_name:
复制代码

一开始我还使用 EBP 进行变量空间寻址,结果当然是出现了保护异常,因为 EBP 作为基址寄存器时,默认操作的是栈空间。

点评

厉害!  发表于 2024-3-6 11:01

3

主题

11

回帖

69

积分

注册会员

积分
69
 楼主| 发表于 2024-3-6 11:04:26 | 显示全部楼层
本帖最后由 Unis 于 2024-3-6 11:11 编辑
  1. ; 符号名的匹配过程是一个三层循环
  2. ; 最内层循环进行实际的匹配,即按顺序比较符号名的各个字节
  3. ; 中间层循环遍历 C-SALT,最外层循环遍历 U-SALT
  4. mov ebx, pVars
  5. mov dword [ebx], 0x28                ; U-SALT 第一个表项首地址
  6.                                                  ; (起始位置偏移地址)
  7. mov dword [ebx+0x04], 1            ; 遍历 U-SALT 的循环变量
  8. .traverse_invoked_api:

复制代码

3

主题

11

回帖

69

积分

注册会员

积分
69
 楼主| 发表于 2024-3-6 11:18:01 | 显示全部楼层
不知道为啥代码总显示不全,发个图片。。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

350

主题

281

回帖

2834

积分

管理员

积分
2834
发表于 2024-3-6 11:32:43 | 显示全部楼层
听说代码总发不全,我来试试。


  1. ;global_defs.wid:系统全局使用的常量定义,2021-09-05

  2. ;定义地址的,至少按16字节对齐!!!与分页有关的地址必须按4KB对齐!!!

  3. %ifndef _GLOBAL_DEFS_
  4.    %define _GLOBAL_DEFS_

  5.    SDA_PHY_ADDR        equ     0x00007e00              ;系统数据区的起始物理地址
  6.    PML5_PHY_ADDR       equ     0x00009000              ;内核5级头表物理地址
  7.    PML4_PHY_ADDR       equ     0x0000a000              ;内核4级头表物理地址
  8.    PDPT_PHY_ADDR       equ     0x0000b000              ;对应于低端2MB的内核页目录指针表物理地址
  9.    PDT_PHY_ADDR        equ     0x0000c000              ;对应于低端2MB的页目录表物理地址
  10.    PT_PHY_ADDR         equ     0x0000d000              ;对应于低端2MB的内核页表的物理地址
  11.    IDT_PHY_ADDR        equ     0x0000e000              ;中断描述符表的物理地址
  12.    LDR_PHY_ADDR        equ     0x0000f000              ;用于安装内核加载器的起始物理地址
  13.    GDT_PHY_ADDR        equ     0x00010000              ;全局描述符表GDT的物理地址
  14.    CORE_PHY_ADDR       equ     0x00020000              ;内核的起始物理地址
  15.    COR_PDPT_ADDR       equ     0x00100000              ;从这个物理地址开始的1MB是内核的254个页目录指针表

  16.    LDR_START_SECTOR    equ     1                       ;内核加载器在硬盘上的起始逻辑扇区号
  17.    COR_START_SECTOR    equ     9                       ;内核程序在硬盘上的起始逻辑扇区号

  18.    ;虚拟内存空间的高端起始于线性地址0xffff800000000000
  19.    UPPER_LINEAR_START  equ     0xffff800000000000

  20.    UPPER_CORE_LINEAR   equ     UPPER_LINEAR_START + CORE_PHY_ADDR     ;内核的高端线性地址
  21.    UPPER_TEXT_VIDEO    equ     UPPER_LINEAR_START + 0x000b8000        ;文本显示缓冲区的高端起始线性地址
  22.    UPPER_SDA_LINEAR    equ     UPPER_LINEAR_START + SDA_PHY_ADDR      ;系统数据区的高端线性地址
  23.    UPPER_GDT_LINEAR    equ     UPPER_LINEAR_START + GDT_PHY_ADDR      ;GDT的高端线性地址
  24.    UPPER_IDT_LINEAR    equ     UPPER_LINEAR_START + IDT_PHY_ADDR      ;IDT的高端线性地址

  25.    ;与全局描述符表有关的选择子定义,及内存管理有关的常量定义
  26.    CORE_CODE64_SEL     equ     0x0018                  ;内核代码段的描述符选择子(RPL=00)
  27.    CORE_STACK64_SEL    equ     0x0020                  ;内核栈段的描述符选择子(RPL=00)
  28.    RESVD_DESC_SEL      equ     0x002b                  ;保留的描述符选择子
  29.    USER_CODE64_SEL     equ     0x003b                  ;3特权级代码段的描述符选择子(RPL=11)
  30.    USER_STACK64_SEL    equ     0x0033                  ;3特权级栈段的描述符选择子(RPL=11)

  31.    PHY_MEMORY_SIZE     equ     32                      ;物理内存大小(MB),要求至少3MB
  32.    CORE_ALLOC_START    equ     0xffff800000200000      ;在虚拟地址空间高端(内核)分配内存时的起始地址
  33.    USER_ALLOC_START    equ     0x0000000000000000      ;在每个任务虚拟地址空间低端分配内存时的起始地址

  34.    ;创建任务时,需要分配一个物理页作为新任务的4级头表,并分配一个临时的线性地址来初始化这个页
  35.    NEW_PML4_LINEAR     equ     0xffffff7ffffff000      ;用来映射新任务4级头表的线性地址

  36.    LAPIC_START_ADDR    equ     0xffffff7fffffe000      ;LOCAL APIC寄存器的起始线性地址
  37.    IOAPIC_START_ADDR   equ     0xffffff7fffffd000      ;I/O APIC寄存器的起始线性地址

  38.    AP_START_UP_ADDR    equ     0x0000f000              ;应用处理器(AP)启动代码的物理地址

  39.    SUGG_PREEM_SLICE    equ     55                      ;推荐的任务/线程抢占时间片长度(毫秒)

  40.    ;多处理器环境下的自旋锁加锁宏。需要两个参数:寄存器,以及一个对应宽度的锁变量
  41.    %macro   SET_SPIN_LOCK 2                            ;两个参数,分别是寄存器%1和锁变量%2
  42.             %%spin_lock:
  43.                        cmp %2, 0                       ;锁是释放状态吗?
  44.                        je %%get_lock                   ;获取锁
  45.                        pause
  46.                        jmp %%spin_lock                 ;继续尝试获取锁
  47.             %%get_lock:
  48.                        mov %1, 1
  49.                        xchg %1, %2
  50.                        cmp %1, 0                       ;交换前为零?
  51.                        jne %%spin_lock                 ;已有程序抢先加锁,失败重来
  52.    %endmacro

  53. %endif
复制代码


350

主题

281

回帖

2834

积分

管理员

积分
2834
发表于 2024-3-6 11:34:22 | 显示全部楼层
再试试长的。

  1. ;c05_core.asm:支持多处理器和高级可编程中断控制器的内核
  2. ;李忠,2020-7-6

  3. %include "..\common\global_defs.wid"

  4. ;===============================================================================
  5. section core_header                               ;内核程序头部
  6.   length       dd core_end                        ;#0:内核程序的总长度(字节数)
  7.   init_entry   dd init                            ;#4:内核入口点
  8.   position     dq 0                               ;#8:内核加载的虚拟(线性)地址

  9. ;===============================================================================
  10. section core_data                                 ;内核数据段
  11.   acpi_error    db "ACPI is not supported or data error.", 0x0d, 0x0a, 0

  12.   num_cpus     db 0                               ;逻辑处理器数量
  13.   cpu_list     times 256 db 0                     ;Local APIC ID的列表
  14.   lapic_addr   dd 0                               ;Local APIC的物理地址

  15.   ioapic_addr  dd 0                               ;I/O APIC的物理地址
  16.   ioapic_id    db 0                               ;I/O APIC ID

  17.   clocks_1ms   dd 0                               ;处理器在1ms内经历的时钟数

  18.   welcome      db "Executing in 64-bit mode.", 0x0d, 0x0a, 0
  19.   tss_ptr      dq 0                               ;任务状态段TSS从此处开始

  20.   sys_entry    dq get_screen_row
  21.                dq get_cmos_time
  22.                dq put_cstringxy64
  23.                dq create_process
  24.                dq get_current_pid
  25.                dq terminate_process

  26.   pcb_ptr      dq 0                               ;进程控制块PCB首节点的线性地址
  27.   cur_pcb      dq 0                               ;当前任务的PCB线性地址

  28. ;===============================================================================
  29. section core_code                                 ;内核代码段

  30. %include "..\common\core_utils64.wid"             ;引入内核用到的例程

  31.          bits 64

  32. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  33. general_interrupt_handler:                        ;通用中断处理过程
  34.          iretq

  35. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  36. general_exception_handler:                        ;通用异常处理过程
  37.                                                   ;在24行0列显示红底白字的错误信息
  38.          mov r15, [rel position]
  39.          lea rbx, [r15 + exceptm]
  40.          mov dh, 24
  41.          mov dl, 0
  42.          mov r9b, 0x4f
  43.          call put_cstringxy64                     ;位于core_utils64.wid

  44.          cli
  45.          hlt                                      ;停机且不接受外部硬件中断

  46.   exceptm      db "A exception raised,halt.", 0   ;发生异常时的错误信息

  47. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  48. general_8259ints_handler:                         ;通用的8259中断处理过程
  49.          push rax

  50.          mov al, 0x20                             ;中断结束命令EOI
  51.          out 0xa0, al                             ;向从片发送
  52.          out 0x20, al                             ;向主片发送

  53.          pop rax

  54.          iretq

  55. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  56. rtm_interrupt_handle:                             ;实时时钟中断处理过程(任务切换)
  57.          push r8
  58.          push rax
  59.          push rbx

  60.          ;mov al, 0x20                             ;中断结束命令EOI
  61.          ;out 0xa0, al                             ;向8259A从片发送
  62.          ;out 0x20, al                             ;向8259A主片发送

  63.          mov al, 0x0c                             ;寄存器C的索引。且开放NMI
  64.          out 0x70, al
  65.          in al, 0x71                              ;读一下RTC的寄存器C,否则只发生一次中断
  66.                                                   ;此处不考虑闹钟和周期性中断的情况

  67.          ;除非是NMI、SMI、INIT、ExtINT、SIPI或者INIT-Deassert引发的中断,中断处理过程必须
  68.          ;包含一条写EOI寄存器的指令
  69.          mov r8, LAPIC_START_ADDR                ;给Local APIC发送中断结束命令EOI
  70.          mov dword [r8 + 0xb0], 0

  71.          ;以下开始执行任务切换
  72.          ;任务切换的原理是,它发生在所有任务的全局空间。在任务A的全局空间执行任务
  73.          ;切换,切换到任务B,实际上也是从任务B的全局空间返回任务B的私有空间。

  74.          ;从PCB链表中寻找就绪的任务。
  75.          mov r8, [rel cur_pcb]                    ;定位到当前任务的PCB节点
  76.          cmp r8, 0                                ;系统中尚未确立当前任务?
  77.          jz .return                               ;是。未找到就绪任务(节点),返回
  78.   .again:
  79.          mov r8, [r8 + 280]                       ;取得下一个节点
  80.          cmp r8, [rel cur_pcb]                    ;是否转一圈回到当前节点?
  81.          jz .return                               ;是。未找到就绪任务(节点),返回
  82.          cmp qword [r8 + 16], 0                   ;是就绪任务(节点)?
  83.          jz .found                                ;是。转任务切换
  84.          jmp .again

  85.   .found:
  86.          mov rax, [rel cur_pcb]                   ;取得当前任务的PCB(线性地址)
  87.          cmp qword [rax + 16], 2                  ;当前任务有可能已经被标记为终止。
  88.          jz .restore

  89.          ;保存当前任务的状态以便将来恢复执行
  90.          mov rbx, cr3
  91.          mov [rax + 56], rbx

  92.          mov qword [rax + 16], 0                  ;置任务状态为就绪
  93.          ;mov [rax + 64], rax                     ;不需设置,将来恢复执行时从栈中弹出
  94.          ;mov [rax + 72], rbx                     ;不需设置,将来恢复执行时从栈中弹出
  95.          mov [rax + 80], rcx
  96.          mov [rax + 88], rdx
  97.          mov [rax + 96], rsi
  98.          mov [rax + 104], rdi
  99.          mov [rax + 112], rbp
  100.          mov [rax + 120], rsp
  101.          ;mov [rax + 128], r8                     ;不需设置,将来恢复执行时从栈中弹出
  102.          mov [rax + 136], r9
  103.          mov [rax + 144], r10
  104.          mov [rax + 152], r11
  105.          mov [rax + 160], r12
  106.          mov [rax + 168], r13
  107.          mov [rax + 176], r14
  108.          mov [rax + 184], r15
  109.          mov rbx, [rel position]
  110.          lea rbx, [rbx + .return]
  111.          mov [rax + 192], rbx                     ;RIP为中断返回点
  112.          mov [rax + 200], cs
  113.          mov [rax + 208], ss
  114.          pushfq
  115.          pop qword [rax + 232]

  116.   .restore:
  117.          ;恢复新任务的状态
  118.          mov [rel cur_pcb], r8                    ;将新任务设置为当前任务
  119.          mov qword [r8 + 16], 1                   ;置任务状态为忙

  120.          mov rax, [r8 + 32]                       ;取PCB中的RSP0
  121.          mov rbx, [rel tss_ptr]
  122.          mov [rbx + 4], rax                       ;置TSS的RSP0

  123.          mov rax, [r8 + 56]
  124.          mov cr3, rax                             ;切换地址空间

  125.          mov rax, [r8 + 64]
  126.          mov rbx, [r8 + 72]
  127.          mov rcx, [r8 + 80]
  128.          mov rdx, [r8 + 88]
  129.          mov rsi, [r8 + 96]
  130.          mov rdi, [r8 + 104]
  131.          mov rbp, [r8 + 112]
  132.          mov rsp, [r8 + 120]
  133.          mov r9, [r8 + 136]
  134.          mov r10, [r8 + 144]
  135.          mov r11, [r8 + 152]
  136.          mov r12, [r8 + 160]
  137.          mov r13, [r8 + 168]
  138.          mov r14, [r8 + 176]
  139.          mov r15, [r8 + 184]
  140.          push qword [r8 + 208]                    ;SS
  141.          push qword [r8 + 120]                    ;RSP
  142.          push qword [r8 + 232]                    ;RFLAGS
  143.          push qword [r8 + 200]                    ;CS
  144.          push qword [r8 + 192]                    ;RIP

  145.          mov r8, [r8 + 128]                       ;恢复R8的值

  146.          iretq                                    ;转入新任务局部空间执行

  147.   .return:
  148.          pop rbx
  149.          pop rax
  150.          pop r8

  151.          iretq

  152. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  153. append_to_pcb_link:                               ;在PCB链上追加任务控制块
  154.                                                   ;输入:R11=PCB线性基地址
  155.          push rax
  156.          push rbx

  157.          cli

  158.          mov rbx, [rel pcb_ptr]                   ;取得链表首节点的线性地址
  159.          or rbx, rbx
  160.          jnz .not_empty                           ;链表非空,转.not_empty
  161.          mov [r11], r11                           ;唯一的节点:前驱是自己
  162.          mov [r11 + 280], r11                     ;后继也是自己
  163.          mov [rel pcb_ptr], r11                   ;这是头节点
  164.          jmp .return

  165.   .not_empty:
  166.          mov rax, [rbx]                           ;取得头节点的前驱节点的线性地址
  167.          ;此处,RBX=头节点;RAX=头节点的前驱节点;R11=追加的节点
  168.          mov [rax + 280], r11                     ;前驱节点的后继是追加的节点
  169.          mov [r11 + 280], rbx                     ;追加的节点的后继是头节点
  170.          mov [r11], rax                           ;追加的节点的前驱是头节点的前驱
  171.          mov [rbx], r11                           ;头节点的前驱是追加的节点

  172.   .return:
  173.          sti

  174.          pop rbx
  175.          pop rax

  176.          ret

  177. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  178. get_current_pid:                                  ;返回当前任务(进程)的标识
  179.          mov rax, [rel cur_pcb]
  180.          mov rax, [rax + 8]

  181.          ret

  182. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  183. terminate_process:                                ;终止当前任务
  184.          cli                                      ;执行流改变期间禁止时钟中断引发的任务切换

  185.          mov rax, [rel cur_pcb]                   ;定位到当前任务的PCB节点
  186.          mov qword [rax + 16], 2                  ;状态=终止

  187.          jmp rtm_interrupt_handle                 ;强制任务调度,交还处理器控制权

  188. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  189. create_process:                                   ;创建新的任务
  190.                                                   ;输入:R8=程序的起始逻辑扇区号
  191.          push rax
  192.          push rbx
  193.          push rcx
  194.          push rdx
  195.          push rsi
  196.          push rdi
  197.          push rbp
  198.          push r8
  199.          push r9
  200.          push r10
  201.          push r11
  202.          push r12
  203.          push r13
  204.          push r14
  205.          push r15

  206.          ;首先在地址空间的高端(内核)创建任务控制块PCB
  207.          mov rcx, 512                             ;任务控制块PCB的尺寸
  208.          call core_memory_allocate                ;在虚拟地址空间的高端(内核)分配内存

  209.          mov r11, r13                             ;以下,R11专用于保存PCB线性地址

  210.          mov qword [r11 + 24], USER_ALLOC_START   ;填写PCB的下一次可分配线性地址域

  211.          ;从当前活动的4级头表复制并创建新任务的4级头表。
  212.          call copy_current_pml4
  213.          mov [r11 + 56], rax                      ;填写PCB的CR3域,默认PCD=PWT=0

  214.          ;以下,切换到新任务的地址空间,并清空其4级头表的前半部分。不过没有关系,
  215.          ;我们正在地址空间的高端执行,可正常执行内核代码并访问内核数据,毕竟所有
  216.          ;任务的高端(全局)部分都相同。同时,当前使用的栈位于地址空间高端的栈。
  217.          mov r15, cr3                             ;保存控制寄存器CR3的值
  218.          mov cr3, rax                             ;切换到新4级头表映射的新地址空间

  219.          ;清空当前4级头表的前半部分(对应于任务的局部地址空间)
  220.          mov rax, 0xffff_ffff_ffff_f000           ;当前活动4级头表自身的线性地址
  221.          mov rcx, 256
  222.   .clsp:
  223.          mov qword [rax], 0
  224.          add rax, 8
  225.          loop .clsp

  226.          mov rax, cr3                             ;刷新TLB
  227.          mov cr3, rax

  228.          mov rcx, 4096 * 16                       ;为TSS的RSP0开辟栈空间
  229.          call core_memory_allocate                ;必须是在内核的空间中开辟
  230.          mov [r11 + 32], r14                      ;填写PCB中的RSP0域的值

  231.          mov rcx, 4096 * 16                       ;为用户程序开辟栈空间
  232.          call user_memory_allocate
  233.          mov [r11 + 120], r14                     ;用户程序执行时的RSP。

  234.          mov qword [r11 + 16], 0                  ;任务状态=就绪

  235.          ;以下开始加载用户程序
  236.          mov rcx, 512                             ;在私有空间开辟一个缓冲区
  237.          call user_memory_allocate
  238.          mov rbx, r13
  239.          mov rax, r8                              ;用户程序起始扇区号
  240.          call read_hard_disk_0

  241.          mov [r13 + 16], r13                      ;在程序中填写它自己的起始线性地址
  242.          mov r14, r13
  243.          add r14, [r13 + 8]
  244.          mov [r11 + 192], r14                     ;在PCB中登记程序的入口点线性地址

  245.          ;以下判断整个程序有多大
  246.          mov rcx, [r13]                           ;程序尺寸
  247.          test rcx, 0x1ff                          ;能够被512整除吗?
  248.          jz .y512
  249.          shr rcx, 9                               ;不能?凑整。
  250.          shl rcx, 9
  251.          add rcx, 512
  252.   .y512:
  253.          sub rcx, 512                             ;减去已经读的一个扇区长度
  254.          jz .rdok
  255.          call user_memory_allocate
  256.          ;mov rbx, r13
  257.          shr rcx, 9                               ;除以512,还需要读的扇区数
  258.          inc rax                                  ;起始扇区号
  259.   .b1:
  260.          call read_hard_disk_0
  261.          inc rax
  262.          loop .b1                                 ;循环读,直到读完整个用户程序

  263.   .rdok:
  264.          mov qword [r11 + 200], USER_CODE64_SEL   ;新任务的代码段选择子
  265.          mov qword [r11 + 208], USER_STACK64_SEL  ;新任务的栈段选择子

  266.          pushfq
  267.          pop qword [r11 + 232]

  268.          call generate_process_id
  269.          mov [r11 + 8], rax                       ;记录当前任务的标识

  270.          call append_to_pcb_link                  ;将PCB添加到进程控制块链表尾部

  271.          mov cr3, r15                             ;切换到原任务的地址空间

  272.          pop r15
  273.          pop r14
  274.          pop r13
  275.          pop r12
  276.          pop r11
  277.          pop r10
  278.          pop r9
  279.          pop r8
  280.          pop rbp
  281.          pop rdi
  282.          pop rsi
  283.          pop rdx
  284.          pop rcx
  285.          pop rbx
  286.          pop rax

  287.          ret
  288. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  289. syscall_procedure:                                ;系统调用的处理过程
  290.          ;RCX和R11由处理器使用,保存RIP和RFLAGS的内容;RBP和R15由此例程占用。如
  291.          ;有必要,请用户程序在调用syscall前保存它们,在系统调用返回后自行恢复。
  292.          mov rbp, rsp
  293.          mov r15, [rel tss_ptr]
  294.          mov rsp, [r15 + 4]                       ;使用TSS的RSP0作为安全栈

  295.          sti

  296.          mov r15, [rel position]
  297.          add r15, [r15 + rax * 8 + sys_entry]
  298.          call r15

  299.          cli
  300.          mov rsp, rbp                             ;还原到用户程序的栈
  301.          o64 sysret
  302. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  303. init:    ;初始化内核的工作环境

  304.          ;将GDT的线性地址映射到虚拟内存高端的相同位置。
  305.          ;处理器不支持64位立即数到内存地址的操作,所以用两条指令完成。
  306.          mov rax, UPPER_GDT_LINEAR                ;GDT的高端线性地址
  307.          mov qword [SDA_PHY_ADDR + 4], rax        ;注意:必须是扩高地址

  308.          lgdt [SDA_PHY_ADDR + 2]                  ;只有在64位模式下才能加载64位线性地址部分

  309.          ;将栈映射到高端,否则,压栈时依然压在低端,并和低端的内容冲突。
  310.          ;64位模式下不支持源操作数为64位立即数的加法操作。
  311.          mov rax, 0xffff800000000000              ;或者加上UPPER_LINEAR_START
  312.          add rsp,rax                              ;栈指针必须转换为高端地址且必须是扩高地址

  313.          ;准备让处理器从虚拟地址空间的高端开始执行(现在依然在低端执行)
  314.          mov rax, 0xffff800000000000              ;或者使用常量UPPER_LINEAR_START
  315.          add [rel position], rax                  ;内核程序的起始位置数据也必须转换成扩高地址

  316.          ;内核的起始地址 + 标号.to_upper的汇编地址 = 标号.to_upper所在位置的运行时扩高地址
  317.          mov rax, [rel position]
  318.          add rax, .to_upper
  319.          jmp rax                                  ;绝对间接近转移,从此在高端执行后面的指令

  320.   .to_upper:
  321.          ;初始化中断描述符表IDT,并为32个异常以及224个中断安装门描述符

  322.          ;为32个异常创建通用处理过程的中断门
  323.          mov r9, [rel position]
  324.          lea rax, [r9 + general_exception_handler];得到通用异常处理过程的线性地址
  325.          call make_interrupt_gate                 ;位于core_utils64.wid

  326.          xor r8, r8
  327.   .idt0:
  328.          call mount_idt_entry                     ;位于core_utils64.wid
  329.          inc r8
  330.          cmp r8, 31
  331.          jle .idt0

  332.          ;创建并安装对应于其它中断的通用处理过程的中断门
  333.          lea rax, [r9 + general_interrupt_handler];得到通用中断处理过程的线性地址
  334.          call make_interrupt_gate                 ;位于core_utils64.wid

  335.          mov r8, 32
  336.   .idt1:
  337.          call mount_idt_entry                     ;位于core_utils64.wid
  338.          inc r8
  339.          cmp r8, 255
  340.          jle .idt1

  341.          mov rax, UPPER_IDT_LINEAR                ;中断描述符表IDT的高端线性地址
  342.          mov rbx, UPPER_SDA_LINEAR                ;系统数据区SDA的高端线性地址
  343.          mov qword [rbx + 0x0e], rax
  344.          mov word [rbx + 0x0c], 256 * 16 - 1

  345.          lidt [rbx + 0x0c]                        ;只有在64位模式下才能加载64位线性地址部分

  346.          ;初始化8259中断控制器,包括重新设置中断向量号
  347.          call init_8259

  348.          ;创建并安装16个8259中断处理过程的中断门,向量0x20--0x2f
  349.          lea rax, [r9 + general_8259ints_handler] ;得到通用8259中断处理过程的线性地址
  350.          call make_interrupt_gate                 ;位于core_utils64.wid

  351.          mov r8, 0x20
  352.   .8259:
  353.          call mount_idt_entry                     ;位于core_utils64.wid
  354.          inc r8
  355.          cmp r8, 0x2f
  356.          jle .8259

  357.          sti                                      ;开放硬件中断

  358.          ;在64位模式下显示的第一条信息!
  359.          mov r15, [rel position]
  360.          lea rbx, [r15 + welcome]
  361.          call put_string64                        ;位于core_utils64.wid
  362.          ;----------------------------------------------------------------------
  363.          ;安装系统服务所需要的代码段和栈段描述符
  364.          sub rsp, 16                              ;开辟16字节的空间操作GDT和GDTR
  365.          sgdt [rsp]
  366.          xor rbx, rbx
  367.          mov bx, [rsp]                            ;得到GDT的界限值
  368.          inc bx                                   ;得到GDT的长度(字节数)
  369.          add rbx, [rsp + 2]
  370.          ;以下,处理器不支持从64位立即数到内存之间的传送!!!
  371.          mov dword [rbx], 0x0000ffff
  372.          mov dword [rbx + 4], 0x00cf9200          ;数据段描述符,DPL=00
  373.          mov dword [rbx + 8], 0                   ;保留的描述符槽位
  374.          mov dword [rbx + 12], 0
  375.          mov dword [rbx + 16], 0x0000ffff         ;数据段描述符,DPL=11
  376.          mov dword [rbx + 20], 0x00cff200
  377.          mov dword [rbx + 24], 0x0000ffff         ;代码段描述符,DPL=11
  378.          mov dword [rbx + 28], 0x00aff800

  379.          ;安装任务状态段TSS的描述符
  380.          mov rcx, 104                             ;TSS的标准长度
  381.          call core_memory_allocate
  382.          mov [rel tss_ptr], r13
  383.          mov rax, r13
  384.          call make_tss_descriptor
  385.          mov qword [rbx + 32], rsi                ;TSS描述符的低64位
  386.          mov qword [rbx + 40], rdi                ;TSS描述符的高64位

  387.          add word [rsp], 48                       ;4个段描述符和1个TSS描述符的总字节数
  388.          lgdt [rsp]
  389.          add rsp, 16                              ;恢复栈平衡

  390.          mov cx, 0x0040                           ;TSS描述符的选择子
  391.          ltr cx

  392.          ;为快速系统调用SYSCALL和SYSRET准备参数
  393.          mov ecx, 0x0c0000080                     ;指定型号专属寄存器IA32_EFER
  394.          rdmsr
  395.          bts eax, 0                               ;设置SCE位,允许SYSCALL指令
  396.          wrmsr

  397.          mov ecx, 0xc0000081                      ;STAR
  398.          mov edx, (RESVD_DESC_SEL << 16) | CORE_CODE64_SEL
  399.          xor eax, eax
  400.          wrmsr

  401.          mov ecx, 0xc0000082                      ;LSTAR
  402.          mov rax, [rel position]
  403.          lea rax, [rax + syscall_procedure]       ;只用EAX部分
  404.          mov rdx, rax
  405.          shr rdx, 32                              ;使用EDX部分
  406.          wrmsr

  407.          mov ecx, 0xc0000084                      ;FMASK
  408.          xor edx, edx
  409.          mov eax, 0x00047700                      ;要求TF=IF=DF=AC=0;IOPL=00
  410.          wrmsr

  411.          ;以下初始化高级可编程中断控制器APIC。在计算机启动后,BIOS已经对LAPIC和IOAPIC做了
  412.          ;初始化并创建了相关的高级配置和电源管理接口(ACPI)表项。可以从中获取多处理器和
  413.          ;APIC信息。英特尔架构的个人计算机(IA-PC)从1MB物理内存中搜索获取;启用可扩展固件
  414.          ;接口(EFI或者叫UEFI)的计算机需使用EFI传递的EFI系统表指针定位相关表格并从中获取
  415.          ;多处理器和APIC信息。为简单起见,我们采用前一种传统的方式。请注意虚拟机的配置!

  416.          ;ACPI申领的内存区域已经保存在我们的系统数据区(SDA),以下将其读出。此内存区可能
  417.          ;位于分页系统尚未映射的部分,故以下先将这部分内存进行一一映射(线性地址=物理地址)
  418.          cmp word [SDA_PHY_ADDR + 0x16], 0
  419.          jz .acpi_err                             ;不正确的ACPI数据,可能不支持ACPI
  420.          mov rsi, SDA_PHY_ADDR + 0x18             ;系统数据区:地址范围描述结构的起始地址
  421.   .looking:
  422.          cmp dword [rsi + 16], 3                  ;3:ACPI申领的内存(AddressRangeACPI)
  423.          jz .looked
  424.          add rsi, 32                              ;32:每个地址范围描述结构的长度
  425.          loop .looking

  426.   .acpi_err:
  427.          mov r15, [rel position]
  428.          lea rbx, [r15 + acpi_error]
  429.          call put_string64                        ;位于core_utils64_mp.wid
  430.          cli
  431.          hlt

  432.   .looked:
  433.          mov rbx, [rsi]                           ;ACPI申领的起始物理地址
  434.          mov rcx, [rsi + 8]                       ;ACPI申领的内存数量,以字节计
  435.          add rcx, rbx                             ;ACPI申领的内存上边界
  436.          mov rdx, 0xffff_ffff_ffff_f000           ;用于生成页地址的掩码
  437.   .maping:
  438.          mov r13, rbx                             ;R13:本次映射的线性地址
  439.          mov rax, rbx
  440.          and rax, rdx
  441.          or rax, 0x07                             ;RAX:本次映射的物理地址及属性
  442.          call mapping_laddr_to_page
  443.          add rbx, 0x1000
  444.          cmp rbx, rcx
  445.          jle .maping

  446.          ;从物理地址0x60000开始,搜索根系统描述指针结构(RSDP)
  447.          mov rbx, 0x60000
  448.          mov rcx, 'RSD PTR '                      ;结构的起始标记(注意尾部的空格)
  449.   .searc:
  450.          cmp qword [rbx], rcx
  451.          je .finda
  452.          add rbx, 16                              ;结构的标记总是位于16字节边界处
  453.          cmp rbx, 0xffff0                         ;低端1MB物理内存的上边界
  454.          jl .searc
  455.          jmp .acpi_err                            ;未找到RSDP,报错停机处理。

  456.   .finda:
  457.          ;RSDT和XSDT都指向MADT,但RSDT给出的是32位物理地址,而XDST给出64位物理地址。
  458.          ;只有VCPI 2.0及更高版本才有XSDT。典型地,VBox支持ACPI 2.0而Bochs仅支持1.0
  459.          cmp byte [rbx + 15], 2                   ;检测ACPI的版本是否为2
  460.          jne .acpi_1
  461.          mov rbx, [rbx + 24]                      ;得到扩展的系统描述表(XSDT)的物理地址

  462.          ;以下,开始在XSDT中遍历搜索多APIC描述表(MADT)
  463.          xor rdi, rdi
  464.          mov edi, [rbx + 4]                       ;获得XSDT的长度(以字节计)
  465.          add rdi, rbx                             ;计算XSDT上边界的物理位置
  466.          add rbx, 36                              ;XSDT尾部数组的物理位置
  467.   .madt0:
  468.          mov r11, [rbx]
  469.          cmp dword [r11], 'APIC'                  ;MADT表的标记
  470.          je .findm
  471.          add rbx, 8                               ;下一个元素
  472.          cmp rbx, rdi
  473.          jl .madt0
  474.          jmp .acpi_err                            ;未找到MADT,报错停机处理。

  475.          ;以下按VCPI 1.0处理,开始在RSDT中遍历搜索多APIC描述表(MADT)
  476.   .acpi_1:
  477.          mov ebx, [rbx + 16]                      ;得到根系统描述表(RSDT)的物理地址
  478.          ;以下,开始在RSDT中遍历搜索多APIC描述表(MADT)
  479.          mov edi, [ebx + 4]                       ;获得RSDT的长度(以字节计)
  480.          add edi, ebx                             ;计算RSDT末端的物理位置
  481.          add ebx, 36                              ;RSDT尾部数组的物理位置
  482.          xor r11, r11
  483.   .madt1:
  484.          mov r11d, [ebx]
  485.          cmp dword [r11], 'APIC'                  ;MADT表的标记
  486.          je .findm
  487.          add ebx, 4                               ;下一个元素
  488.          cmp ebx, edi
  489.          jl .madt1
  490.          jmp .acpi_err                            ;未找到MADT,报错停机处理。

  491.   .findm:
  492.          ;此时,R11是MADT的物理地址
  493.          mov edx, [r11 + 36]                      ;预置的LAPIC物理地址
  494.          mov [rel lapic_addr], edx

  495.          ;以下开始遍历系统中的逻辑处理器(其LAPID ID)和I/O APIC。
  496.          mov r15, [rel position]                  ;为访问cpu_list准备线性地址
  497.          lea r15, [r15 + cpu_list]

  498.          xor rdi, rdi
  499.          mov edi, [r11 + 4]                       ;EDI:MADT的长度,以字节计
  500.          add rdi, r11                             ;RDI:MADT尾部边界的物理地址
  501.          add r11, 44                              ;R11:指向MADT尾部的中断控制器结构列表
  502.   .enumd:
  503.          cmp byte [r11], 0                        ;列表项类型:Processor Local APIC
  504.          je .l_apic
  505.          cmp byte [r11], 1                        ;列表项类型:I/O APIC
  506.          je .ioapic
  507.          jmp .m_end
  508.   .l_apic:
  509.          cmp dword [r11 + 4], 0                   ;Local APIC Flags
  510.          jz .m_end
  511.          mov al, [r11 + 3]                        ;local APIC ID
  512.          mov [r15], al                            ;保存local APIC ID到cpu_list
  513.          inc r15
  514.          inc byte [rel num_cpus]                  ;可用的CPU数量递增
  515.          jmp .m_end
  516.   .ioapic:
  517.          mov al, [r11 + 2]                        ;取出I/O APIC ID
  518.          mov [rel ioapic_id], al                  ;保存I/O APIC ID
  519.          mov eax, [r11 + 4]                       ;取出I/O APIC物理地址
  520.          mov [rel ioapic_addr], eax               ;保存I/O APIC物理地址
  521.    .m_end:
  522.          xor rax, rax
  523.          mov al, [r11 + 1]
  524.          add r11, rax                             ;计算下一个中断控制器结构列表项的地址
  525.          cmp r11, rdi
  526.          jl .enumd

  527.          ;将Local APIC的物理地址映射到预定义的线性地址LAPIC_START_ADDR
  528.          mov r13, LAPIC_START_ADDR                ;在global_defs.wid中定义
  529.          xor rax, rax
  530.          mov eax, [rel lapic_addr]                ;取出LAPIC的物理地址
  531.          or eax, 0x1f                             ;PCD=PWT=U/S=R/W=P=1,强不可缓存
  532.          call mapping_laddr_to_page

  533.          ;将I/O APIC的物理地址映射到预定义的线性地址IOAPIC_START_ADDR
  534.          mov r13, IOAPIC_START_ADDR               ;在global_defs.wid中定义
  535.          xor rax, rax
  536.          mov eax, [rel ioapic_addr]               ;取出I/O APIC的物理地址
  537.          or eax, 0x1f                             ;PCD=PWT=U/S=R/W=P=1,强不可缓存
  538.          call mapping_laddr_to_page

  539.          ;以下测量当前处理器在1毫秒的时间里经历多少时钟周期,作为后续的定时基准。
  540.          mov rsi, LAPIC_START_ADDR                ;Local APIC的线性地址

  541.          mov dword [rsi + 0x320], 0x10000         ;定时器的本地向量表入口寄存器。单次击发(one shot)模式
  542.          mov dword [rsi + 0x3e0], 0x0b            ;定时器的分频配置寄存器:1分频(不分频)

  543.          mov al, 0x0b                             ;RTC寄存器B
  544.          or al, 0x80                              ;阻断NMI
  545.          out 0x70, al
  546.          mov al, 0x52                             ;设置寄存器B,开放周期性中断,开放更
  547.          out 0x71, al                             ;新结束后中断,BCD码,24小时制

  548.          mov al, 0x8a                             ;CMOS寄存器A
  549.          out 0x70, al
  550.          ;in al, 0x71
  551.          mov al, 0x2d                             ;32kHz,125ms的周期性中断
  552.          out 0x71, al                             ;写回CMOS寄存器A

  553.          mov al, 0x8c
  554.          out 0x70, al
  555.          in al, 0x71                              ;读寄存器C
  556.   .w0:
  557.          in al, 0x71                              ;读寄存器C
  558.          bt rax, 6                                ;更新周期结束中断已发生?
  559.          jnc .w0
  560.          mov dword [rsi + 0x380], 0xffff_ffff     ;定时器初始计数寄存器:置初值并开始计数
  561.   .w1:
  562.          in al, 0x71                              ;读寄存器C
  563.          bt rax, 6                                ;更新周期结束中断已发生?
  564.          jnc .w1
  565.          mov edx, [rsi + 0x390]                   ;定时器当前计数寄存器:读当前计数值

  566.          mov eax, 0xffff_ffff
  567.          sub eax, edx
  568.          xor edx, edx
  569.          mov ebx, 125                             ;125毫秒
  570.          div ebx                                  ;EAX=当前处理器在1ms内的时钟数

  571.          mov [rel clocks_1ms], eax                ;登记起来用于其它定时的场合

  572.          mov al, 0x0b                             ;RTC寄存器B
  573.          or al, 0x80                              ;阻断NMI
  574.          out 0x70, al
  575.          mov al, 0x12                             ;设置寄存器B,只允许更新周期结束中断
  576.          out 0x71, al

  577.          ;以下安装用于任务切换的中断处理过程
  578.          mov r9, [rel position]
  579.          lea rax, [r9 + rtm_interrupt_handle]     ;得到中断处理过程的线性地址
  580.          call make_interrupt_gate                 ;位于core_utils64.wid

  581.          cli

  582.          mov r8, 0x28                             ;任务切换使用的中断向量
  583.          call mount_idt_entry                     ;位于core_utils64.wid

  584.          ;设置和时钟中断相关的硬件
  585.          mov al, 0x0b                             ;RTC寄存器B
  586.          or al, 0x80                              ;阻断NMI
  587.          out 0x70, al
  588.          mov al, 0x12                             ;设置寄存器B,禁止周期性中断,开放更
  589.          out 0x71, al                             ;新结束后中断,BCD码,24小时制

  590.          in al, 0xa1                              ;读8259从片的IMR寄存器
  591.          and al, 0xfe                             ;清除bit 0(此位连接RTC)
  592.          out 0xa1, al                             ;写回此寄存器

  593.          sti

  594.          mov al, 0x0c
  595.          out 0x70, al
  596.          in al, 0x71                              ;读RTC寄存器C,复位未决的中断状态

  597.          ;计算机启动后,默认使用经由LINT0的虚拟线模式。所以,第6章实际上工作在这种模式下。
  598.          ;LVT LINT0寄存器的默认值:0x700,不屏蔽LINT0,ExtINT投递模式
  599.          ;LVT LINT1寄存器的默认值:0x400,不屏蔽LINT1,NMI投递模式


  600.          ;如果不使用8259A PIC,直接使用I/O APIC,则应当屏蔽LVT LINT0或者8259A PIC的输入。
  601.          ;以下两种方式可以选择一种即可。建议选择第二种,即,屏蔽8259A的全部中断输入。
  602.          ;mov rsi, LAPIC_START_ADDR
  603.          ;mov dword [rsi + 0x350], 0x10000         ;屏蔽LINT0的中断信号

  604.          mov al, 0xff                             ;屏蔽所有发往8259A主芯片的中断信号
  605.          out 0x21, al                             ;多处理器环境下不再使用8259芯片


  606.          mov rdi, IOAPIC_START_ADDR               ;I/O APIC的线性地址

  607.          ;根据图纸可知,若选择RTC定时器,需要设置I/O APIC的I/O重定向表寄存器8(IOREDTBL8)
  608.          ;mov dword [rdi], 0x20                    ;对应RTC。
  609.          ;mov dword [rdi + 0x10], 0x00000028       ;不屏蔽;物理模式;固定模式;向量0x28
  610.          ;mov dword [rdi], 0x21
  611.          ;mov dword [rdi + 0x10], 0x00000000       ;Local APIC ID:0

  612.          ;如果想加快任务切换速度,可选择8254定时器。对应I/O APIC的IOREDTBL2
  613.          ;mov dword [rdi], 0x14                    ;对应8254定时器。
  614.          ;mov dword [rdi + 0x10], 0x00000028       ;不屏蔽;物理模式;固定模式;向量0x28
  615.          ;mov dword [rdi], 0x15
  616.          ;mov dword [rdi + 0x10], 0x00000000       ;Local APIC ID:0


  617.          ;也可以使用Local APIC内部的定时器,更加灵活。
  618.          mov eax, [rel clocks_1ms]
  619.          mov ebx, 3000
  620.          mul ebx
  621.          mov rsi, LAPIC_START_ADDR                ;Local APIC的线性地址
  622.          mov dword [rsi + 0x3e0], 0x0b            ;1分频(不分频)
  623.          mov dword [rsi + 0x320], 0x20028         ;周期性模式;固定模式;中断向量:0x28
  624.          mov dword [rsi + 0x380], eax             ;初始计数值

  625.          ;以下开始创建系统外壳任务(进程)
  626.          mov r8, 50
  627.          call create_process

  628.          mov rbx, [rel pcb_ptr]                   ;得到外壳任务PCB的线性地址
  629.          mov rax, [rbx + 56]                      ;从PCB中取出CR3
  630.          mov cr3, rax                             ;切换到新进程的地址空间

  631.          mov [rel cur_pcb], rbx                   ;设置当前任务的PCB。
  632.          mov qword [rbx + 16], 1                  ;设置任务状态为“忙”。

  633.          mov rax, [rbx + 32]                      ;从PCB中取出RSP0
  634.          mov rdx, [rel tss_ptr]                   ;得到TSS的线性地址
  635.          mov [rdx + 4], rax                       ;在TSS中填写RSP0

  636.          push qword [rbx + 208]                   ;用户程序的SS
  637.          push qword [rbx + 120]                   ;用户程序的RSP
  638.          pushfq
  639.          push qword [rbx + 200]                   ;用户程序的CS
  640.          push qword [rbx + 192]                   ;用户程序的RIP

  641.          iretq                                    ;返回当前任务的私有空间执行

  642. ;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  643. core_end:
复制代码

3

主题

11

回帖

69

积分

注册会员

积分
69
 楼主| 发表于 2024-3-6 11:44:19 | 显示全部楼层
站长 发表于 2024-3-6 11:32
听说代码总发不全,我来试试。

老师为啥我的就显示不全,弄了好几次

350

主题

281

回帖

2834

积分

管理员

积分
2834
发表于 2024-3-6 13:37:50 | 显示全部楼层
Unis 发表于 2024-3-6 11:44
老师为啥我的就显示不全,弄了好几次

俺也不太知道诶,,
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|鼠侠网 ( 吉ICP备19001332号 )

GMT+8, 2024-9-17 03:42 , Processed in 0.251722 second(s), 21 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表