Handling PCI BAR assignment in BIOS
Posted: Wed Nov 16, 2022 3:28 pm
I'm trying to write a simple x86 BIOS and I'm not able to initialize the PCI bus. Specifically, I'm trying to figure out how my BIOS would actually assign an address to BAR0 of a PCI device.
After following the various tutorials on OSDev and elsewhere, I am able to successfully enumerate the PCI bus, but I cannot seem to actually perform any CONFIG updates.
I have forked qemu and created my own custom PCI device called mydummypcidevice. This device has one BAR (BAR0) in memory address space, and does nothing other than log messages when read from or written to. Its vendor ID is 0x10E8 and its device ID is 0x7777.
When starting qemu with the default BIOS, BAR0 for the device is successfully registered:
Additionally, if I write a simple kernel which reads from the hard-coded BAR value (0xfebf1000), I get the expected log message
I run qemu with my custom BIOS using this command:
Reading the memory at 0x0000EEC6 yields 0x10e8 0x7777, which matches my device's vendor ID and device ID, as expected. However, the BAR is never updated, and remains unchanged from the initial:
After setting CONFIG_ADDRESS, I'm trying to set BAR0 to 0x0001ABCD with this code (see source for full context):
I'm running on MSYS2 MINGW64 on Windows 10.
Any help would be greatly appreciated!
Project files for my BIOS (adapted from https://pete.akeo.ie/2011/06/crafting-b ... ratch.html):
pci_bar_setup.s
pci_bar_setup.ld
Makefile
After following the various tutorials on OSDev and elsewhere, I am able to successfully enumerate the PCI bus, but I cannot seem to actually perform any CONFIG updates.
I have forked qemu and created my own custom PCI device called mydummypcidevice. This device has one BAR (BAR0) in memory address space, and does nothing other than log messages when read from or written to. Its vendor ID is 0x10E8 and its device ID is 0x7777.
When starting qemu with the default BIOS, BAR0 for the device is successfully registered:
Code: Select all
./qemu-system-i386.exe -monitor stdio -device mydummypcidevice
info pci
...
Bus 0, device 4, function 0:
Class 2432: PCI device 10e8:7777
PCI subsystem 1af4:1100
BAR0: 32 bit memory at 0xfebf1000 [0xfebf100f].
id ""
So it seems that the device implementation is working as expected.DUMMYPCIDEVICE :: mem_readfn :: memory read!
I run qemu with my custom BIOS using this command:
Code: Select all
./qemu-system-i386.exe -monitor stdio -device mydummypcidevice -bios out/pci_bar_setup.rom -nographic
Code: Select all
Bus 0, device 4, function 0:
Class 2432: PCI device 10e8:7777
PCI subsystem 1af4:1100
BAR0: 32 bit memory at 0xffffffffffffffff [0x0000000e].
id ""
Code: Select all
mov dx, 0xCFC
/* we want to assign 0x0001ABCD to BAR0 */
mov eax, 0x0001ABCD
out dx, eax
Any help would be greatly appreciated!
Project files for my BIOS (adapted from https://pete.akeo.ie/2011/06/crafting-b ... ratch.html):
pci_bar_setup.s
Code: Select all
/********************************************************************************/
/* VMware BIOS ROM example */
/* Copyright (c) 2011 Pete Batard ([email protected]) - Public Domain */
/********************************************************************************/
/********************************************************************************/
/* GNU Assembler Settings: */
/********************************************************************************/
.intel_syntax noprefix
.code16
/********************************************************************************/
/********************************************************************************/
/* Constants: */
/********************************************************************************/
PCI_ENUMERATOR_OFFSET = 0 /* also tried with 0x4 and 0x10, but BAR0 is still not updated */
PCI_ENUMERATOR_BUS = 0
PCI_ENUMERATOR_DEVICE = 4
PCI_ENUMERATOR_FUNC = 0
TMP_ADDR_1 = 0x0000EEAA
ENUMERATED_PCI_ADDR = 0x0000EEC6
SAVE_ADDR_1 = 0x0000EFCA
/********************************************************************************/
/********************************************************************************/
/* begin : Dummy section marking the very start of the BIOS. */
/* This allows the .rom binary to be filled to the right size with objcopy. */
/********************************************************************************/
.section begin, "a" /* The 'ALLOC' flag is needed for objcopy */
.ascii "PCIBARSETUP v1.00" /* Dummy ID string */
.align 16
/********************************************************************************/
/********************************************************************************/
/* main: */
/* This section will be relocated according to the pci_bar_setup.ld script. */
/********************************************************************************/
.section main, "ax"
.globl _pci_bar_setup
_pci_bar_setup:
cli
mov ds, ax
mov ss, ax
/* write a value here so that we can verify that this point was reached */
movw [SAVE_ADDR_1], 0x9999
/* begin dummy enumeration */
mov eax, PCI_ENUMERATOR_BUS
shl eax, 16
mov edx, PCI_ENUMERATOR_DEVICE
shl edx, 11
or eax, edx
mov edx, PCI_ENUMERATOR_FUNC
shl edx, 8
or eax, edx
mov edx, TMP_ADDR_1
mov edx, [edx]
add edx, PCI_ENUMERATOR_OFFSET
and edx, 0xFC
or eax, edx
or eax, 0x80000000
mov dx, 0xCF8
out dx, eax
mov dx, 0xCFC
in eax, dx
mov DWORD PTR [ENUMERATED_PCI_ADDR], eax
/* end of dummy enumeration */
/* begin dummy write */
mov eax, PCI_ENUMERATOR_BUS
shl eax, 16
mov edx, PCI_ENUMERATOR_DEVICE
shl edx, 11
or eax, edx
mov edx, PCI_ENUMERATOR_FUNC
shl edx, 8
or eax, edx
mov edx, TMP_ADDR_1
mov edx, [edx]
add edx, PCI_ENUMERATOR_OFFSET
and edx, 0xFC
or eax, edx
or eax, 0x80000000
mov dx, 0xCF8
out dx, eax
mov dx, 0xCFC
in eax, dx
/* we want to assign 0x0001ABCD to BAR0 */
mov eax, 0x0001ABCD
out dx, eax
/* end of dummy write */
/* write a value here so that we can verify that this point was reached */
movw [SAVE_ADDR_1], 0x8888
hlt
/********************************************************************************/
/********************************************************************************/
/* reset: this section must reside at 0xfffffff0, and be exactly 16 bytes */
/********************************************************************************/
.section reset, "ax"
/* Issue a manual jmp to work around a binutils bug. */
/* See coreboot's src/cpu/x86/16bit/reset16.inc */
.byte 0xe9
.int _pci_bar_setup - ( . + 2 )
.align 16, 0xff /* fills section to end of ROM (with 0xFF) */
/********************************************************************************/
Code: Select all
OUTPUT_ARCH(i386) /* i386 for 32 bit, i8086 for 16 bit */
/* Set the variable below to the address you want the "main" section, from bios.S, */
/* to be located. The BIOS should be located at the area just below 4GB (4096 MB). */
main_address = 4096M - 4K; /* Use the last 4K block */
/* Set the BIOS size below (both locations) according to your target flash size */
MEMORY {
ROM (rx) : org = 4096M - 512K, len = 512K
}
/* You shouldn't have to modify anything below this */
SECTIONS {
ENTRY(_pci_bar_setup) /* To avoid antivirus false positives */
/* Sanity check on the _pci_bar_setup entrypoint */
_assert = ASSERT(_pci_bar_setup >= 4096M - 64K,
"'_pci_bar_setup' entrypoint too low - it needs to reside in the last 64K.");
.begin : { /* NB: ld section labels MUST be 6 letters or less */
*(begin)
} >ROM /* Places this first section at the beginning of the ROM */
/* the --gap-fill option of objcopy will be used to fill the gap to .main */
.main main_address : {
*(main)
}
.reset 4096M - 0x10 : { /* First instruction executed after reset */
*(reset)
}
.igot 0 : { /* Required on Linux */
*(.igot.plt)
}
}
Code: Select all
.PHONY: default
default: generate_romfile ;
prepare :
mkdir out -p
clean :
rm -rf out
compile : clean prepare
gcc -c -o out/pci_bar_setup.o -m32 -mtune=generic -march=i386 -nostartfiles -no-pie -fno-pie -z -g -ggdb -static -m32 pci_bar_setup.s
to_binary : compile
objcopy -O binary -j .main --set-section-flags .main=alloc,load,readonly,code out/pci_bar_setup.o out/main.bin
link : to_binary
ld -A elf32-i386 -Tpci_bar_setup.ld -o out/pci_bar_setup.out out/pci_bar_setup.o -Map out/xMemLayout.map
generate_romfile : link
objcopy -Felf32-i386 -O binary -j .begin -j .main -j .reset --gap-fill=0x0ff out/pci_bar_setup.out out/pci_bar_setup.rom