* 实验随记 ** Lab 1 *** 练习3 入口就是boot/start.S的_start。 mrs 读取程序状态字寄存器 mpidr_el1 掩码计算处理器位 用 cbz 跳转主核心至主核心代码,剩下代码忙等 secondary_hang *** 练习4 #+begin_src Idx Name Size VMA LMA File off Algn 0 init 0000b5b0 0000000000080000 0000000000080000 00010000 2**12 CONTENTS, ALLOC, LOAD, CODE 1 .text 000011dc ffffff000008c000 000000000008c000 0001c000 2**3 CONTENTS, ALLOC, LOAD, READONLY, CODE 2 .rodata 000000f8 ffffff0000090000 0000000000090000 00020000 2**3 CONTENTS, ALLOC, LOAD, READONLY, DATA 3 .bss 00008000 ffffff0000090100 0000000000090100 000200f8 2**4 ALLOC 4 .comment 00000032 0000000000000000 0000000000000000 000200f8 2**0 CONTENTS, READONLY #+end_src 因为内核代码高地址 0xffffff,所以 VMA 不同。 使用 MMU 处理这个映射,位于 boot/mmu.c L126-130。 *** 练习6 初始化 fp sp 位于 start_kernel 函数(kernel/head.S) 内核栈在 main.c 里定义,build/kernel.sym 可以查到地址 内核预先分配栈的总大小(sp = 栈位置 + 大小) *** 练习7 使用 info 指令查看地址:info address stack_test 断点设置:b stack_test / b *0xffffff000008c020 sp 寄存器 FP 寄存器:约定俗成通用寄存器倒数第三:$x29 打印内存,地址是 $x29,连续长度10,8字节一单位:x/10g $x29 显示寄存器:p/x $x29 调用一次:0xffffff0000092100 -> 0xffffff00000920e0 (-32) 每次压入 4 个 8 字节 *** 练习8 函数 disasm:x/30i stack_test #+begin_src asm stp x29, x30, [sp, #-32]! ; 开栈帧,保存调用方 FP、LR mov x29, sp ; 设置 FP = 新SP = 老SP - 32 str x19, [sp, #16] ; 保存寄存器 r19 #+end_src 官方的调用约定:https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#611general-purpose-registers 因为这里函数没有局部变量,所以 FP = SP **** 栈状态 ========被调用方栈======== [SP] ----> 栈上数据(这里没有) [FP] ----> 上一栈帧FP 返回地址(LR) ---------- 局部变量(这里没有) ---------- 保存的寄存器(恰好有调用方函数的调用参数,因为调用方传参是寄存器,而这里保存了下来) 空(可能是对齐) ========调用方栈======== 上一栈帧栈上数据 ------------- FP LR 保存的寄存器(之前函数的调用参数) 空 **** 实验中显然调用参数是通过寄存器传送的,怎么能打印出参数呢? 其实参数就在下个栈帧保存的之前的寄存器状态中。 ** Lab 2 *** 问题1:哪个文件或代码段中指定了 ChCore 物理内存布局 **** 编译阶段 编译时配置镜像格式的文件为 scripts/linker-aarch64.lds.in **** 运行阶段 运行阶段在 kernel/mm.c:L70 的 mm_init 设置各个地址 *** 练习1 基本思路就是回收的时候一路尝试向上合并,分配的时候找一个更大的块然后一路向下分裂。 *** 练习2 没啥特别的。不过目前只实现了用户空间的 2k 页分配。 *** 练习3 boot/mmu.c:L80-106 映射了 KBASE~KBASE+256M boot/mmu.c:L109-110 映射了 KBASE+512M~KBASE+4G 因此只需要类似操作,补全中间的 256M 即可。 注意的是,寄存器保存的地址是 paddr_t ** Lab 3 *** Capability 好像和 MC、Linux 的 Cap 都不太一样。这里的 Cap 是一个可变的描述符,用来指代一个内核资源。 比起 Cap,更像是一个对象表。 *** 练习1 **** ELF 读入 ELF Section、Segment 两个概念。 - Section 是程序中的不同节,比如 .text。 - Segment 是程序中实际分配的不同段,包括多个 Section,可以用 `readelf -l` 查看关系 主要操作就是计算偏移与复制。 其实连对齐都不用,直接就干上去也行,反正之前页表的时候已经处理了没对齐的情况。 **** 初始化线程上下文 照着文字写就行,初始化 TCB **** 切换上下文 返回上下文结构体的首地址就行 *** 练习2 process_create_root、thread_create_main 直接写在注释里面了。