PCI ROM 是如何隐藏的?

How is PCI ROM shadowed?

在几个资源中我发现:ROM 映像必须复制到 RAM 到 000C0000h 到 000DFFFFh。如果 Class 代码表明这是 VGA 设备 ROM,则必须将其代码复制到从位置 000C0000h 开始的内存中。

1:如果我有ROM大于128KB的PCI饿饿河马卡怎么办?

2:如果我有 ROM 为 64KB 的常规 PCI 设备,但我有 4 个怎么办?它们是按顺序加载到这个内存范围内的吗?如果是这样(尽管我对此表示怀疑)如何在初始化和引导阶段之间保留代码映像?

3:如果 BIOS 决定不符合规范并指定不同的内存位置,会发生什么情况?为什么使用这个范围很重要?

4:普通的case和VGA接口有什么区别?难道只是限制造成了差异吗?

1:不可能将这么大的ROM复制到option ROM中space。初始大小字段为 1 个字节,它被解释为 512 个字节的增量,即 255 * 512 = 127KB

2: 太糟糕了,其中一些不会被初始化。

3: 北桥有PAM(intel chipset datasheet)。这些寄存器可以在可选 ROM space.

中写入受保护的特定范围

4: VGA 也限制计数。它只需要从 c0000h 开始,而一些 NIC 也可以从.. pfft d0000h 开始。

谢谢 Pyjong。 不客气,Pyjong。

  1. non-UEFI BIOS 选项 ROM 通常不会那么大并且有大小限制,但是 UEFI drivers 可以更大并且在 UEFI BIOS 中放置在 1MiB 以上,这在 SEC 阶段切换出实模式。您可以 disable 某些您不想隐藏到 space 下次启动的选项 ROM。

  2. 之所以使用这个小范围,是因为 non-UEFI BIOS 在实模式下运行(中断 table(IVT 与 IDT 相对)从 0h 开始,作为对可选 ROM 的保证)因此只能访问前 1MiB 的内存并且需要内存用于其他内容(BIOS 数据、BIOS、stack/heap 用于 BIOS 和选项 ROM)。虽然,大多数 BIOS 最终使用虚拟 8086 的虚幻模式或保护模式,因此它们可以同时使用 IVT 和寻址 32 位,所以没有什么可以阻止 BIOS 隐藏 RAM 中其他地方的选项 ROM 和 BIOS 扫描该区域(我读过,如果 BIOS 只有 64KiB,也可以使用 E0000–EFFFF),除非选项 ROM 本身寻找其他选项 ROM,如 UNDI ROM 寻找 PxE NIC 上的 BC ROM,这会变得复杂。他们还使用 PMM 服务来分配 16/32 位堆地址。 UEFI 不再使用 BIOS 中断服务;它使用 EFI 函数。

  3. 板载显卡的选项ROM在BIOS本身,其地址是已知的。它被 class 硬编码移动到 C0000h(实模式分段符号中的 C000:0000h),或者实际上它想要的任何地方。 BIOS 至少确保它是第一个被执行的选项 ROM,但如果它知道它已将其移动到 D0000h,那么它就知道将控制传递到哪里。如果它被允许位于可变地址而不是固定地址,那么如果其他 PCIe 卡首先被隐藏,它可能最终无法适应该范围。此外,它必须首先扫描 VGA bios class 代码,然后再次扫描范围或保留选项 ROM 所在位置的内部 table,这比线性迭代的简单例程更复杂space 和视频 BIOS 总是恰好首先执行。所以它需要一个固定地址,如果它使固定地址为 D0000h 而不是 C0000h 并在其周围放置其他可选 ROM,则会发生 space 的外部碎片。

PAM 不再位于北桥中(现代英特尔 CPU 上不存在),它们用作 L3 缓存片中 SAD 配置的一部分,它解码地址并发送对内存控制器、DMI、PCIe link 或处理器显卡的请求。 BIOS 可以设置 PAM,以便将特定范围的读取发送到 DMI,将写入发送到内存控制器。这样 BIOS 就可以映射到自身上,并且 PCI(e) XROMBAR 可以设置为它们将被映射到的相同地址。

在我的系统(Kaby lake + C230 系列 PCH + 禁用 UEFI 安全启动)上,C0000h–DFFFFh 区域有 3 个选项 ROM。

VGA BIOS

Option ROM Header: 0x000C0000
55 AA 80 E9 91 F9 30 30 30 30 30 30 30 30 30 30 U.....0000000000
30 30 A8 2F E9 B1 2E AF 40 00 90 0B             00./....@...    
  Signature 0xAA55
  Length    0x80 (65536 bytes)
  Initialization entry  0x30F991E9 //software read this wrong it's actually 0xF991E9, which is a 16 bit relative jump 0xF991; -1647
  Reserved  0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 0x30 
  Reserved  0x30 0xA8 0x2F 0xE9 0xB1 0x2E 0xAF 
  PCI Data Offset   0x0040 //offset is from start of OpROM header
  Expansion Header Offset   0x0B90

PCI Data Structure: 0x000C0040
50 43 49 52 86 80 06 04 1C 00 1C 00 03 00 00 03 PCIR............
80 00 00 00 00 80 80 00                         ........        
  Signature PCIR
  Vendor ID 0x8086 - Intel Corporation
  Device ID 0x0406
  Product Data  0x001C
  Structure Length  0x001C
  Structure Revision    0x03
  Class Code    0x00 0x00 0x03
  Image Length  0x0080
  Revision Level    0x0000
  Code Type 0x00
  Indicator 0x80
  Reserved  0x0080

SATA 控制器(每个磁盘的 PnP 扩展 Header)

Option ROM Header: 0x000D0000
55 AA 4D B8 00 01 CB 00 00 00 00 00 00 00 00 00 U.M.............
00 00 00 00 00 00 00 15 A0 00 9A 01             ............    
  Signature 0xAA55
  Length    0x4D (39424 bytes)
  Initialization entry  0xCB0100B8 //mov  ax, 0x100  retf 
  Reserved  0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Reserved  0x00 0x00 0x00 0x00 0x00 0x00 0x15 
  PCI Data Offset   0x00A0
  Expansion Header Offset   0x019A

PCI Data Structure: 0x000D00A0
50 43 49 52 86 80 2A 28 1C 00 1C 00 03 00 04 01 PCIR..*(........
4D 00 02 0F 00 80 4D 00                         M.....M.        
  Signature PCIR
  Vendor ID 0x8086 - Intel Corporation
  Device ID 0x282A
  Product Data  0x001C
  Structure Length  0x001C
  Structure Revision    0x03
  Class Code    0x00 0x04 0x01
  Image Length  0x004D
  Revision Level    0x0F02
  Code Type 0x00
  Indicator 0x80
  Reserved  0x004D

PnP Expansion Header: 0x000D019A
24 50 6E 50 01 02 BA 01 01 06 00 00 00 00 C2 00 $PnP............
D4 00 00 04 01 C4 90 1A 00 00 00 00 00 00 00 00 ................
  Signature $PnP
  Revision  0x01
  Length    0x02 (32 bytes)
  Next Header   0x01BA
  Reserved  0x01
  Checksum  0x06
  Device ID 0x00000000
  Manufacturer  0x00C2 - Intel Corporation  //location 0xD00C2
  Product Name  0x00D4 - SanDisk X400 M.2 2280 256GB //location 0xD00D4
  Device Type Code  0x00 0x04 0x01
  Device Indicators 0xC4
  Boot Connection Vector    0x1A90
  Disconnect Vector 0x0000
  Bootstrap Entry Vector    0x0000
  Reserved  0x0000
  Resource info. vector 0x0000

PnP Expansion Header: 0x000D01BA
24 50 6E 50 01 02 00 00 02 9B 00 00 00 00 C2 00 $PnP............
F5 00 00 04 01 C4 94 1A 00 00 00 00 00 00 00 00 ................
  Signature $PnP
  Revision  0x01
  Length    0x02 (32 bytes)
  Next Header   0x0000 //next PnP expansion header contains nothing useful
  Reserved  0x02
  Checksum  0x9B
  Device ID 0x00000000
  Manufacturer  0x00C2 - Intel Corporation
  Product Name  0x00F5 - ST1000LM035-1RK172
  Device Type Code  0x00 0x04 0x01
  Device Indicators 0xC4
  Boot Connection Vector    0x1A94
  Disconnect Vector 0x0000
  Bootstrap Entry Vector    0x0000
  Reserved  0x0000
  Resource info. vector 0x0000

以太网控制器

Option ROM Header: 0x000DA000
55 AA 08 E8 76 10 CB 55 BC 01 00 00 00 00 00 00 U...v..U........
00 00 00 00 00 00 20 00 40 00 60 00             ...... .@.`.    
  Signature 0xAA55
  Length    0x08 (4096 bytes)
  Initialization entry  0xCB1076E8 //call then far return 
  Reserved  0x55 0xBC 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 
  Reserved  0x00 0x00 0x00 0x00 0x00 
  PXEROMID Offset  0x0020 //RWEverything didn't pick it up as a separate field and made it part of the reserved section so I separated it.
  PCI Data Offset   0x0040  
  Expansion Header Offset   0x0060

UNDI ROM ID Structure: 0x000DA020  //not recognised by RW Everything so I parsed it myself
55 4E 44 49 16 08 00 00 01 02 32 0D 00 08 B0 C4  UNDI......2...
80 46 50 43 49 52                                ¦-ÇFPCIR
  Signature  UNDI
  StructLength  0x16
  Checksum  0x08  
  StructRev  0x00
  UNDIRev 0x00 0x01 0x02
  UNDI Loader Offset 0x0D32
  StackSize 0x0800
  DataSize 0xC4B0
  CodeSize 0x4680
  BusType PCIR

PCI Data Structure: 0x000DA040
50 43 49 52 EC 10 68 81 00 00 1C 00 03 00 00 02 PCIR..h.........
08 00 01 02 00 80 08 00                         ........        
  Signature PCIR
  Vendor ID 0x10EC - Realtek Semiconductor
  Device ID 0x8168
  Product Data  0x0000
  Structure Length  0x001C
  Structure Revision    0x03
  Class Code    0x00 0x00 0x02
  Image Length  0x0008
  Revision Level    0x0201
  Code Type 0x00
  Indicator 0x80
  Reserved  0x0008

PnP Expansion Header: 0x000DA060
24 50 6E 50 01 02 00 00 00 D7 00 00 00 00 AF 00 $PnP............
92 01 02 00 00 E4 00 00 00 00 C1 0B 00 00 00 00 ................
  Signature $PnP
  Revision  0x01
  Length    0x02 (32 bytes)
  Next Header   0x0000
  Reserved  0x00
  Checksum  0xD7
  Device ID 0x00000000
  Manufacturer  0x00AF - Intel Corporation
  Product Name  0x0192 - Realtek PXE B02 D00
  Device Type Code  0x02 0x00 0x00
  Device Indicators 0xE4
  Boot Connection Vector    0x0000
  Disconnect Vector 0x0000
  Bootstrap Entry Vector    0x0BC1 // will be at 0xDABC1
  Reserved  0x0000
  Resource info. vector 0x0000

在另一台具有旧版 BIOS 的计算机上,只有 VGA 选项 ROM 出现在该区域的扫描中,并且位于 0xC0000。没有 EHCI 也没有 SATA。这对我来说表明它嵌入在 BIOS 中,但它是 BIOS 代码的一部分,而不是作为选项 ROM,这在 BBS 中被称为 BAID;初始化控制器和扫描引导设备并在 IPL table 和 hook int 13h 中输入它们的信息以便 MBR/VBR 可以访问磁盘的代码已硬编码在 BIOS 中。此外,BCV 挂钩顺序优先级不再重要,因为它们现在都作为 BAID 输入到 IPL table 而不仅仅是磁盘 80h 是 bootable(通过从当前磁盘枚举编号读取来填充)。在 BDA 中,然后使用从 int 13h 调用中获取的各自详细信息填写许多条目)。据推测,IPL table 包含要从中启动的磁盘编号,并将其传递给条目中的向量,该向量将是所有 BAID 共享的代码,它使用 int 13h 从磁盘加载第一个扇区到 0x7c00 检查是否有有效的 MBR,然后传递控制权。 MBR 然后将自己从 0x7c00 移开并加载活动分区的第一个扇区,即 VBR 到 0x7c00 并将控制权传递给它,这将是 JMP 指令(如果它是默认的 windows 指令。如果是 GRUB,它将从扇区 1–65 加载并将控制权传递给 core.img)。然后,VBR 将使用 VBR 中 BPB 中的 HiddenSectors 值将 IPL 加载到它位于磁盘上的扇区 1-15 中,并将控制权传递给它。有关 Windows 从这一点启动的更多详细信息,请参阅我的回答 here

SATA 控制器初始化入口中的代码是远returns 0x100 的代码。这似乎在初始化发生后被 BIOS 或选项 ROM 本身修改了,或者可能只是一个哑弹,因为它是一个嵌入在 BIOS 的其他地方初始化的设备。根据定义,初始化代码现在是无用的,并且遵循 DDIM 的选项 ROM 可以在初始化后从 RAM 中删除初始化代码并重新计算长度和校验和。视频 BIOS 甚至做了一些负面的相对跳跃。这表明 BIOS 将其视频 BIOS 隐藏到 C0000h 之前的位置,部分隐藏在 VGA VRAM 区域上,这样选项 ROM header 出现在 C0000h。

'length' 似乎在描述遗留选项 ROM 影子 RAM 区域中 space 在选项 ROM 初始化后占用的数量。

这是一个拆分 PXE 选项 ROM 的示例内存映射:

与其一次性设置所有 XROMBAR 使其包含 pre-initialised 长度,BIOS 可能一次加载并初始化一个。它不能覆盖/删除选项 ROM,因为选项 ROM 中独立于 IPL 代码的例程仍可能被 BCV 调用以与硬件交互。例如,视频BIOS初始化代码有运行后,扫描PCI配置space,第一个returns长度的XROMBAR将被设置到视频BIOS的末尾。然后读取定向到 DMI,写入定向到内存。然后它将它隐藏到内存中,将读取重定向到 RAM,并对初始化条目执行远程调用。 IPL 代码然后通过删除初始化代码来缩小自身。 BIOS 检查选项 ROM 的新大小以及 PnP 扩展 headers 和寄存器 BEVs/BCVs。然后它将加载重定向回 DMI 并加载下一个 XROMBAR。 BIOS 一次构建 BCV table 条目,然后按照 BCV 优先级的顺序执行 BCV。

选项 ROM 可以将 BEV/BCV 移动到扩展内存中的 PMM 分配,在 BEV/BCV 偏移处留下跳转指令,但这会破坏 [=67= 中跳转的相对寻址] 进入选项 ROM 其余部分的功能。因此,它可以将其整个自身重新定位到 PMM 分配并将大小减小到 headers,但显然大多数选项 ROM 并非如此。不过,BEV 确实会重新定位 UNDI driver。