virt 上跑通了,说明内核和环境都没问题。剩下的就是把这个模型写出来。
最小骨架要五样东西
QEMU 的机器模型,本质是告诉内核"你在什么硬件上"。最少要凑齐五样:
【07_01_机器骨架】
代码不长,几十行就能让内核开始跑。真正花时间的是后面接外设。
第一个坑:野指针
写完第一次跑,直接崩:
qemu-system-arm: ../hw/core/loader.c:720 do_cpu_reset: code should not be reached
do_cpu_reset 里不该走到的地方走到了。查下来是 arm_boot_info 这个东西。
我图省事,把它定义成了函数里的局部变量,传给 arm_load_kernel()。但那个函数只存指针,等它后面真正要用的时候,栈早就没了——指针指向的是已经被覆盖的内存。
改成文件级 static struct arm_boot_info 就好了。这种 bug 不报编译错,跑起来才炸,而且炸的地方离你写的代码很远。
第二个坑:RAM 重复注册
RAMBlock "sama7d65.ram" already registered
因为我同时设了 default_ram_id 又自己创建了 RAM。去掉 default_ram_id 那一行就行。
QEMU 的内存模型里,机器要么用默认 RAM,要么自己建,不能两边都来。
串口:两个坑
AT91 USART 的寄存器不多,但要接对。
【07_02_USART寄存器】
坑一:PDC 寄存器在 0x100 之外。
USART 的基础寄存器到 0x20 就完了,但驱动还要访问 PDC(外设 DMA 控制器)的 PTCR(0x120)和 PTSR(0x124)。我一开始只映射了 0x100 个字节,结果驱动一写 PTCR,内核直接静默 panic——没有报错,就是不动了。
把映射区扩到 0x200,问题消失。
坑二:能输出,但收不到输入。
控制台起来了,打印正常,但敲键盘没反应。查了很久,问题在 chardev 的回调:can_receive() 返回 0 之后,QEMU 就停止往这个后端投递数据了。而我在 FIFO 满了的时候返回 0,结果 FIFO 一空就再也没人通知我。
解法是显式调用 qemu_chr_fe_accept_input(),并且把 can_receive() 改成只看 FIFO 空间。
通了之后
【07_03_串口打通】
earlycon: atmel_serial0 at MMIO 0xe2020200 (options '115200n8') OF: fdt: Machine model: Microchip SAMA7D65 (QEMU sim) atmel_usart_serial.0.auto: ttyS0 at MMIO 0xe2020200 (irq = 20, base_baud = 6250000) is a ATMEL_SERIAL printk: console [ttyS0] enabled Run /bin/sh as init process /bin/sh: can't access tty; job control turned off ~ #
注意第三行:ttyS0 ... is a ATMEL_SERIAL,这是真实的 atmel_serial 驱动注册上来的,不是 earlycon 糊出来的。base_baud 6250000 也说明时钟传对了(100 MHz / 16)。
到这一步,模型能跑 shell 了。
但还不能干活
shell 能进,但很多操作会卡住。当时以为是没接的外设的问题,后来发现是个更深的原因。
我要赚赏金
