Commit message: dm: validate the input in 'pci_emul_mem_handler()'

 checking the inputs explicitly instead of using Assert.

Tracked-On: #4003
Signed-off-by: Yonghua Huang <yonghua.huang@intel.com>
Reviewed-by: Shuo Liu <shuo.a.liu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
CVE: CVE-2019-18844
CWE: CWE-703
Vulnerability description: The Device Model in ACRN before 2019w25.5-140000p relies on assert calls in devicemodel/hw/pci/core.c and devicemodel/include/pci_core.h (instead of other mechanisms for propagating error information or diagnostic information), which might allow attackers to cause a denial of service (assertion failure) within pci core. This is fixed in 1.2. 6199e653418e is a mitigation for pre-1.1 versions, whereas 2b3dedfb9ba1 is a mitigation for 1.1.

Project: acrn-hypervisor
File: core.c

init_pci(struct vmctx *ctx)
{
	struct mem_range mr;
	struct pci_vdev_ops *ops;
	struct businfo *bi;
	struct slotinfo *si;
	struct funcinfo *fi;
	size_t lowmem;
	int bus, slot, func;
	int success_cnt = 0;
	int error;

	pci_emul_iobase = PCI_EMUL_IOBASE;
	pci_emul_membase32 = vm_get_lowmem_limit(ctx);
	pci_emul_membase64 = PCI_EMUL_MEMBASE64;

	create_gsi_sharing_groups();

	for (bus = 0; bus < MAXBUSES; bus++) {
		bi = pci_businfo[bus];
		if (bi == NULL)
			continue;
		/*
		 * Keep track of the i/o and memory resources allocated to
		 * this bus.
		 */
		bi->iobase = pci_emul_iobase;
		bi->membase32 = pci_emul_membase32;
		bi->membase64 = pci_emul_membase64;

		for (slot = 0; slot < MAXSLOTS; slot++) {
			si = &bi->slotinfo[slot];
			for (func = 0; func < MAXFUNCS; func++) {
				fi = &si->si_funcs[func];
				if (fi->fi_name == NULL)
					continue;
				ops = pci_emul_finddev(fi->fi_name);
				assert(ops != NULL);
				error = pci_emul_init(ctx, ops, bus, slot,
				    func, fi);
				if (error)
					goto pci_emul_init_fail;
				success_cnt++;
			}
		}

		/*
		 * Add some slop to the I/O and memory resources decoded by
		 * this bus to give a guest some flexibility if it wants to
		 * reprogram the BARs.
		 */
		pci_emul_iobase += BUSIO_ROUNDUP;
		pci_emul_iobase = roundup2(pci_emul_iobase, BUSIO_ROUNDUP);
		bi->iolimit = pci_emul_iobase;

		pci_emul_membase32 += BUSMEM_ROUNDUP;
		pci_emul_membase32 = roundup2(pci_emul_membase32,
		    BUSMEM_ROUNDUP);
		bi->memlimit32 = pci_emul_membase32;

		pci_emul_membase64 += BUSMEM_ROUNDUP;
		pci_emul_membase64 = roundup2(pci_emul_membase64,
		    BUSMEM_ROUNDUP);
		bi->memlimit64 = pci_emul_membase64;
	}

	error = check_gsi_sharing_violation();
	if (error < 0)
		goto pci_emul_init_fail;

	/*
	 * PCI backends are initialized before routing INTx interrupts
	 * so that LPC devices are able to reserve ISA IRQs before
	 * routing PIRQ pins.
	 */
	for (bus = 0; bus < MAXBUSES; bus++) {
		bi = pci_businfo[bus];
		if (bi == NULL)
			continue;

		for (slot = 0; slot < MAXSLOTS; slot++) {
			si = &bi->slotinfo[slot];
			for (func = 0; func < MAXFUNCS; func++) {
				fi = &si->si_funcs[func];
				if (fi->fi_devi == NULL)
					continue;
				pci_lintr_route(fi->fi_devi);
				ops = fi->fi_devi->dev_ops;
				if (ops && ops->vdev_phys_access)
					ops->vdev_phys_access(ctx,
						fi->fi_devi);
			}
		}
	}
	lpc_pirq_routed();

	/*
	 * The guest physical memory map looks like the following:
	 * [0,              lowmem)         guest system memory
	 * [lowmem,         lowmem_limit)   memory hole (may be absent)
	 * [lowmem_limit,   0xE0000000)     PCI hole (32-bit BAR allocation)
	 * [0xE0000000,     0xF0000000)     PCI extended config window
	 * [0xF0000000,     4GB)            LAPIC, IOAPIC, HPET, firmware
	 * [4GB,            5GB)            PCI hole (64-bit BAR allocation)
	 * [5GB,            5GB + highmem)  guest system memory
	 */

	/*
	 * Accesses to memory addresses that are not allocated to system
	 * memory or PCI devices return 0xff's.
	 */
	lowmem = vm_get_lowmem_size(ctx);
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI hole (32-bit)";
	mr.flags = MEM_F_RW;
	mr.base = lowmem;
	mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
	mr.handler = pci_emul_fallback_handler;
	error = register_mem_fallback(&mr);
	assert(error == 0);

	/* ditto for the 64-bit PCI host aperture */
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI hole (64-bit)";
	mr.flags = MEM_F_RW;
	mr.base = PCI_EMUL_MEMBASE64;
	mr.size = PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64;
	mr.handler = pci_emul_fallback_handler;
	error = register_mem_fallback(&mr);
	assert(error == 0);

	/* PCI extended config space */
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI ECFG";
	mr.flags = MEM_F_RW;
	mr.base = PCI_EMUL_ECFG_BASE;
	mr.size = PCI_EMUL_ECFG_SIZE;
	mr.handler = pci_emul_ecfg_handler;
	error = register_mem(&mr);
	assert(error == 0);

	return 0;

pci_emul_init_fail:
	for (bus = 0; bus < MAXBUSES && success_cnt > 0; bus++) {
		bi = pci_businfo[bus];
		if (bi == NULL)
			continue;
		for (slot = 0; slot < MAXSLOTS && success_cnt > 0; slot++) {
			si = &bi->slotinfo[slot];
			for (func = 0; func < MAXFUNCS; func++) {
				fi = &si->si_funcs[func];
				if (fi->fi_name == NULL)
					continue;
				if (success_cnt-- <= 0)
					break;
				ops = pci_emul_finddev(fi->fi_name);
				assert(ops != NULL);
				pci_emul_deinit(ctx, ops, bus, slot,
				    func, fi);
			}
		}
	}

	return error;
}

Project: acrn-hypervisor
File: core.c

pci_emul_mem_handler(struct vmctx *ctx, int vcpu, int dir, uint64_t addr,
		     int size, uint64_t *val, void *arg1, long arg2)
{
	struct pci_vdev *pdi = arg1;
	struct pci_vdev_ops *ops = pdi->dev_ops;
	uint64_t offset;
	int bidx = (int) arg2;

	if (addr + size > pdi->bar[bidx].addr + pdi->bar[bidx].size) {
		printf("%s, Out of emulated memory range.\n", __func__);
		return -ESRCH;
	}

	offset = addr - pdi->bar[bidx].addr;

	if (dir == MEM_F_WRITE) {
		if (size == 8) {
			(*ops->vdev_barwrite)(ctx, vcpu, pdi, bidx, offset,
					   4, *val & 0xffffffff);
			(*ops->vdev_barwrite)(ctx, vcpu, pdi, bidx, offset + 4,
					   4, *val >> 32);
		} else {
			(*ops->vdev_barwrite)(ctx, vcpu, pdi, bidx, offset,
					   size, bar_value(size, *val));
		}
	} else {
		if (size == 8) {
			uint64_t val_lo, val_hi;

			val_lo = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
			                              offset, 4);
			val_lo = bar_value(4, val_lo);

			val_hi = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
			                              offset + 4, 4);

			*val = val_lo | (val_hi << 32);
		} else {
			*val = (*ops->vdev_barread)(ctx, vcpu, pdi, bidx,
			                            offset, size);
			*val = bar_value(size, *val);
		}
	}

	return 0;
}

Project: acrn-hypervisor
File: core.c

pci_emul_free_bars(struct pci_vdev *pdi)
{
	int i, enabled;

	for (i = 0; i < PCI_BARMAX; i++) {
		if ((pdi->bar[i].type != PCIBAR_NONE) &&
			(pdi->bar[i].type != PCIBAR_MEMHI64)){
			/*
			 * Check whether the bar is enabled or not,
			 * if it is disabled then it should have been
			 * unregistered in pci_emul_cmdsts_write.
			 */
			if (pdi->bar[i].type == PCIBAR_IO)
				enabled = porten(pdi);
			else
				enabled = memen(pdi);

			if (enabled)
				unregister_bar(pdi, i);
			pdi->bar[i].type = PCIBAR_NONE;
		}
	}
}

Project: acrn-hypervisor
File: core.c

deinit_pci(struct vmctx *ctx)
{
	struct pci_vdev_ops *ops;
	struct businfo *bi;
	struct slotinfo *si;
	struct funcinfo *fi;
	int bus, slot, func;
	size_t lowmem;
	struct mem_range mr;

	/* Release PCI extended config space */
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI ECFG";
	mr.base = PCI_EMUL_ECFG_BASE;
	mr.size = PCI_EMUL_ECFG_SIZE;
	unregister_mem(&mr);

	/* Release PCI hole space */
	lowmem = vm_get_lowmem_size(ctx);
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI hole (32-bit)";
	mr.base = lowmem;
	mr.size = (4ULL * 1024 * 1024 * 1024) - lowmem;
	unregister_mem_fallback(&mr);

	/* ditto for the 64-bit PCI host aperture */
	bzero(&mr, sizeof(struct mem_range));
	mr.name = "PCI hole (64-bit)";
	mr.base = PCI_EMUL_MEMBASE64;
	mr.size = PCI_EMUL_MEMLIMIT64 - PCI_EMUL_MEMBASE64;
	unregister_mem_fallback(&mr);

	for (bus = 0; bus < MAXBUSES; bus++) {
		bi = pci_businfo[bus];
		if (bi == NULL)
			continue;

		for (slot = 0; slot < MAXSLOTS; slot++) {
			si = &bi->slotinfo[slot];
			for (func = 0; func < MAXFUNCS; func++) {
				fi = &si->si_funcs[func];
				if (fi->fi_name == NULL)
					continue;
				ops = pci_emul_finddev(fi->fi_name);
				assert(ops != NULL);
				pci_emul_deinit(ctx, ops, bus, slot,
				    func, fi);
			}
		}
	}
}

Project: acrn-hypervisor
File: core.c

porten(struct pci_vdev *dev)
{
	uint16_t cmd;

	cmd = pci_get_cfgdata16(dev, PCIR_COMMAND);

	return (cmd & PCIM_CMD_PORTEN);
}

Project: acrn-hypervisor
File: core.c

pci_bus_write_dsdt(int bus)
{
	struct businfo *bi;
	struct slotinfo *si;
	struct pci_vdev *dev;
	int count, func, slot;

	/*
	 * If there are no devices on this 'bus' then just return.
	 */
	 bi = pci_businfo[bus];
	if (bi == NULL) {
		/*
		 * Bus 0 is special because it decodes the I/O ports used
		 * for PCI config space access even if there are no devices
		 * on it.
		 */
		if (bus != 0)
			return;
	}

	dsdt_line("  Device (PCI%01X)", bus);
	dsdt_line("  {");
	dsdt_line("    Name (_HID, EisaId (\"PNP0A03\"))");
	dsdt_line("    Name (_ADR, Zero)");

	dsdt_line("    Method (_BBN, 0, NotSerialized)");
	dsdt_line("    {");
	dsdt_line("        Return (0x%08X)", bus);
	dsdt_line("    }");
	dsdt_line("    Name (_CRS, ResourceTemplate ()");
	dsdt_line("    {");
	dsdt_line("      WordBusNumber (ResourceProducer, MinFixed, "
	    "MaxFixed, PosDecode,");
	dsdt_line("        0x0000,             // Granularity");
	dsdt_line("        0x%04X,             // Range Minimum", bus);
	dsdt_line("        0x%04X,             // Range Maximum", bus);
	dsdt_line("        0x0000,             // Translation Offset");
	dsdt_line("        0x0001,             // Length");
	dsdt_line("        ,, )");

	if (bus == 0) {
		dsdt_indent(3);
		dsdt_fixed_ioport(0xCF8, 8);
		dsdt_unindent(3);

		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
		    "PosDecode, EntireRange,");
		dsdt_line("        0x0000,             // Granularity");
		dsdt_line("        0x0000,             // Range Minimum");
		dsdt_line("        0x0CF7,             // Range Maximum");
		dsdt_line("        0x0000,             // Translation Offset");
		dsdt_line("        0x0CF8,             // Length");
		dsdt_line("        ,, , TypeStatic)");

		dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
		    "PosDecode, EntireRange,");
		dsdt_line("        0x0000,             // Granularity");
		dsdt_line("        0x0D00,             // Range Minimum");
		dsdt_line("        0x%04X,             // Range Maximum",
		    PCI_EMUL_IOBASE - 1);
		dsdt_line("        0x0000,             // Translation Offset");
		dsdt_line("        0x%04X,             // Length",
		    PCI_EMUL_IOBASE - 0x0D00);
		dsdt_line("        ,, , TypeStatic)");

		if (bi == NULL) {
			dsdt_line("    })");
			goto done;
		}
	}
	assert(bi != NULL);

	/* i/o window */
	dsdt_line("      WordIO (ResourceProducer, MinFixed, MaxFixed, "
	    "PosDecode, EntireRange,");
	dsdt_line("        0x0000,             // Granularity");
	dsdt_line("        0x%04X,             // Range Minimum", bi->iobase);
	dsdt_line("        0x%04X,             // Range Maximum",
	    bi->iolimit - 1);
	dsdt_line("        0x0000,             // Translation Offset");
	dsdt_line("        0x%04X,             // Length",
	    bi->iolimit - bi->iobase);
	dsdt_line("        ,, , TypeStatic)");

	/* mmio window (32-bit) */
	dsdt_line("      DWordMemory (ResourceProducer, PosDecode, "
	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
	dsdt_line("        0x00000000,         // Granularity");
	dsdt_line("        0x%08X,         // Range Minimum\n", bi->membase32);
	dsdt_line("        0x%08X,         // Range Maximum\n",
	    bi->memlimit32 - 1);
	dsdt_line("        0x00000000,         // Translation Offset");
	dsdt_line("        0x%08X,         // Length\n",
	    bi->memlimit32 - bi->membase32);
	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");

	/* mmio window (64-bit) */
	dsdt_line("      QWordMemory (ResourceProducer, PosDecode, "
	    "MinFixed, MaxFixed, NonCacheable, ReadWrite,");
	dsdt_line("        0x0000000000000000, // Granularity");
	dsdt_line("        0x%016lX, // Range Minimum\n", bi->membase64);
	dsdt_line("        0x%016lX, // Range Maximum\n",
	    bi->memlimit64 - 1);
	dsdt_line("        0x0000000000000000, // Translation Offset");
	dsdt_line("        0x%016lX, // Length\n",
	    bi->memlimit64 - bi->membase64);
	dsdt_line("        ,, , AddressRangeMemory, TypeStatic)");
	dsdt_line("    })");

	count = pci_count_lintr(bus);
	if (count != 0) {
		dsdt_indent(2);
		dsdt_line("Name (PPRT, Package ()");
		dsdt_line("{");
		pci_walk_lintr(bus, pci_pirq_prt_entry, NULL);
		dsdt_line("})");
		dsdt_line("Name (APRT, Package ()");
		dsdt_line("{");
		pci_walk_lintr(bus, pci_apic_prt_entry, NULL);
		dsdt_line("})");
		dsdt_line("Method (_PRT, 0, NotSerialized)");
		dsdt_line("{");
		dsdt_line("  If (PICM)");
		dsdt_line("  {");
		dsdt_line("    Return (APRT)");
		dsdt_line("  }");
		dsdt_line("  Else");
		dsdt_line("  {");
		dsdt_line("    Return (PPRT)");
		dsdt_line("  }");
		dsdt_line("}");
		dsdt_unindent(2);
	}

	dsdt_indent(2);
	for (slot = 0; slot < MAXSLOTS; slot++) {
		si = &bi->slotinfo[slot];
		for (func = 0; func < MAXFUNCS; func++) {
			dev = si->si_funcs[func].fi_devi;
			if (dev != NULL &&
			    dev->dev_ops->vdev_write_dsdt != NULL)
				dev->dev_ops->vdev_write_dsdt(dev);
		}
	}
	dsdt_unindent(2);
done:
	dsdt_line("  }");
}

Project: acrn-hypervisor
File: core.c

memen(struct pci_vdev *dev)
{
	uint16_t cmd;

	cmd = pci_get_cfgdata16(dev, PCIR_COMMAND);

	return (cmd & PCIM_CMD_MEMEN);
}

Project: acrn-hypervisor
File: core.c

update_bar_address(struct  pci_vdev *dev, uint64_t addr, int idx, int type)
{
	int decode;

	if (dev->bar[idx].type == PCIBAR_IO)
		decode = porten(dev);
	else
		decode = memen(dev);

	if (decode)
		unregister_bar(dev, idx);

	switch (type) {
	case PCIBAR_IO:
	case PCIBAR_MEM32:
		dev->bar[idx].addr = addr;
		break;
	case PCIBAR_MEM64:
		dev->bar[idx].addr &= ~0xffffffffUL;
		dev->bar[idx].addr |= addr;
		break;
	case PCIBAR_MEMHI64:
		dev->bar[idx].addr &= 0xffffffff;
		dev->bar[idx].addr |= addr;
		break;
	default:
		assert(0);
	}

	if (decode)
		register_bar(dev, idx);
}

Project: acrn-hypervisor
File: core.c

modify_bar_registration(struct pci_vdev *dev, int idx, int registration)
{
	int error;
	struct inout_port iop;
	struct mem_range mr;

	switch (dev->bar[idx].type) {
	case PCIBAR_IO:
		bzero(&iop, sizeof(struct inout_port));
		iop.name = dev->name;
		iop.port = dev->bar[idx].addr;
		iop.size = dev->bar[idx].size;
		if (registration) {
			iop.flags = IOPORT_F_INOUT;
			iop.handler = pci_emul_io_handler;
			iop.arg = dev;
			error = register_inout(&iop);
		} else
			error = unregister_inout(&iop);
		break;
	case PCIBAR_MEM32:
	case PCIBAR_MEM64:
		bzero(&mr, sizeof(struct mem_range));
		mr.name = dev->name;
		mr.base = dev->bar[idx].addr;
		mr.size = dev->bar[idx].size;
		if (registration) {
			mr.flags = MEM_F_RW;
			mr.handler = pci_emul_mem_handler;
			mr.arg1 = dev;
			mr.arg2 = idx;
			error = register_mem(&mr);
		} else
			error = unregister_mem(&mr);
		break;
	default:
		error = EINVAL;
		break;
	}
	assert(error == 0);
}

Project: acrn-hypervisor
File: core.c

pci_cfgrw(struct vmctx *ctx, int vcpu, int in, int bus, int slot, int func,
	  int coff, int bytes, uint32_t *eax)
{
	struct businfo *bi;
	struct slotinfo *si;
	struct pci_vdev *dev;
	struct pci_vdev_ops *ops;
	int idx, needcfg;
	uint64_t addr, bar, mask;

	bi = pci_businfo[bus];
	if (bi != NULL) {
		si = &bi->slotinfo[slot];
		dev = si->si_funcs[func].fi_devi;
	} else
		dev = NULL;

	/*
	 * Just return if there is no device at this slot:func or if the
	 * the guest is doing an un-aligned access.
	 */
	if (dev == NULL || (bytes != 1 && bytes != 2 && bytes != 4) ||
	    (coff & (bytes - 1)) != 0) {
		if (in)
			*eax = 0xffffffff;
		return;
	}

	ops = dev->dev_ops;

	/*
	 * For non-passthru device, extended config space is NOT supported.
	 * Ignore all writes beyond the standard config space and return all
	 * ones on reads.
	 *
	 * For passthru device, extended config space is supported.
	 * Access to extended config space is implemented via libpciaccess.
	 */
	if (strcmp("passthru", ops->class_name)) {
		if (coff >= PCI_REGMAX + 1) {
			if (in) {
				*eax = 0xffffffff;
				/*
				 * Extended capabilities begin at offset 256 in
				 * config space.
				 * Absence of extended capabilities is signaled
				 * with all 0s in the extended capability header
				 * at offset 256.
				 */
				if (coff <= PCI_REGMAX + 4)
					*eax = 0x00000000;
			}
			return;
		}
	}

	/*
	 * Config read
	 */
	if (in) {
		/* Let the device emulation override the default handler */
		if (ops->vdev_cfgread != NULL) {
			needcfg = ops->vdev_cfgread(ctx, vcpu, dev, coff, bytes,
			    eax);
		} else {
			needcfg = 1;
		}

		if (needcfg)
			*eax = CFGREAD(dev, coff, bytes);

		pci_emul_hdrtype_fixup(bus, slot, coff, bytes, eax);
	} else {
		/* Let the device emulation override the default handler */
		if (ops->vdev_cfgwrite != NULL &&
		    (*ops->vdev_cfgwrite)(ctx, vcpu, dev,
					  coff, bytes, *eax) == 0)
			return;

		/*
		 * Special handling for write to BAR registers
		 */
		if (coff >= PCIR_BAR(0) && coff < PCIR_BAR(PCI_BARMAX + 1)) {
			/*
			 * Ignore writes to BAR registers that are not
			 * 4-byte aligned.
			 */
			if (bytes != 4 || (coff & 0x3) != 0)
				return;
			idx = (coff - PCIR_BAR(0)) / 4;
			mask = ~(dev->bar[idx].size - 1);
			switch (dev->bar[idx].type) {
			case PCIBAR_NONE:
				dev->bar[idx].addr = bar = 0;
				break;
			case PCIBAR_IO:
				addr = *eax & mask;
				addr &= 0xffff;
				bar = addr | PCIM_BAR_IO_SPACE;
				/*
				 * Register the new BAR value for interception
				 */
				if (addr != dev->bar[idx].addr) {
					update_bar_address(dev, addr, idx,
							   PCIBAR_IO);
				}
				break;
			case PCIBAR_MEM32:
				addr = bar = *eax & mask;
				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
				if (addr != dev->bar[idx].addr) {
					update_bar_address(dev, addr, idx,
							   PCIBAR_MEM32);
				}
				break;
			case PCIBAR_MEM64:
				addr = bar = *eax & mask;
				bar |= PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
				       PCIM_BAR_MEM_PREFETCH;
				if (addr != (uint32_t)dev->bar[idx].addr) {
					update_bar_address(dev, addr, idx,
							   PCIBAR_MEM64);
				}
				break;
			case PCIBAR_MEMHI64:
				assert(idx >= 1);
				mask = ~(dev->bar[idx - 1].size - 1);
				addr = ((uint64_t)*eax << 32) & mask;
				bar = addr >> 32;
				if (bar != dev->bar[idx - 1].addr >> 32) {
					update_bar_address(dev, addr, idx - 1,
							   PCIBAR_MEMHI64);
				}
				break;
			default:
				assert(0);
			}
			pci_set_cfgdata32(dev, coff, bar);

		} else if (coff == PCIR_BIOS) {
			/* ignore ROM BAR length request */
		} else if (pci_emul_iscap(dev, coff)) {
			pci_emul_capwrite(dev, coff, bytes, *eax);
		} else if (coff >= PCIR_COMMAND && coff < PCIR_REVID) {
			pci_emul_cmdsts_write(dev, coff, *eax, bytes);
		} else {
			CFGWRITE(dev, coff, *eax, bytes);
		}
	}
}

Project: acrn-hypervisor
File: core.c

pci_emul_alloc_pbar(struct pci_vdev *pdi, int idx, uint64_t hostbase,
		    enum pcibar_type type, uint64_t size)
{
	int error;
	uint64_t *baseptr, limit, addr, mask, lobits, bar;

	assert(idx >= 0 && idx <= PCI_BARMAX);

	if ((size & (size - 1)) != 0)
		size = 1UL << flsl(size);	/* round up to a power of 2 */

	/* Enforce minimum BAR sizes required by the PCI standard */
	if (type == PCIBAR_IO) {
		if (size < 4)
			size = 4;
	} else {
		if (size < 16)
			size = 16;
	}

	switch (type) {
	case PCIBAR_NONE:
		baseptr = NULL;
		addr = mask = lobits = 0;
		break;
	case PCIBAR_IO:
		baseptr = &pci_emul_iobase;
		limit = PCI_EMUL_IOLIMIT;
		mask = PCIM_BAR_IO_BASE;
		lobits = PCIM_BAR_IO_SPACE;
		break;
	case PCIBAR_MEM64:
		/*
		 * FIXME
		 * Some drivers do not work well if the 64-bit BAR is allocated
		 * above 4GB. Allow for this by allocating small requests under
		 * 4GB unless then allocation size is larger than some arbitrary
		 * number (32MB currently). If guest booted by ovmf, then skip the
		 * workaround.
		 */
		if (!skip_pci_mem64bar_workaround && (size <= 32 * 1024 * 1024)) {
			baseptr = &pci_emul_membase32;
			limit = PCI_EMUL_MEMLIMIT32;
			mask = PCIM_BAR_MEM_BASE;
			lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64;
			break;
		}

		/*
		 * XXX special case for device requiring peer-peer DMA
		 */
		if (size == 0x100000000UL)
			baseptr = &hostbase;
		else
			baseptr = &pci_emul_membase64;
		limit = PCI_EMUL_MEMLIMIT64;
		mask = PCIM_BAR_MEM_BASE;
		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_64 |
			PCIM_BAR_MEM_PREFETCH;
		break;
	case PCIBAR_MEM32:
		baseptr = &pci_emul_membase32;
		limit = PCI_EMUL_MEMLIMIT32;
		mask = PCIM_BAR_MEM_BASE;
		lobits = PCIM_BAR_MEM_SPACE | PCIM_BAR_MEM_32;
		break;
	default:
		printf("pci_emul_alloc_base: invalid bar type %d\n", type);
		assert(0);
	}

	if (baseptr != NULL) {
		error = pci_emul_alloc_resource(baseptr, limit, size, &addr);
		if (error != 0)
			return error;
	}

	pdi->bar[idx].type = type;
	pdi->bar[idx].addr = addr;
	pdi->bar[idx].size = size;

	/* Initialize the BAR register in config space */
	bar = (addr & mask) | lobits;
	pci_set_cfgdata32(pdi, PCIR_BAR(idx), bar);

	if (type == PCIBAR_MEM64) {
		assert(idx + 1 <= PCI_BARMAX);
		pdi->bar[idx + 1].type = PCIBAR_MEMHI64;
		pci_set_cfgdata32(pdi, PCIR_BAR(idx + 1), bar >> 32);
	}

	register_bar(pdi, idx);

	return 0;
}