MACOS 访问串口教程https://software.intel.com/en-us/setting-up-serial-terminal-on-system-with-mac-os-x
命令行
qemu-system-aarch64 -M raspi3 -k en-us -serial null -serial stdio -drive file=sdcard.img,format=raw -kernel kernel8.img
arm中的cpsr相当于MIPS CP0里的status
arm中 通过w0-30表示32位引用寄存器的方法, x0-30表示64位引用寄存器的方法
Broadcom Arm Cortex A53-architecture processor 是一个arm 处理器 为v8代ARM
将uart to usb转换口连接到GPIO上, 将kernel8.img和相关配置文件拷贝到microsd卡上, 连接usb,
上电, 启动, 使用screen -L /dev/cu.usbserial-00006014 115200
指令观察串口输出。
配置include.mk, 修改gcc和ld的路径到自己电脑安装交叉编译器的路径
在start.S中, 首先获得当前CPU的编号
获得CPU编号的方法很简单, 通过MPIDR_EL1寄存级获得, 通过查阅手册, 可以发现
MPIDR_EL1这个寄存器可以提供关于当前运行的CPU的核的信息, 想要知道当前是第几个核心, 只需要访问最后四位就好了。
mrs x0, mpidr_el1
[外链图片转存失败(img-Zi99XmLM-1562856918022)(/Users/edward/Library/Application Support/typora-user-images/image-20190528153456748.png)]
[外链图片转存失败(img-nPKYt2K5-1562856918023)(/Users/edward/Library/Application Support/typora-user-images/image-20190528153524359.png)]
使用mrs指令以64位的方式放入0号寄存器中。
如果这是0号CPU的话, 也就是最低三位都是0, 那么跳转到main函数。
否则一直循环。
参考了教程中的函数, 实现了uart和读取功能。
我把和GPIO相关的宏放到了gpio.h中, UART的宏定义放在uart.h中, uart的函数放在uart.c中
设置14, 15号为uart的端口, 初始化GPIO, 并且设置波特率为115200b/s
需要注意的是树莓派显示下\r和]\n的转化
通过这一个lab的学习, 我理解了之前计组的软件硬件接口。
在计算机中, 任何一个软件层级都采用了封装的方式去隐藏细节, 通过提供接口来给外界去使用。
而对于CPU来讲, 寄存器+手册就是CPU的接口, OS通过这个接口访问CPU, 同时给出自己的接口给用户。
对于ARM来说有不同的处理器权限
MAIR_EL1
这个寄存器的目的是给内存属性提供编码方式
通过查阅手册, 可以发现这个寄存器有8个attribute, 每个attribute8位
[外链图片转存失败(img-A0bjgdF7-1562856918024)(/Users/edward/Library/Application Support/typora-user-images/image-20190611230131206.png)]
通过下面的编码方式可以用户自定义内存属性
举个例子, 比如PTE_DEVICE = 1<<2, 也就是说PTE_DEVICE定义的内存在attr1里
那么ATTR1中存入, 0x04 对应device-nGnREmemeory
在arm v8中, G代表Gathering, R代表Reordering, E 代表early-achnowledge
[外链图片转存失败(img-Lq6N6Nkq-1562856918024)(/Users/edward/Library/Application Support/typora-user-images/image-20190611225902317.png)]
翻译控制寄存器
TCR_EL1是设置翻译控制的一个寄存器
[外链图片转存失败(img-kTkXfzc8-1562856918024)(/Users/edward/Library/Application Support/typora-user-images/image-20190611230917298.png)]
查到的资料表示, 这个寄存器表示哪个寄存器用来显示一级页表的地址。
TCR_EL1的38-0位是有效的
应设置使TTBR1_EL1作为内核空间一级页表物理地址, TTBR0_EL1用于用户空间
isb清除流水线中所有的指令
EL0 EL1 EL2 EL3
[外链图片转存失败(img-fb2mmYlZ-1562856918025)(/Users/edward/Library/Application Support/typora-user-images/image-20190526212639817.png)]
通过阅读手册, 可以知道这四种权限的功能。
[外链图片转存失败(img-WV5dD13A-1562856918025)(/Users/edward/Library/Application Support/typora-user-images/image-20190528155350192.png)]
[外链图片转存失败(img-Vqyikcy6-1562856918025)(/Users/edward/Library/Application Support/typora-user-images/image-20190528155331814.png)]
currentEL[3:2]代表了当前处于哪一级, 比如currentEL为4说明为EL1
在设置MMU之前, 首先需要回落到EL1
通过检查currentEl判断当前到底在哪个权限级上
[外链图片转存失败(img-70Bq6iib-1562856918026)(/Users/edward/Library/Application Support/typora-user-images/image-20190603223808046.png)]
在这张图中, 可以看到Arm cortex-A53的内存模型, 每个core有一个L1 cache, 同时四个core共用一个L2 cache, 在我的树莓派OS设计中, 我只用到了一个core。
在start.S中, 首先设置栈指针为0x80000, 然后进入vm函数
在这个函数中, 我做了如下事情
用boot_alloc分配一页内存给pgdir
用boot_map把0-P_LIMIT 和P_LIMIT往上0x1000000和0x40000000上BY2PG大小
分别对应正常的内存, IO以及控制寄存器
经过设置vm后, 开始一些寄存器的设置
首先通过currentel判断是否处在EL1
之后用给的参考代码来从EL2回落到EL1
之后再start里, 我设置了指导书中的一些寄存器
设置mair_el1为 0x440488, 代表内存类型
设置tcr_el1 0x2BA593A19,
把ttbr0_EL1和ttbr1_el1都设置为0x00200000
设置系统控制寄存器sctlr_el1为0x30d51825
之后, 仿照MIPS OS, 把boot_pgdir, 等函数改成 9 9 9 12三级页表的方式,
设置mmu.h中的宏
按照如下方式分配内存
#define K_TIMESTACK_TOP KADDR(P_TIMESTACK_TOP)
#define K_ENVS_BASE KADDR(P_ENVS_BASE)
#define K_PAGES_BASE KADDR(P_PAGES_BASE)
#define K_PGDIR_BASE KADDR(P_PGDIR_BASE)
#define K_STACK_TOP KADDR(P_STACK_TOP)
#define U_PAGES_BASE 0x90000000
#define U_ENVS_BASE 0x80000000
#define U_LIMIT 0x80000000
#define U_XSTACK_TOP 0x80000000
#define U_STACK_TOP 0x01000000
#define U_TEXT 0x00000000
通过page_alloc和page_insert来进行内存的分配
在pmap中, 除了pgdir_walk的时候要考虑三级页表pde, pme, 和pte之外,其他和MIPS OS基本相同。
ARM中R15位pc, 通过ISB, 冲刷
从0x00B00000开始存储ENV结构体
Env结构体里存储了trapframe, env_id, parent_id, ipc value, ipc from ,ipc recving, env_pgfault_handler, env_xstacktop等变量。 和mips实验基本保持一致
在env_init里, 初始化envs数组, 把所有env结构体设置为ENV_FREE
创建一个进程的时候, 通过env_create, 读入u_char *binary和文件大小
- 调用env_alloc 分配一个env结构体
- 调用load_icode, 用load_elf把程序读入到entry point, 之后设置trapframe的elr, 让进程被调度的时候从elr开始运行,
在运行进程的时候, 首先把TIMESTACK中的内容拷贝到当前进程的trapframe结构体里
之后把 curenv 改为要新运行的进程
设置ttbr为curenv的 pgdir地址
最后tlb_invalidate, 运行新进程。
dsb data memry barrier
tlbi invalidate tlb
Isb 清空延迟槽中的所有指令
通过向cntp_tval寄存器写入开启中断
通过cntp_ctl_el0寄存器写入来控制主频
处理时钟中断的时候
首先push time stack
之后跳转到handle_int
系统调用在MIPS lab的基础上加入了emmc read
使用svc指令陷入内核态, 然后跳转到handle syscall
根据传入的sys callnumber 跳转到对应的处理函数
进程通信和MIPS 实验没有改动
fork中 写一个三重循环for i, j, k 遍历三级页表, 来复制页面
写时复制机制和MIPS类似, PTE_USER为EL0, EL1都可访问,
PTE_4KB仅仅为EL1可以访问
注意 如果读多了 会read_blk报错
macos下dd教程
https://www.cyberciti.biz/faq/how-to-create-disk-image-on-mac-os-x-with-dd-command/
diskutil list
diskutil unmountDisk /dev/disk2
dd if=user/pingpong.elf of=/dev/disk2 seek=4000 bs=512
写了后, 后面的磁盘会被清除cat
在文件系统中, 把文件系统挂载在了10000扇区开始的地方, 把MIPS OS里的read_block替换成EMMC的驱动, 就形成了树莓派的文件系统。
[外链图片转存失败(img-HNTIIMfe-1562856918026)(/Users/edward/Desktop/image-20190623000757962.png)]
LAB6需要修改的主要是spawn和run.sh
在spawn中需要自己实现一个系统调用来完成set elr的工作, 此外在加载程序的时候, 还要用64位的elf方式加载。
其余部分, 参考MIPSOS和调用LAB5的接口就可以实现