Linux kernel point of view
A good way to learn something is to interact with it, so let's use the Linux kernel for this.
Here is a minimal example of PCI on a QEMU emulated device: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/366b1c1af269f56d6a7e6464f2862ba2bc368062/kernel_module/pci.c
The first 64 bytes of the PCI configuration are standardized as:

Image from LDD3.
So we see that there are 6 bars. The wiki page displays the contents of each BAR:

However, the width of the area requires magical recording: How is the size of the PCI / PCIe BAR determined?
This memory is configured by the PCI device and provides information to the kernel.
Each BAR corresponds to a range of addresses that serves as a separate communication channel with a PCI device.
The length of each region is determined by the hardware and transmitted to the software through the configuration registers.
Each region also has additional hardware-specific properties, in addition to length, in particular, the type of memory:
IORESOURCE_IO : must be accessible using inX and outXIORESOURCE_MEM : Must be accessible using ioreadX and iowriteX
Some Linux kernel PCI PCI functions use the BAR as a parameter to determine which communication channel should be used, for example:
mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR)); pci_resource_flags(dev, BAR); pci_resource_start(pdev, BAR); pci_resource_end(pdev, BAR);
Studying the source code of the QEMU device, we see that QEMU devices register these regions with:
memory_region_init_io(&edu->mmio, OBJECT(edu), &edu_mmio_ops, edu, "edu-mmio", 1 << 20); pci_register_bar(pdev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &edu->mmio);
and it is clear that the BAR properties are determined by hardware, for example, BAR number 0 , has a memory of type PCI_BASE_ADDRESS_SPACE_MEMORY , and the memory area has a length of 1 MB 1 << 20 .
See also: http://wiki.osdev.org/PCI#Base_Address_Registers of course.