|
| 1 | +--- |
| 2 | +title: 2024秋冬季开源操作系统训练营第四阶段总结-hbuxiaofei |
| 3 | +date: 2024-12-21 22:00:35 |
| 4 | +tags: |
| 5 | +--- |
| 6 | + |
| 7 | + |
| 8 | +## x86 架构 hypervisor SeaBIOS 引导与 Linux 启动实现 |
| 9 | + |
| 10 | +### 1. seabios 工作流程 |
| 11 | +``` |
| 12 | +(1) POST( Power On Self Test):上电自检,BIOS 对计算机硬件(CPU、主板、内存等)的检测。 |
| 13 | +(2) POST 之后的初始化与启动相关硬件(磁盘、键盘控制器等)。 |
| 14 | +(3) 为 OS 创建一些参数,如 ACPI、E820 表等。 |
| 15 | +(4) 选择引导设备,从设备中加载 BootLoader,进而启动操作系统。 |
| 16 | +``` |
| 17 | + |
| 18 | +### 2. qemu 加载seabios过程 |
| 19 | +``` |
| 20 | +(1) qemu加载 seabios 在地址的 4G 最顶端的 LOW_MMIO 区,以及低 1M 区域各有一份。 |
| 21 | +(2) cpu 的第一条取指地址为 0xFFFFFFF0,该地址指向贴近 4G 的 BIOS 的最后 16 个字节,这也是 BIOS 的第一条指令。 |
| 22 | +(3) BIOS 最后 16 个字节处,是一个长跳转指令,目的就是换到低 1M 段空间去执行 entry_post ( ORG 0xe05b ) |
| 23 | +``` |
| 24 | + |
| 25 | +### 3. kbuild 使用方法 |
| 26 | +``` |
| 27 | +参考: https://github.com/Starry-OS/Starry |
| 28 | +
|
| 29 | +# To download the tool |
| 30 | +$ cargo install kbuild |
| 31 | +$ mkdir crates |
| 32 | +$ kbuild patch add axstarry |
| 33 | +$ kbuild patch remove axstarry |
| 34 | +$ kbuild patch list |
| 35 | +``` |
| 36 | + |
| 37 | +### 4. seabios 编译方法 |
| 38 | +``` |
| 39 | +cat > .config << EOF |
| 40 | +# for qemu machine types 2.0 + newer |
| 41 | +CONFIG_QEMU=y |
| 42 | +CONFIG_ROM_SIZE=256 |
| 43 | +CONFIG_ATA_DMA=n |
| 44 | +
|
| 45 | +CONFIG_XEN=n |
| 46 | +
|
| 47 | +CONFIG_DEBUG_LEVEL=9 |
| 48 | +CONFIG_DEBUG_SERIAL=y |
| 49 | +EOF |
| 50 | +echo "CONFIG_DEBUG_LEVEL=9" >> .config |
| 51 | +
|
| 52 | +make PYTHON=python3 oldnoconfig |
| 53 | +make |
| 54 | +
|
| 55 | +``` |
| 56 | + |
| 57 | +### 5. seabios 反汇编 |
| 58 | + |
| 59 | +``` |
| 60 | +objdump -D -b binary -m i8086 bios.bin |
| 61 | +objdump -D -b binary -m i8086 romlayout.o |
| 62 | +
|
| 63 | +-M intel : 指定intel格式 |
| 64 | +``` |
| 65 | + |
| 66 | +### 6. kvm 中所有 port IO |
| 67 | +所谓端口Port IO, x86上使用in out指令进行访问, 和内存的地址空间完全隔离.(ARM上没有PIO) Guest以Linux为例: `cat /proc/ioports`查看当前OS的所有的ioports : |
| 68 | + |
| 69 | +``` |
| 70 | + 0000-0cf7 : PCI Bus 0000:00 |
| 71 | + 0000-001f : dma1 |
| 72 | + 0020-0021 : pic1 |
| 73 | + 0040-0043 : timer0 |
| 74 | + 0050-0053 : timer1 |
| 75 | + 0060-0060 : keyboard |
| 76 | + 0064-0064 : keyboard |
| 77 | + 0070-0077 : rtc0 |
| 78 | + 0080-008f : dma page reg |
| 79 | + 00a0-00a1 : pic2 |
| 80 | + 00c0-00df : dma2 |
| 81 | + 00f0-00ff : fpu |
| 82 | + 03c0-03df : vga+ |
| 83 | + 03f8-03ff : serial |
| 84 | + 0510-051b : QEMU0002:00 |
| 85 | + 0510-051b : fw_cfg_io |
| 86 | + 0600-067f : 0000:00:1f.0 |
| 87 | + 0600-0603 : ACPI PM1a_EVT_BLK |
| 88 | + 0604-0605 : ACPI PM1a_CNT_BLK |
| 89 | + 0608-060b : ACPI PM_TMR |
| 90 | + 0620-062f : ACPI GPE0_BLK |
| 91 | + 0630-0633 : iTCO_wdt.0.auto |
| 92 | + 0630-0633 : iTCO_wdt |
| 93 | + 0660-067f : iTCO_wdt.0.auto |
| 94 | + 0660-067f : iTCO_wdt |
| 95 | + 0700-073f : 0000:00:1f.3 |
| 96 | + 0700-073f : i801_smbus |
| 97 | +0cf8-0cff : PCI conf1 |
| 98 | +0d00-ffff : PCI Bus 0000:00 |
| 99 | + 1000-1fff : PCI Bus 0000:01 |
| 100 | + 2000-2fff : PCI Bus 0000:02 |
| 101 | + 3000-3fff : PCI Bus 0000:03 |
| 102 | + 4000-4fff : PCI Bus 0000:04 |
| 103 | + 5000-5fff : PCI Bus 0000:05 |
| 104 | + 6000-6fff : PCI Bus 0000:06 |
| 105 | + 7000-7fff : PCI Bus 0000:07 |
| 106 | + c040-c05f : 0000:00:1f.2 |
| 107 | + c040-c05f : ahci |
| 108 | +
|
| 109 | +``` |
| 110 | + |
| 111 | +### 7. 项目实现总结 |
| 112 | + |
| 113 | +项目刚开始, 我把seabios当作 kernel,写了个简单的 bios 来引导 seabios ,seabios成功运行 |
| 114 | +```asm |
| 115 | +.section .text |
| 116 | +.code16 |
| 117 | +.global entry16 |
| 118 | +entry16: |
| 119 | + cli |
| 120 | + cld |
| 121 | +
|
| 122 | + xor ax, ax |
| 123 | + mov ds, ax |
| 124 | + mov es, ax |
| 125 | +
|
| 126 | + ljmp 0xf000, 0xe05b |
| 127 | +``` |
| 128 | + |
| 129 | +后面通过学习vmcs的使用方法,增加了CS寄存器的设置后,seabios 可以自启动成功。 |
| 130 | +```rust |
| 131 | +VmcsGuestNW::RIP.write(entry.as_usize() & 0xffff)?; |
| 132 | +VmcsGuest16::CS_SELECTOR.write(((entry.as_usize() >> 4) & 0xf000) as u16)?; |
| 133 | +// On Intel requires 'base' to be 'selector * 16' in real mode. |
| 134 | +VmcsGuestNW::CS_BASE.write(entry.as_usize() & 0xf0000)?; |
| 135 | +``` |
| 136 | +对应的linux.tmol修改为 |
| 137 | +```toml |
| 138 | +cpu_num = 1 |
| 139 | +phys_cpu_sets = [1] |
| 140 | +entry_point = 0xf_e05b |
| 141 | +bios_path = "bios-256k.bin" |
| 142 | +bios_load_addr = 0xc_0000 |
| 143 | +kernel_path = "arceos-x86_64.bin" |
| 144 | +kernel_load_addr = 0x100_0000 |
| 145 | +# ramdisk_path = "" |
| 146 | +# ramdisk_load_addr = 0 |
| 147 | +# disk_path = "disk.img" |
| 148 | +# Memory regions with format (`base_paddr`, `size`, `flags`). |
| 149 | +memory_regions = [ |
| 150 | + [0x0000_0000, 0x1_0000, 0x13], # IO Port 64K 0b10011 |
| 151 | + [0x0001_0000, 0x400_0000, 0x7], # Low RAM 64M 0b111 |
| 152 | + [0xfec0_0000, 0x1000, 0x17], # IO APIC 4K 0b10111 |
| 153 | + [0xfee0_0000, 0x1000, 0x17], # Local APIC 4K 0b10111 |
| 154 | + [0xfed0_0000, 0x1000, 0x17], # HPET 4K 0b10111 |
| 155 | +] |
| 156 | +# Emu_devices |
| 157 | +# Name Base-Ipa Ipa_len Alloc-Irq Emu-Type EmuConfig |
| 158 | +emu_devices = [ |
| 159 | +] |
| 160 | +``` |
| 161 | + |
| 162 | +Seabios 加载内核流程,seabios加载内核是通过 fw_cfg 的 file 接口,读取 multiboo.bin 当作 rom 来加载的,这个 multiboo.bin是linux内核封装过的带有 0x55aa 标记的可以引导的 rom,seabios读取到 rom后,加载到内存中然后执行。整理需要实现内容如下(“对号” 为截至此笔记已完成的): |
| 163 | + |
| 164 | +- [x] 1. seabios第一条指令地址为: 0xf000:0xe05b, 支持设置primary vcpu第一条指令地址 entry_point. |
| 165 | + |
| 166 | + |
| 167 | + ``` |
| 168 | + 1. 目前实模式下还不支设置超过0xffff的地址 |
| 169 | + 2. 考虑设置代码段 CS 寄存器 |
| 170 | + ``` |
| 171 | +
|
| 172 | +- [x] 2. 设置虚拟化需要截获的io端口 |
| 173 | +
|
| 174 | + ``` |
| 175 | + 有些端口需要进行截获, 否则会透传到宿主机, 获取宿主机的信息, 例如pci信息, 内存大小信息等 |
| 176 | + ``` |
| 177 | +
|
| 178 | +- [ ] 3. dma 实现支持 |
| 179 | +
|
| 180 | + ``` |
| 181 | + 很多数据的传输需要通过 dma 传输 |
| 182 | + ``` |
| 183 | +
|
| 184 | +
|
| 185 | +- [ ] 4. 实现fw_cfg设备模拟 |
| 186 | +
|
| 187 | +
|
| 188 | + - [x] fw_cfg 实现 pio, 设备地址 [0x510, 0x511] |
| 189 | + ``` |
| 190 | + 告诉seabios, 虚拟化环境为 “QEMU” |
| 191 | +
|
| 192 | + ``` |
| 193 | +
|
| 194 | + - [ ] fw_cfg 实现 dma, 设备地址 [0x514] |
| 195 | + ``` |
| 196 | + 用于传输数据, 例如内核data数据等 |
| 197 | + ``` |
| 198 | +
|
| 199 | +- [x] 5. 实现rtc设备模拟, 设备地址 [0x70, 0x71] |
| 200 | +
|
| 201 | +
|
| 202 | + ``` |
| 203 | + 在虚拟化环境中, seabios 通过 rtc 几个保留的寄存器获取内存大小信息 |
| 204 | + ``` |
| 205 | +
|
| 206 | +- [ ] 6. multiboot 实现 |
| 207 | +
|
| 208 | + ``` |
| 209 | + seabios通过内核启动是通过multiboot协议启动的, 需要将内核文件进行重新封装 |
| 210 | + ``` |
| 211 | +
|
| 212 | +
|
| 213 | +- [ ] 其他 ... |
| 214 | +
|
| 215 | +
|
| 216 | +**修改链接如下:** |
| 217 | +
|
| 218 | +https://github.com/hbuxiaofei/arceos-umhv/tree/support-seabios |
| 219 | +https://github.com/hbuxiaofei/axvcpu/tree/support-seabios |
| 220 | +https://github.com/hbuxiaofei/x86_vcpu/tree/support-seabios |
| 221 | +https://github.com/hbuxiaofei/axvm/tree/support-seabios |
| 222 | +https://github.com/hbuxiaofei/axdevice/tree/support-seabios |
| 223 | +
|
| 224 | +
|
| 225 | +**运行日志:** |
| 226 | +``` |
| 227 | +[ 0.307806 0:2 axvm::vm:230] Booting VM[1] |
| 228 | +[ 0.308076 0:2 arceos_vmm::vmm:40] VM[1] boot success |
| 229 | +[ 0.308390 0:2 axtask::run_queue:393] task block: Task(2, "main") |
| 230 | +[ 0.308757 0:3 axtask::task:471] task drop: Task(4, "") |
| 231 | +[ 0.309079 0:3 axtask::run_queue:393] task block: Task(3, "gc") |
| 232 | +[ 0.309436 0:5 arceos_vmm::vmm::vcpus:240] VM[1] Vcpu[0] waiting for running |
| 233 | +[ 0.309852 0:5 arceos_vmm::vmm::vcpus:243] VM[1] Vcpu[0] running... |
| 234 | +[ 0.310227 0:5 x86_vcpu::vmx::vcpu:118] VmxVcpu bind to current processor vmcs @ PA:0x5b2000 |
| 235 | +[ 0.310751 0:5 axvm::vm:258] >>>>> exit_reason IoWrite { |
| 236 | + port: 0x70, |
| 237 | + width: Byte, |
| 238 | + data: 0x8f, |
| 239 | +} |
| 240 | + |
| 241 | +[ 0.311332 0:5 axvm::vm:289] IoWrite: 0x70 Byte 0x8f |
| 242 | +[ 0.311646 0:5 axdevice::device:96] emu: GPA:0x70..GPA:0x72 handler write port:GPA:0x70 width:1 val:0x8f |
| 243 | +[ 0.312180 0:5 axdevice::rtc:54] Rtc select 0xf |
| 244 | + |
| 245 | +[ 0.312482 0:5 axvm::vm:258] >>>>> exit_reason IoRead { |
| 246 | + port: 0x71, |
| 247 | + width: Byte, |
| 248 | +} |
| 249 | + |
| 250 | +[ 0.312984 0:5 axvm::vm:278] IoRead: 0x71 Byte |
| 251 | +[ 0.313268 0:5 axdevice::device:79] emu: GPA:0x70..GPA:0x72 handler read port:GPA:0x71 width:1 |
| 252 | +[ 0.313758 0:5 axdevice::rtc:81] Rtc read addr: GPA:0x71 GPA:0x71 |
| 253 | + |
| 254 | +[ 0.314130 0:5 axdevice::rtc:62] Rtc get index: 0xf |
| 255 | + |
| 256 | +[ 0.314454 0:5 axvm::vm:258] >>>>> exit_reason Nothing |
| 257 | + |
| 258 | +[ 0.314787 0:5 x86_vcpu::vmx::vcpu:131] VmxVcpu unbind from current processor vmcs @ PA:0x5b2000 |
| 259 | +[ 0.315285 0:5 x86_vcpu::vmx::vcpu:118] VmxVcpu bind to current processor vmcs @ PA:0x5b2000 |
| 260 | +SeaBIOS (version 1.16.0-20241104_115553-centos83-dev) |
| 261 | +BUILD: gcc: (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) binutils: version 2.30-108.el8_5.1 |
| 262 | +enabling shadow ram |
| 263 | +[ 0.316631 0:5 axvm::vm:258] >>>>> exit_reason IoWrite { |
| 264 | + port: 0xcf8, |
| 265 | + width: Dword, |
| 266 | + data: 0x80000000, |
| 267 | +} |
| 268 | + |
| 269 | +[ 0.317243 0:5 axvm::vm:289] IoWrite: 0xcf8 Dword 0x80000000 |
| 270 | +[ 0.317586 0:5 axdevice::device:96] emu: GPA:0xcf8..GPA:0xd00 handler write port:GPA:0xcf8 width:4 val:0x80000000 |
| 271 | +[ 0.318159 0:5 axdevice::pci:210] >>> axdevice pci write GPA:0xcf8 0x80000000... |
| 272 | + |
| 273 | +[ 0.318592 0:5 axdevice::pci:87] >>> set address 0x0 : device:0x0 : 0x0 : 0x0 |
| 274 | + |
| 275 | +[ 0.319020 0:5 axvm::vm:258] >>>>> exit_reason IoRead { |
| 276 | + port: 0xcfc, |
| 277 | + width: Word, |
| 278 | +} |
| 279 | +read QEMU_CFG_SIGNATURE 85(U) |
| 280 | +Found QEMU fw_cfg |
| 281 | +>>> qemu_cfg_read_entry start ... |
| 282 | +>>> cfg read qemu_cfg_read over |
| 283 | +>>> qemu_cfg_read_entry over ... |
| 284 | +QEMU fw_cfg: 956659(0xe98f3) 0x2 |
| 285 | +QEMU fw_cfg DMA interface supported |
| 286 | +>>> qemu_early_e820 call qemu_cfg_read_entry, port:0x19 |
| 287 | +>>> qemu_cfg_read_entry start ... |
| 288 | +>>> cfg read qemu_cfg_dma_transfer 0x6f80 4 |
| 289 | +>>> dma outl: 0x518 0x806f000000000000 |
| 290 | +[ 0.508528 0:5 axvm::vm:258] >>>>> exit_reason IoWrite { |
| 291 | + port: 0x518, |
| 292 | + width: Dword, |
| 293 | + data: 0x206f0000, |
| 294 | +} |
| 295 | + |
| 296 | +[ 0.509263 0:5 axvm::vm:289] IoWrite: 0x518 Dword 0x206f0000 |
| 297 | +[ 0.509671 0:5 axdevice::device:96] emu: GPA:0x510..GPA:0x522 handler write port:GPA:0x518 width:4 val:0x206f0000 |
| 298 | +[ 0.510356 0:5 axdevice::fwcfg:238] >>> do_write GPA:0x518 4 0x206f0000 |
| 299 | + |
| 300 | +[ 0.510837 0:5 axdevice::fwcfg:226] dma_write: GPA:0x518 0x206f0000 |
| 301 | + |
| 302 | +>>> dma outl over: 0x518 0x806f000000000000 |
| 303 | +QEMU: Terminated |
| 304 | +``` |
| 305 | +
|
| 306 | +--- |
| 307 | +
|
| 308 | +**参考文档:** |
| 309 | +
|
| 310 | +[SeaBIOS实现简单分析](https://www.cnblogs.com/gnuemacs/p/14287120.html) |
| 311 | +[浅度剖析 SeaBIOS 之 QEMU 初始化](https://zhuanlan.zhihu.com/p/678576761) |
| 312 | +<<Qemu/kvm源码解析与应用>> - 李强 |
0 commit comments