失败的尝试——为MOS添加VGA驱动
每一个OS都有一个宿命——运行DOOM,为了让Mos也能运行DOOM,我尝试给Mos写VGA驱动。
当然最后失败了,在这里记录一下开发的过程,如果有这方面相关经验能够解决这个问题的欢迎联系我
我不明白,请帮助我
什么是VGA驱动
VGA(Video Graphics Array)是一种视频显示标准,最初由IBM在1987年引入。它是用于计算机显示器的图形标准,定义了屏幕分辨率、色彩深度和信号传输的方式。VGA已经成为计算机显示器和图形卡的基础标准之一,尽管在现代已经被更高级的标准(如DVI、HDMI和DisplayPort)所取代。(来自GPT)
为了让Mos能够在显示设备(显示器)上显示东西,我们需要按照VGA规范给Mos提供一个VGA驱动软件。
为什么选择VGA
因为Malta(Mos运行的主板)似乎只支持这一种视频规范,通过查询qemu的文档,我们能看到mips处理器虚拟化支持这几种主板
注意,其中两种主板是64位处理器,而mipssim并不支持VGA。
那么怎么写一个驱动呢
首先我们要弄清楚VGA是怎么一种规范,以及怎么去控制实现了VGA规范的硬件设备。最终,我找到了这个VGA文档,为了理解怎么控制VGA便花费了两天的时间,略过这段过程,简单来说,要让VGA能够显示需要做两件事
- 设置VGA的寄存器(VGA有上百个寄存器,通过设置它们我们可以设置VGA的参数)
- 设置VGA的显示缓冲
控制VGA的behavior,常用的设置类型有几种,我们想让VGA能够显示一块画面,从而运行DOOM,因此我们想要的是Mode 13h(320x200 linear 256-color mode),在这里可以查询到寄存器的详细信息。
要走到这一步,我花费了大概20个小时来理解寄存器有什么作用以及在互联网上搜索VGA这个过时规范的信息(真的很难找)
写一个VGA驱动
不过走到这儿了,其他不就明朗了吗
不不不,这里才是痛苦的开始
为了设置VGA的寄存器和显示缓冲,我们需要qemu的支持,那么qemu是怎么处理主板和硬件的交互呢?
通过查询qemu的文档,我们可以找到
设置寄存器
qemu通过MMIO的方式设置寄存器,根据VGA规范,VGA寄存器会被映射到0x03c0 - 0x03df这个地址区间。
VGA还有一些比较特别的地方:由于寄存器数目众多,VGA规范规定设置大部分寄存器通过先设置一个地址寄存器,再读写一个值寄存器来设置寄存器的值,细节可以查询VGA文档,此处不赘述。
那么就直接向这个地址读写就可以了吗?
不行,实际上,大部分外设都通过MMIO的方式来实现硬件寄存器-地址的映射关系,所以我们应该写的地址是MMIO基地址+寄存器地址。
MMIO映射
那么这个基地址在哪儿呢,很遗憾,如果查询qemu文档或者查询Malta文档,都没有办法找到这个基地址,Mos关于Malta的定义中有这样的代码
/*
* QEMU MMIO address definitions.
*/
#define MALTA_PCIIO_BASE 0x18000000
#define MALTA_FPGA_BASE 0x1f000000
这就是我们需要的基地址吗?
先说结论,是的,就是这个地址,那么这个地址是哪儿来的呢?
花费了很长的时间,搜遍了互联网,第一次见到这个地址是在qemu的源码处,malta的实现部分有以下代码
/*
* Load BAR registers as done by YAMON:
*
* - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff
* - set up PCI0 MEM0 at 0x10000000, size 0x7e00000
* - set up PCI0 MEM1 at 0x18200000, size 0xbc00000
*
*/
由此,我们不仅找到了MMIO的偏移(0x18000000),还找到了我们想要的显示缓冲的地址偏移(0x18200000)
实践
然而事情没有这么简单,经过编写代码发现
- MMIO是有效的,可以正常写入寄存器
- 显示缓冲的地址映射无效,写不进去
显示缓冲
前情提要,查询VGA规范可以知道,根据不同设置,显示缓冲会被映射到
00 -- A0000h-BFFFFh -- 128K
01 -- A0000h-AFFFFh -- 64K
10 -- B0000h-B7FFFh -- 32K
11 -- B8000h-BFFFFh -- 32K
类似的,我们除了这个地址,还需要一个基地址偏移,也就是上文中找到的0x18200000
那么写入不了显示缓冲到底是为什么呢?我在这个地方尝试了很多办法,最终发现了qemu的终端可以显示目前的地址映射,让我们看看映射的结果是什么样的(如果想要尝试复现这个结果,我的代码在Mos仓库的vga分支上)
在qemu的debug终端输入info mtree
address-space: memory
0000000000000000-ffffffffffffffff (prio 0, i/o): system
0000000000000000-0000000003ffffff (prio 0, ram): alias mips_malta_low_preio.ram @mips_malta.ram 0000000000000000-0000000003ffffff
0000000000000000-000000001fffffff (prio -10000, i/o): empty-slot
0000000010000000-0000000017ffffff (prio 0, i/o): alias pci0-mem0 @pci0-mem 0000000010000000-0000000017ffffff
0000000018000000-00000000181fffff (prio 0, i/o): alias pci0-io @io 0000000000000000-00000000001fffff
0000000018200000-000000001bdfffff (prio 0, i/o): alias pci0-mem1 @pci0-mem 0000000018200000-000000001bdfffff
000000001be00000-000000001be00fff (prio 0, i/o): gt64120-isd
000000001e000000-000000001e3fffff (prio 0, romd): mips_malta.bios
000000001f000000-000000001f0008ff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000000-00000000000008ff
000000001f000900-000000001f00093f (prio 0, i/o): serial
000000001f000a00-000000001f0fffff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000a00-00000000000fffff
000000001fc00000-000000001fffffff (prio 0, rom): bios.1fc
0000000080000000-0000000083ffffff (prio 0, ram): mips_malta.ram
address-space: I/O
0000000000000000-000000000000ffff (prio 0, i/o): io
0000000000000000-0000000000000003 (prio 0, i/o): acpi-evt
0000000000000004-0000000000000005 (prio 0, i/o): acpi-cnt
0000000000000008-000000000000000b (prio 0, i/o): acpi-tmr
0000000000000000-0000000000000007 (prio 0, i/o): dma-chan
0000000000000008-000000000000000f (prio 0, i/o): dma-cont
0000000000000020-0000000000000021 (prio 0, i/o): pic
0000000000000040-0000000000000043 (prio 0, i/o): pit
0000000000000060-0000000000000060 (prio 0, i/o): i8042-data
0000000000000064-0000000000000064 (prio 0, i/o): i8042-cmd
0000000000000070-0000000000000071 (prio 0, i/o): rtc
0000000000000070-0000000000000070 (prio 0, i/o): rtc-index
0000000000000081-0000000000000083 (prio 0, i/o): dma-page
0000000000000087-0000000000000087 (prio 0, i/o): dma-page
0000000000000089-000000000000008b (prio 0, i/o): dma-page
000000000000008f-000000000000008f (prio 0, i/o): dma-page
00000000000000a0-00000000000000a1 (prio 0, i/o): pic
00000000000000b2-00000000000000b3 (prio 0, i/o): apm-io
00000000000000c0-00000000000000cf (prio 0, i/o): dma-chan
00000000000000d0-00000000000000df (prio 0, i/o): dma-cont
0000000000000170-0000000000000177 (prio 0, i/o): ide
00000000000001ce-00000000000001d1 (prio 0, i/o): vbe
00000000000001f0-00000000000001f7 (prio 0, i/o): ide
00000000000002f8-00000000000002ff (prio 0, i/o): serial
0000000000000376-0000000000000376 (prio 0, i/o): ide
0000000000000378-000000000000037f (prio 0, i/o): parallel
00000000000003b4-00000000000003b5 (prio 0, i/o): vga
00000000000003ba-00000000000003ba (prio 0, i/o): vga
00000000000003c0-00000000000003cf (prio 0, i/o): vga
00000000000003d4-00000000000003d5 (prio 0, i/o): vga
00000000000003da-00000000000003da (prio 0, i/o): vga
00000000000003f1-00000000000003f5 (prio 0, i/o): fdc
00000000000003f6-00000000000003f6 (prio 0, i/o): ide
00000000000003f7-00000000000003f7 (prio 0, i/o): fdc
00000000000003f8-00000000000003ff (prio 0, i/o): serial
00000000000004d0-00000000000004d0 (prio 0, i/o): elcr
00000000000004d1-00000000000004d1 (prio 0, i/o): elcr
0000000000000cf9-0000000000000cf9 (prio 1, i/o): reset-control
0000000000001100-000000000000113f (prio 0, i/o): pm-smbus
000000000000afe0-000000000000afe3 (prio 0, i/o): acpi-gpe0
address-space: cpu-memory-0
0000000000000000-ffffffffffffffff (prio 0, i/o): system
0000000000000000-0000000003ffffff (prio 0, ram): alias mips_malta_low_preio.ram @mips_malta.ram 0000000000000000-0000000003ffffff
0000000000000000-000000001fffffff (prio -10000, i/o): empty-slot
0000000010000000-0000000017ffffff (prio 0, i/o): alias pci0-mem0 @pci0-mem 0000000010000000-0000000017ffffff
0000000018000000-00000000181fffff (prio 0, i/o): alias pci0-io @io 0000000000000000-00000000001fffff
0000000018200000-000000001bdfffff (prio 0, i/o): alias pci0-mem1 @pci0-mem 0000000018200000-000000001bdfffff
000000001be00000-000000001be00fff (prio 0, i/o): gt64120-isd
000000001e000000-000000001e3fffff (prio 0, romd): mips_malta.bios
000000001f000000-000000001f0008ff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000000-00000000000008ff
000000001f000900-000000001f00093f (prio 0, i/o): serial
000000001f000a00-000000001f0fffff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000a00-00000000000fffff
000000001fc00000-000000001fffffff (prio 0, rom): bios.1fc
0000000080000000-0000000083ffffff (prio 0, ram): mips_malta.ram
address-space: pci0-mem
0000000000000000-00000000ffffffff (prio 0, i/o): pci0-mem
00000000000a0000-00000000000affff (prio 2, ram): alias vga.chain4 @vga.vram 0000000000000000-000000000000ffff
00000000000a0000-00000000000bffff (prio 1, i/o): vga-lowmem
address-space: gt64120_pci
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: piix4-isa
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: piix4-ide
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: piix4-usb-uhci
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: PIIX4_PM
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: pcnet
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
address-space: VGA
0000000000000000-ffffffffffffffff (prio 0, i/o): bus master container
memory-region: mips_malta.ram
0000000080000000-0000000083ffffff (prio 0, ram): mips_malta.ram
memory-region: pci0-mem
0000000000000000-00000000ffffffff (prio 0, i/o): pci0-mem
00000000000a0000-00000000000affff (prio 2, ram): alias vga.chain4 @vga.vram 0000000000000000-000000000000ffff
00000000000a0000-00000000000bffff (prio 1, i/o): vga-lowmem
memory-region: io
0000000000000000-000000000000ffff (prio 0, i/o): io
0000000000000000-0000000000000003 (prio 0, i/o): acpi-evt
0000000000000004-0000000000000005 (prio 0, i/o): acpi-cnt
0000000000000008-000000000000000b (prio 0, i/o): acpi-tmr
0000000000000000-0000000000000007 (prio 0, i/o): dma-chan
0000000000000008-000000000000000f (prio 0, i/o): dma-cont
0000000000000020-0000000000000021 (prio 0, i/o): pic
0000000000000040-0000000000000043 (prio 0, i/o): pit
0000000000000060-0000000000000060 (prio 0, i/o): i8042-data
0000000000000064-0000000000000064 (prio 0, i/o): i8042-cmd
0000000000000070-0000000000000071 (prio 0, i/o): rtc
0000000000000070-0000000000000070 (prio 0, i/o): rtc-index
0000000000000081-0000000000000083 (prio 0, i/o): dma-page
0000000000000087-0000000000000087 (prio 0, i/o): dma-page
0000000000000089-000000000000008b (prio 0, i/o): dma-page
000000000000008f-000000000000008f (prio 0, i/o): dma-page
00000000000000a0-00000000000000a1 (prio 0, i/o): pic
00000000000000b2-00000000000000b3 (prio 0, i/o): apm-io
00000000000000c0-00000000000000cf (prio 0, i/o): dma-chan
00000000000000d0-00000000000000df (prio 0, i/o): dma-cont
0000000000000170-0000000000000177 (prio 0, i/o): ide
00000000000001ce-00000000000001d1 (prio 0, i/o): vbe
00000000000001f0-00000000000001f7 (prio 0, i/o): ide
00000000000002f8-00000000000002ff (prio 0, i/o): serial
0000000000000376-0000000000000376 (prio 0, i/o): ide
0000000000000378-000000000000037f (prio 0, i/o): parallel
00000000000003b4-00000000000003b5 (prio 0, i/o): vga
00000000000003ba-00000000000003ba (prio 0, i/o): vga
00000000000003c0-00000000000003cf (prio 0, i/o): vga
00000000000003d4-00000000000003d5 (prio 0, i/o): vga
00000000000003da-00000000000003da (prio 0, i/o): vga
00000000000003f1-00000000000003f5 (prio 0, i/o): fdc
00000000000003f6-00000000000003f6 (prio 0, i/o): ide
00000000000003f7-00000000000003f7 (prio 0, i/o): fdc
00000000000003f8-00000000000003ff (prio 0, i/o): serial
00000000000004d0-00000000000004d0 (prio 0, i/o): elcr
00000000000004d1-00000000000004d1 (prio 0, i/o): elcr
0000000000000cf9-0000000000000cf9 (prio 1, i/o): reset-control
0000000000001100-000000000000113f (prio 0, i/o): pm-smbus
000000000000afe0-000000000000afe3 (prio 0, i/o): acpi-gpe0
memory-region: malta-fpga
0000000000000000-00000000000fffff (prio 0, i/o): malta-fpga
memory-region: vga.vram
0000000000000000-0000000000ffffff (prio 0, ram): vga.vram
memory-region: system
0000000000000000-ffffffffffffffff (prio 0, i/o): system
0000000000000000-0000000003ffffff (prio 0, ram): alias mips_malta_low_preio.ram @mips_malta.ram 0000000000000000-0000000003ffffff
0000000000000000-000000001fffffff (prio -10000, i/o): empty-slot
0000000010000000-0000000017ffffff (prio 0, i/o): alias pci0-mem0 @pci0-mem 0000000010000000-0000000017ffffff
0000000018000000-00000000181fffff (prio 0, i/o): alias pci0-io @io 0000000000000000-00000000001fffff
0000000018200000-000000001bdfffff (prio 0, i/o): alias pci0-mem1 @pci0-mem 0000000018200000-000000001bdfffff
000000001be00000-000000001be00fff (prio 0, i/o): gt64120-isd
000000001e000000-000000001e3fffff (prio 0, romd): mips_malta.bios
000000001f000000-000000001f0008ff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000000-00000000000008ff
000000001f000900-000000001f00093f (prio 0, i/o): serial
000000001f000a00-000000001f0fffff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000a00-00000000000fffff
000000001fc00000-000000001fffffff (prio 0, rom): bios.1fc
0000000080000000-0000000083ffffff (prio 0, ram): mips_malta.ram
主要关注这两个地方
address-space: memory
0000000000000000-ffffffffffffffff (prio 0, i/o): system
0000000000000000-0000000003ffffff (prio 0, ram): alias mips_malta_low_preio.ram @mips_malta.ram 0000000000000000-0000000003ffffff
0000000000000000-000000001fffffff (prio -10000, i/o): empty-slot
0000000010000000-0000000017ffffff (prio 0, i/o): alias pci0-mem0 @pci0-mem 0000000010000000-0000000017ffffff
0000000018000000-00000000181fffff (prio 0, i/o): alias pci0-io @io 0000000000000000-00000000001fffff
0000000018200000-000000001bdfffff (prio 0, i/o): alias pci0-mem1 @pci0-mem 0000000018200000-000000001bdfffff
000000001be00000-000000001be00fff (prio 0, i/o): gt64120-isd
000000001e000000-000000001e3fffff (prio 0, romd): mips_malta.bios
000000001f000000-000000001f0008ff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000000-00000000000008ff
000000001f000900-000000001f00093f (prio 0, i/o): serial
000000001f000a00-000000001f0fffff (prio 0, i/o): alias malta-fpga @malta-fpga 0000000000000a00-00000000000fffff
000000001fc00000-000000001fffffff (prio 0, rom): bios.1fc
0000000080000000-0000000083ffffff (prio 0, ram): mips_malta.ram
和qemu源码一样,pci的mem0和mem1,MMIO端口都正确的映射到内存的位置
然而
0000000010000000-0000000017ffffff (prio 0, i/o): alias pci0-mem0 @pci0-mem 0000000010000000-0000000017ffffff
0000000018000000-00000000181fffff (prio 0, i/o): alias pci0-io @io 0000000000000000-00000000001fffff
0000000018200000-000000001bdfffff (prio 0, i/o): alias pci0-mem1 @pci0-mem 0000000018200000-000000001bdfffff
这里可以看到,我们需要的地址范围:pci0-mem 0xa0000-0xbf000并没有被映射到内存区域,被映射的是pci总线的0x10000000-0x1bdfffff,所以才不能写入缓冲区域
怎么解决
我没有能够解决这个问题,如果有谁能解决,请联系我
或许这是我的代码问题,或许这是qemu源码的bug,或许我们再也没办法得知真相。