/** * Read a VGA rom using the 0xc0000 mapping. * * This function should be extended to handle access through PCI resources, * which should be more reliable when available. */ static int pci_device_freebsd_read_rom( struct pci_device * dev, void * buffer ) { struct pci_device_private *priv = (struct pci_device_private *) dev; void *bios; pciaddr_t rom_base; uint32_t rom; uint16_t reg; int pci_rom, memfd; if ( ( dev->device_class & 0x00ffff00 ) != ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) ) { return ENOSYS; } if (priv->rom_base == 0) { #if defined(__amd64__) || defined(__i386__) rom_base = 0xc0000; pci_rom = 0; #else return ENOSYS; #endif } else { rom_base = priv->rom_base; pci_rom = 1; pci_device_cfg_read_u16( dev, ®, PCIR_COMMAND ); pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND ); pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS ); pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS ); } printf("Using rom_base = 0x%lx\n", (long)rom_base); memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC ); if ( memfd == -1 ) return errno; bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base ); if ( bios == MAP_FAILED ) { close( memfd ); return errno; } memcpy( buffer, bios, dev->rom_size ); munmap( bios, dev->rom_size ); close( memfd ); if (pci_rom) { pci_device_cfg_write_u32( dev, PCIR_BIOS, rom ); pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg ); } return 0; }
static int pci_device_freebsd_probe( struct pci_device * dev ) { struct pci_device_private *priv = (struct pci_device_private *) dev; uint32_t reg, size; uint8_t irq; int err, i, bar; /* Many of the fields were filled in during initial device enumeration. * At this point, we need to fill in regions, rom_size, and irq. */ err = pci_device_cfg_read_u8( dev, &irq, 60 ); if (err) return errno; dev->irq = irq; bar = 0x10; for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) { pci_device_freebsd_get_region_info( dev, i, bar ); if (dev->regions[i].is_64) { bar += 0x08; i++; } else bar += 0x04; } /* If it's a VGA device, set up the rom size for read_rom */ if ((dev->device_class & 0x00ffff00) == ((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) { err = pci_device_cfg_read_u32( dev, ®, PCIR_BIOS ); if (err) return errno; if (reg == 0) { dev->rom_size = 0x10000; return 0; } err = pci_device_cfg_write_u32( dev, ~PCIM_BIOS_ENABLE, PCIR_BIOS ); if (err) return errno; pci_device_cfg_read_u32( dev, &size, PCIR_BIOS ); pci_device_cfg_write_u32( dev, reg, PCIR_BIOS ); if ((reg & PCIM_BIOS_ADDR_MASK) != 0) { priv->rom_base = (reg & PCIM_BIOS_ADDR_MASK); dev->rom_size = -(size & PCIM_BIOS_ADDR_MASK); } } return 0; }
int rumpcomp_pci_confwrite(unsigned bus, unsigned dev, unsigned fun, int reg, unsigned int v) { assert(bus == 0 && fun == 0); if (dev >= NUMDEVS) return 1; pci_userspace_init(); pci_device_cfg_write_u32(pci_devices[dev], v, reg); return 0; }
int pci_device_cfg_write_bits( struct pci_device * dev, uint32_t mask, uint32_t data, pciaddr_t offset ) { uint32_t temp; int err; err = pci_device_cfg_read_u32( dev, & temp, offset ); if ( ! err ) { temp &= ~mask; temp |= data; err = pci_device_cfg_write_u32(dev, temp, offset); } return err; }
/** * Sets the address and size information for the region from config space * registers. * * This would be much better provided by a kernel interface. * * \return 0 on success, or an errno value. */ static int pci_device_freebsd_get_region_info( struct pci_device * dev, int region, int bar ) { uint32_t addr, testval; uint16_t cmd; int err; /* Get the base address */ err = pci_device_cfg_read_u32( dev, &addr, bar ); if (err != 0) return err; /* * We are going to be doing evil things to the registers here * so disable them via the command register first. */ err = pci_device_cfg_read_u16( dev, &cmd, PCIR_COMMAND ); if (err != 0) return err; err = pci_device_cfg_write_u16( dev, cmd & ~(PCI_BAR_MEM(addr) ? PCIM_CMD_MEMEN : PCIM_CMD_PORTEN), PCIR_COMMAND ); if (err != 0) return err; /* Test write all ones to the register, then restore it. */ err = pci_device_cfg_write_u32( dev, 0xffffffff, bar ); if (err != 0) return err; err = pci_device_cfg_read_u32( dev, &testval, bar ); if (err != 0) return err; err = pci_device_cfg_write_u32( dev, addr, bar ); if (err != 0) return err; /* Restore the command register */ err = pci_device_cfg_write_u16( dev, cmd, PCIR_COMMAND ); if (err != 0) return err; if (addr & 0x01) dev->regions[region].is_IO = 1; if (addr & 0x04) dev->regions[region].is_64 = 1; if (addr & 0x08) dev->regions[region].is_prefetchable = 1; /* Set the size */ dev->regions[region].size = get_test_val_size( testval ); printf("size = 0x%lx\n", (long)dev->regions[region].size); /* Set the base address value */ if (dev->regions[region].is_64) { uint32_t top; err = pci_device_cfg_read_u32( dev, &top, bar + 4 ); if (err != 0) return err; dev->regions[region].base_addr = ((uint64_t)top << 32) | get_map_base(addr); } else { dev->regions[region].base_addr = get_map_base(addr); } return 0; }
/** * Read a device's expansion ROM using /dev/mem. * * \note * This function could probably be used, as-is, on other platforms that have * a /dev/mem interface. * * \bugs * Before using the VGA special case code, this function should check that * VGA access are routed to the device. Right? */ _pci_hidden int pci_device_linux_devmem_read_rom(struct pci_device *dev, void *buffer) { struct pci_device_private *priv = (struct pci_device_private *) dev; int fd; int err = 0; uint32_t rom_base_tmp; pciaddr_t rom_base; pciaddr_t rom_size; int PCI_ROM; /* Handle some special cases of legacy devices. */ if (priv->base.rom_size == 0) { /* VGA ROMs are supposed to be at 0xC0000. */ if ((priv->base.device_class & 0x00ffff00) == 0x000030000) { rom_base = 0x000C0000; rom_size = 0x00010000; PCI_ROM = 0; } else { /* "Function not implemented." */ return ENOSYS; } } else { rom_base = priv->rom_base; rom_size = priv->base.rom_size; PCI_ROM = 1; } /* Enable the device's ROM. */ if (PCI_ROM) { err = pci_device_cfg_read_u32(& priv->base, & rom_base_tmp, 48); if (err) { return err; } if ((rom_base_tmp & 0x000000001) == 0) { err = pci_device_cfg_write_u32(& priv->base, rom_base_tmp | 1, 48); if (err) { return err; } } } /* Read the portion of /dev/mem that corresponds to the device's ROM. */ fd = open("/dev/mem", O_RDONLY, 0); if (fd < 0) { err = errno; } else { size_t bytes; for (bytes = 0; bytes < rom_size; /* empty */) { const ssize_t got = pread(fd, buffer, rom_size - bytes, rom_base + bytes); if (got == -1) { err = errno; break; } bytes += got; } close(fd); } /* Disable the device's ROM. */ if (PCI_ROM && ((rom_base_tmp & 0x000000001) == 0)) { const int tmp_err = pci_device_cfg_write_u32(& priv->base, rom_base_tmp, 48); /* Prefer to return the first error that occurred. */ if (err == 0) { err = tmp_err; } } return err; }