oscamp 暑期 proj 周纪要
(六) 7.13:
回顾会议内容, 总结任务目标:
- 为
arceos/tour/*
添加对其他架构 (aarch64
/x86_64
/loongarch
) 的支持 - 在
arceos/tour
下新增一些例子, 体现 ArceOS 特定功能, 如图形显示功能/文件系统功能/新调度算法功能
把工具链换到最新的:
#[naked]
更改为#[unsafe(naked)]
. 裸函数使得编译器不会为函数生成序言和尾声代码(比如保存/恢复寄存器, 设置栈帧等), 操作系统开发的部分场景要求完全控制寄存器细节同时将
asm!
改为naked_asm!
. 裸函数内一般没有 Rust 代码, 因为会隐含地依赖序言和尾声, 所以几乎都是内联汇编代码naked_asm!
不支持伪指令和宏(虽然我不知道为什么原来这里要写伪指令, 可能是和 arm 统一?), 所以要把那一段全部改写成 RISC-V 汇编, 好在代码量比较少, 如果多了我还真想不出什么方便的方法
1 | naked_asm!( |
由于在裸函数汇编中必须完全手动控制行为, 所以也要删除
options(no_return)
试图迁移到其他架构:
不同的架构在 make run 时通过不同的 SBI 启动 (OpenSBI for RISC-V, SeaSBI for x86_64);
在 qemu 中模拟 x86_64 架构是模拟 q35 机器而不是像模拟 RISC-V
时那样模拟 virt
虚拟化通用机器;
不同架构对 pflash 的要求不同
aarch64 下要求 64M 的 pflash, 所以
1
dd if=/dev/zero of=/home/amiriox/oscamp/arceos/pflash.img bs=1M count=32 seek=32 conv=notrunc
另外 pflash 的起始地址也不一样, 所以必须重新调整
x86_64 下较为复杂, 查阅资料发现在 q35 机器中 pflash 被严格用于固件而不能作为通用闪存设备, 且要求两块 Flash 芯片: 只读的 unit0 包含了 UEFI/BIOS 代码, 可读写的 unit1 包含一些固件变量, 后者严格依赖前者.
所以为 x86_64 多写一行
1 | -drive if=pflash,readonly=on,file=$(CURDIR)/dummy_code.img,unit=0 |
其中 dummy_code.img
是空镜像. 启动后没有任何输出,gdb
调试发现甚至都没进 _start,一直在执行 add %al,(%rax)
(一个全 0 的机器码)
我好好想了下, unit=0
里应当是 BIOS 固件代码,
我传了个空镜像进去, 那自然最开始执行的就应该是全 0 代表的机器码了.
换句话说, unit=0
的 pflash 里的 BIOS 优先级更高,
如果有则会优先执行 (此时 QEMU 负责启动的 SeaBIOS 就相当于被忽略的板载
BIOS), 而 unit=1
的 “NVRAM_SLOT” 是不能单独使用的,
根本矛盾在于这个例子需要将 pflash 作为通用闪存使用而 q35
机型禁止这样操作. (相反对于统一 virtio
接口的
virt
机型, pflash 则仅仅是通用存储设备,
可以单纯地作为块设备读入)
如果试图使用 unit=0, 会导致直接进入空的 SBI 反复执行
add %al,(%rax)
; 使用 unit=1
要求
unit=0
存在 (连续定义), 所以也行不通; 而 q35 不支持更多的
pflash 了.
(一) 7.15:
riscv64-qemu-virt
的 pflash 在 MMIO 段中, ArceOS 作为 Unikernel 启动会 map 内核镜像+MMIO段+空余内存, 所以可以正常访存; 而且此时是还没有处理 PAGE_FAULT 的 Trap Handler 的, 由于ああrch64-qemu-virt
规定的 pflash 并不在 MMIO 段, 而是0x0400_0000
, 所以并不在new_kernel_space
的映射范围内, 会触发无法处理的 Trap 的 panic. 所以new_kernel_space
要额外处理一下这段的映射u_1_0
到u_8_0
均能在 aarch64 下正常运行, aarch64 的迁移宣告成功 f91e0b0.- TODO: 需要把额外生成的镜像写进 Makefile (已完成)
(二) 7.16:
使用 ramdisk 代替 pflash. 先创建个空的 ramdisk.img
(TODO: 写入 makefile)
1 | dd if=/dev/zero of=ramdisk.img bs=1M count=32 |
根据 Multiboot 规范规定, multiboot 相关的信息 (包括 ramdisk 信息) 由
Bootloader 放在 %rbx
寄存器 ( magic 在 %rax
),
multiboot.S
已经把这 magic 和 multiboot 信息传递给
rust_entry
的两个参数了, 所以只需要处理一下即可
1 | let mbi_info = *(mbi as *const MultibootInfo); |
关于 MultibootInfo
:
ArceOS 没有 Multiboot 信息的处理, 所以要按照规范手写结构体:
1 | /// Multiboot 1 info struct |
考虑到这个 info 是一个只能被初始化一次的静态变量, 所以考虑使用
OnceCell
1 | once_cell = { version = "^1.5.0", default-features = false, features = ["critical-section"] } |
(default-features = false
是 no_std
环境下必要的, 而必须开了临界区的 feature 才能用
OnceCell
.)
由于是内核极早期, 此时在全局分配器初始化之前, 所以用不了
alloc::boxed::Box
, 所以 once_cell
引以为傲的
no_std
下的 once_cell::race::OnceBox
也用不了.
1 | use once_cell::sync::OnceCell; |
但这样还不够, 还需要手写一个临界区的 acquire 和 release 逻辑才行, 否则链接器会抱怨找不到符号
1 | unsafe impl critical_section::Impl for MyCriticalSection { |
获取 multiboot info:
1 | { |
(qemu 的 bootloader 似乎不怎么好好填结尾地址, 所以得根据传入参数算
size
)
1 | ifeq ($(ARCH), x86_64) |
(五) 7.18:
完成了 virtio-blk
的新 tour/u_9_0
.
u_7_0
只展示了 block 的大小等信息, u_9_0
展示了 block 的读写
TODO: 添加多块读写和随机访问测试
首先获取信息的初始化和 7 差不多都是
axdriver::init_drivers()
(这部分在 ArceOS
的获取设备相关解析过, 之前写的那篇博客也会更新); 对其中一个块
all_devices.block.take_one()
打印设备名
(virtio-blk
) 和设备类型 (Block
)
测试读写: 构造字符串通过 copy_from_slice
拷贝到缓冲区,
然后通过 AxBlockDevice
的
write_block
/flush
/read_block
测试功能, 最后比较读入与写入是否一致.
(六, 日) 7.10~7.20:
试着根据已有的 ArceOS 代码直接写图形界面:
ax_framebuffer_info
获取 fb_info
,
得到显示资源的长宽和一个显存缓冲区,
根据二维到一维的映射关系向其中写入十六进制 RGBA 颜色值, 然后
ax_framebuffer_flush
刷新屏幕.
但是不知道为什么 ax_framebuffer_flush
会始终阻塞,
并且无法显示颜色.
(一..五) 7.21~7.25:
阅读 rCore Tutorial 原版的第九章, 关于 I/O 设备和驱动程序相关的内容
并整理复习: (草稿)从设备到操作系统: 如何编写驱动 | Amiriox’s Storage
(六) 7.26:
阅读 ArceOS 关于驱动部分的源代码:
axdisplay
-> axdriver_crates
(axdriver_virtio
-> axdriver_display
) ->
virtio-drivers
其中 virio-drivers
这个 crate 就是 rCore Tutorial
第九章的例子
然后用 Bresenham 算法画两点之间直线/矩形, 用中点画圆法绘制圆形, 实现了简单几何图形的绘制
进一步计划:
- 整理几何图形绘制算法的博客
- 整理一下 Makefile
- 考虑为
axdriver_gpu
新增光标位置的接口 - 并在操作系统引入
virtio-input
设备实现光标操作 - GUI 基础控件
- 音频设备驱动和相关的 tour
(日..四) 7.27-7.31:
读 OSTEP
(五..六) 8.1-8.2:
- 完成
lvgl
到 ArceOS 的 FFI 调用和交叉编译 - 基于
lvgl_sys
实现了一个简单的图形显示demo
(日..三) 8.3-8.6:
阅读 OSTEP 到第 33 章
(四..六) 8.7-8.9:
基于 virtio-drivers
crate 编写对于 VirtIO Input
设备的驱动:
- 定义
VirtIOInputDev<H, T>
类型, 实现try_new(transport)
和BaseDriverOps
, 维护统一的设备接口 InputDeviceOps
主要有poll_event
和hash_events
两个函数, 后者判断是否有新事件, 前者 poll 出新事件has_events
的实现: 由于virtio-drivers
似乎没有提供对底层VirtQueue
的访问, 所以只能先设置一个时间缓冲区, 每次has_events
时poll
一下如果没有事件就返回 false, 否则放入事件缓冲区; 同理poll
就可以先查一查缓冲区有没有东西.- 将难以理解的事件编号等封装为可读性强的枚举
编写符合 ArceOS 设计架构的 axinput
模块:
- 全局静态变量
LazyInit<Mutex<AxInputDevice>>
表示MAIN_INPUT
- 封装
poll_event
,has_event
等接口为更 Rusty 的形式 - 最后再通过
arceos_api
封装为全局的函数, 类似ax_framebuffer_info
编写 u_12_0
展示对鼠标/平板设备的支持:
- 通过
arceos_api
暴露出的ax_input_*
轮询检查并获取事件, 如果没有就 (TODO)yield
走 - 对事件类型进行模式匹配, 分类处理 (同时进行绘图):
1 | while loop_count < max_loops { |