openbsd_pci.c revision cad31331
14f5e7dd7Smrg/*
2cad31331Smrg * Copyright (c) 2008, 2011 Mark Kettenis
34f5e7dd7Smrg *
44f5e7dd7Smrg * Permission to use, copy, modify, and distribute this software for any
54f5e7dd7Smrg * purpose with or without fee is hereby granted, provided that the above
64f5e7dd7Smrg * copyright notice and this permission notice appear in all copies.
74f5e7dd7Smrg *
84f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
94f5e7dd7Smrg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
104f5e7dd7Smrg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
114f5e7dd7Smrg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
124f5e7dd7Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
134f5e7dd7Smrg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
144f5e7dd7Smrg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
154f5e7dd7Smrg */
164f5e7dd7Smrg
174f5e7dd7Smrg#include <sys/param.h>
184f5e7dd7Smrg#include <sys/ioctl.h>
194f5e7dd7Smrg#include <sys/memrange.h>
204f5e7dd7Smrg#include <sys/mman.h>
214f5e7dd7Smrg#include <sys/pciio.h>
224f5e7dd7Smrg
234f5e7dd7Smrg#include <dev/pci/pcireg.h>
244f5e7dd7Smrg#include <dev/pci/pcidevs.h>
254f5e7dd7Smrg
264f5e7dd7Smrg#include <errno.h>
274f5e7dd7Smrg#include <fcntl.h>
284f5e7dd7Smrg#include <stdio.h>
294f5e7dd7Smrg#include <stdlib.h>
304f5e7dd7Smrg#include <string.h>
314f5e7dd7Smrg#include <unistd.h>
324f5e7dd7Smrg
334f5e7dd7Smrg#include "pciaccess.h"
344f5e7dd7Smrg#include "pciaccess_private.h"
354f5e7dd7Smrg
364f5e7dd7Smrg/*
374f5e7dd7Smrg * This should allow for 16 domains, which should cover everything
384f5e7dd7Smrg * except perhaps the really big fridge-sized sparc64 server machines
394f5e7dd7Smrg * that are unlikely to have any graphics hardware in them.
404f5e7dd7Smrg */
414f5e7dd7Smrgstatic int pcifd[16];
424f5e7dd7Smrgstatic int ndomains;
434f5e7dd7Smrg
444f5e7dd7Smrgstatic int aperturefd = -1;
454f5e7dd7Smrg
464f5e7dd7Smrgstatic int
474f5e7dd7Smrgpci_read(int domain, int bus, int dev, int func, uint32_t reg, uint32_t *val)
484f5e7dd7Smrg{
494f5e7dd7Smrg	struct pci_io io;
504f5e7dd7Smrg	int err;
514f5e7dd7Smrg
524f5e7dd7Smrg	bzero(&io, sizeof(io));
534f5e7dd7Smrg	io.pi_sel.pc_bus = bus;
544f5e7dd7Smrg	io.pi_sel.pc_dev = dev;
554f5e7dd7Smrg	io.pi_sel.pc_func = func;
564f5e7dd7Smrg	io.pi_reg = reg;
574f5e7dd7Smrg	io.pi_width = 4;
584f5e7dd7Smrg
594f5e7dd7Smrg	err = ioctl(pcifd[domain], PCIOCREAD, &io);
604f5e7dd7Smrg	if (err)
614f5e7dd7Smrg		return (err);
624f5e7dd7Smrg
634f5e7dd7Smrg	*val = io.pi_data;
644f5e7dd7Smrg
654f5e7dd7Smrg	return 0;
664f5e7dd7Smrg}
674f5e7dd7Smrg
684f5e7dd7Smrgstatic int
694f5e7dd7Smrgpci_write(int domain, int bus, int dev, int func, uint32_t reg, uint32_t val)
704f5e7dd7Smrg{
714f5e7dd7Smrg	struct pci_io io;
724f5e7dd7Smrg
734f5e7dd7Smrg	bzero(&io, sizeof(io));
744f5e7dd7Smrg	io.pi_sel.pc_bus = bus;
754f5e7dd7Smrg	io.pi_sel.pc_dev = dev;
764f5e7dd7Smrg	io.pi_sel.pc_func = func;
774f5e7dd7Smrg	io.pi_reg = reg;
784f5e7dd7Smrg	io.pi_width = 4;
794f5e7dd7Smrg	io.pi_data = val;
804f5e7dd7Smrg
814f5e7dd7Smrg	return ioctl(pcifd[domain], PCIOCWRITE, &io);
824f5e7dd7Smrg}
834f5e7dd7Smrg
844f5e7dd7Smrg/**
854f5e7dd7Smrg * Read a VGA ROM
864f5e7dd7Smrg *
874f5e7dd7Smrg */
884f5e7dd7Smrgstatic int
894f5e7dd7Smrgpci_device_openbsd_read_rom(struct pci_device *device, void *buffer)
904f5e7dd7Smrg{
914f5e7dd7Smrg	struct pci_device_private *priv = (struct pci_device_private *)device;
924f5e7dd7Smrg	unsigned char *bios;
934f5e7dd7Smrg	pciaddr_t rom_base;
944f5e7dd7Smrg	pciaddr_t rom_size;
954f5e7dd7Smrg	u_int32_t csr, rom;
964f5e7dd7Smrg	int pci_rom, domain, bus, dev, func;
974f5e7dd7Smrg
984f5e7dd7Smrg	domain = device->domain;
994f5e7dd7Smrg	if (domain < 0 || domain >= ndomains)
1004f5e7dd7Smrg		return ENXIO;
1014f5e7dd7Smrg
1024f5e7dd7Smrg	bus = device->bus;
1034f5e7dd7Smrg	dev = device->dev;
1044f5e7dd7Smrg	func = device->func;
1054f5e7dd7Smrg
1064f5e7dd7Smrg	if (aperturefd == -1)
1074f5e7dd7Smrg		return ENOSYS;
1084f5e7dd7Smrg
1094f5e7dd7Smrg	if (priv->base.rom_size == 0) {
1104f5e7dd7Smrg#if defined(__alpha__) || defined(__amd64__) || defined(__i386__)
1114f5e7dd7Smrg		if ((device->device_class & 0x00ffff00) ==
1124f5e7dd7Smrg		    ((PCI_CLASS_DISPLAY << 16) |
1134f5e7dd7Smrg			(PCI_SUBCLASS_DISPLAY_VGA << 8))) {
1144f5e7dd7Smrg			rom_base = 0xc0000;
1154f5e7dd7Smrg			rom_size = 0x10000;
1164f5e7dd7Smrg			pci_rom = 0;
1174f5e7dd7Smrg		} else
1184f5e7dd7Smrg#endif
1194f5e7dd7Smrg			return ENOSYS;
1204f5e7dd7Smrg	} else {
1214f5e7dd7Smrg		rom_base = priv->rom_base;
1224f5e7dd7Smrg		rom_size = priv->base.rom_size;
1234f5e7dd7Smrg		pci_rom = 1;
1244f5e7dd7Smrg
1254f5e7dd7Smrg		pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, &csr);
1264f5e7dd7Smrg		pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG,
1274f5e7dd7Smrg		    csr | PCI_COMMAND_MEM_ENABLE);
1284f5e7dd7Smrg		pci_read(domain, bus, dev, func, PCI_ROM_REG, &rom);
1294f5e7dd7Smrg		pci_write(domain, bus, dev, func, PCI_ROM_REG,
1304f5e7dd7Smrg		    rom | PCI_ROM_ENABLE);
1314f5e7dd7Smrg	}
1324f5e7dd7Smrg
1334f5e7dd7Smrg	bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED,
1344f5e7dd7Smrg	    aperturefd, (off_t)rom_base);
1354f5e7dd7Smrg	if (bios == MAP_FAILED)
1364f5e7dd7Smrg		return errno;
1374f5e7dd7Smrg
1384f5e7dd7Smrg	memcpy(buffer, bios, rom_size);
1394f5e7dd7Smrg	munmap(bios, rom_size);
1404f5e7dd7Smrg
1414f5e7dd7Smrg	if (pci_rom) {
1424f5e7dd7Smrg		/* Restore PCI config space */
1434f5e7dd7Smrg		pci_write(domain, bus, dev, func, PCI_ROM_REG, rom);
1444f5e7dd7Smrg		pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, csr);
1454f5e7dd7Smrg	}
1464f5e7dd7Smrg	return 0;
1474f5e7dd7Smrg}
1484f5e7dd7Smrg
1494f5e7dd7Smrgstatic int
1504f5e7dd7Smrgpci_nfuncs(int domain, int bus, int dev)
1514f5e7dd7Smrg{
1524f5e7dd7Smrg	uint32_t hdr;
1534f5e7dd7Smrg
1544f5e7dd7Smrg	if (domain < 0 || domain >= ndomains)
1554f5e7dd7Smrg		return ENXIO;
1564f5e7dd7Smrg
1574f5e7dd7Smrg	if (pci_read(domain, bus, dev, 0, PCI_BHLC_REG, &hdr) != 0)
1584f5e7dd7Smrg		return -1;
1594f5e7dd7Smrg
1604f5e7dd7Smrg	return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1);
1614f5e7dd7Smrg}
1624f5e7dd7Smrg
1634f5e7dd7Smrgstatic int
1644f5e7dd7Smrgpci_device_openbsd_map_range(struct pci_device *dev,
1654f5e7dd7Smrg    struct pci_device_mapping *map)
1664f5e7dd7Smrg{
1674f5e7dd7Smrg	struct mem_range_desc mr;
1684f5e7dd7Smrg	struct mem_range_op mo;
1694f5e7dd7Smrg	int prot = PROT_READ;
1704f5e7dd7Smrg
1714f5e7dd7Smrg	if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
1724f5e7dd7Smrg		prot |= PROT_WRITE;
1734f5e7dd7Smrg
1744f5e7dd7Smrg	map->memory = mmap(NULL, map->size, prot, MAP_SHARED, aperturefd,
1754f5e7dd7Smrg	    map->base);
1764f5e7dd7Smrg	if (map->memory == MAP_FAILED)
1774f5e7dd7Smrg		return  errno;
1784f5e7dd7Smrg#if defined(__i386__) || defined(__amd64__)
1794f5e7dd7Smrg	/* No need to set an MTRR if it's the default mode. */
1804f5e7dd7Smrg	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
1814f5e7dd7Smrg	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
1824f5e7dd7Smrg		mr.mr_base = map->base;
1834f5e7dd7Smrg		mr.mr_len = map->size;
1844f5e7dd7Smrg		mr.mr_flags = 0;
1854f5e7dd7Smrg		if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE)
1864f5e7dd7Smrg			mr.mr_flags |= MDF_WRITEBACK;
1874f5e7dd7Smrg		if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
1884f5e7dd7Smrg			mr.mr_flags |= MDF_WRITECOMBINE;
1894f5e7dd7Smrg		strlcpy(mr.mr_owner, "pciaccess", sizeof(mr.mr_owner));
1904f5e7dd7Smrg
1914f5e7dd7Smrg		mo.mo_desc = &mr;
1924f5e7dd7Smrg		mo.mo_arg[0] = MEMRANGE_SET_UPDATE;
1934f5e7dd7Smrg
1944f5e7dd7Smrg		if (ioctl(aperturefd, MEMRANGE_SET, &mo))
1954f5e7dd7Smrg			(void)fprintf(stderr, "mtrr set failed: %s\n",
1964f5e7dd7Smrg			    strerror(errno));
1974f5e7dd7Smrg	}
1984f5e7dd7Smrg#endif
1994f5e7dd7Smrg	return 0;
2004f5e7dd7Smrg}
2014f5e7dd7Smrg
2024f5e7dd7Smrgstatic int
2034f5e7dd7Smrgpci_device_openbsd_unmap_range(struct pci_device *dev,
2044f5e7dd7Smrg    struct pci_device_mapping *map)
2054f5e7dd7Smrg{
2064f5e7dd7Smrg#if defined(__i386__) || defined(__amd64__)
2074f5e7dd7Smrg	struct mem_range_desc mr;
2084f5e7dd7Smrg	struct mem_range_op mo;
2094f5e7dd7Smrg
2104f5e7dd7Smrg	if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
2114f5e7dd7Smrg	    (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) {
2124f5e7dd7Smrg		mr.mr_base = map->base;
2134f5e7dd7Smrg		mr.mr_len = map->size;
2144f5e7dd7Smrg		mr.mr_flags = MDF_UNCACHEABLE;
2154f5e7dd7Smrg		strlcpy(mr.mr_owner, "pciaccess", sizeof(mr.mr_owner));
2164f5e7dd7Smrg
2174f5e7dd7Smrg		mo.mo_desc = &mr;
2184f5e7dd7Smrg		mo.mo_arg[0] = MEMRANGE_SET_REMOVE;
2194f5e7dd7Smrg
2204f5e7dd7Smrg		(void)ioctl(aperturefd, MEMRANGE_SET, &mo);
2214f5e7dd7Smrg	}
2224f5e7dd7Smrg#endif
2234f5e7dd7Smrg	return pci_device_generic_unmap_range(dev, map);
2244f5e7dd7Smrg}
2254f5e7dd7Smrg
2264f5e7dd7Smrgstatic int
2274f5e7dd7Smrgpci_device_openbsd_read(struct pci_device *dev, void *data,
2284f5e7dd7Smrg    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
2294f5e7dd7Smrg{
2304f5e7dd7Smrg	struct pci_io io;
2314f5e7dd7Smrg
2324f5e7dd7Smrg	io.pi_sel.pc_bus = dev->bus;
2334f5e7dd7Smrg	io.pi_sel.pc_dev = dev->dev;
2344f5e7dd7Smrg	io.pi_sel.pc_func = dev->func;
2354f5e7dd7Smrg
2364f5e7dd7Smrg	*bytes_read = 0;
2374f5e7dd7Smrg	while (size > 0) {
2384f5e7dd7Smrg		int toread = MIN(size, 4 - (offset & 0x3));
2394f5e7dd7Smrg
2404f5e7dd7Smrg		io.pi_reg = (offset & ~0x3);
2414f5e7dd7Smrg		io.pi_width = 4;
2424f5e7dd7Smrg
2434f5e7dd7Smrg		if (ioctl(pcifd[dev->domain], PCIOCREAD, &io) == -1)
2444f5e7dd7Smrg			return errno;
2454f5e7dd7Smrg
2464f5e7dd7Smrg		io.pi_data = htole32(io.pi_data);
2474f5e7dd7Smrg		io.pi_data >>= ((offset & 0x3) * 8);
2484f5e7dd7Smrg
2494f5e7dd7Smrg		memcpy(data, &io.pi_data, toread);
2504f5e7dd7Smrg
2514f5e7dd7Smrg		offset += toread;
2524f5e7dd7Smrg		data = (char *)data + toread;
2534f5e7dd7Smrg		size -= toread;
2544f5e7dd7Smrg		*bytes_read += toread;
2554f5e7dd7Smrg	}
2564f5e7dd7Smrg
2574f5e7dd7Smrg	return 0;
2584f5e7dd7Smrg}
2594f5e7dd7Smrg
2604f5e7dd7Smrgstatic int
2614f5e7dd7Smrgpci_device_openbsd_write(struct pci_device *dev, const void *data,
2624f5e7dd7Smrg    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
2634f5e7dd7Smrg{
2644f5e7dd7Smrg	struct pci_io io;
2654f5e7dd7Smrg
2664f5e7dd7Smrg	if ((offset % 4) != 0 || (size % 4) != 0)
2674f5e7dd7Smrg		return EINVAL;
2684f5e7dd7Smrg
2694f5e7dd7Smrg	io.pi_sel.pc_bus = dev->bus;
2704f5e7dd7Smrg	io.pi_sel.pc_dev = dev->dev;
2714f5e7dd7Smrg	io.pi_sel.pc_func = dev->func;
2724f5e7dd7Smrg
2734f5e7dd7Smrg	*bytes_written = 0;
2744f5e7dd7Smrg	while (size > 0) {
2754f5e7dd7Smrg		io.pi_reg = offset;
2764f5e7dd7Smrg		io.pi_width = 4;
2774f5e7dd7Smrg		memcpy(&io.pi_data, data, 4);
2784f5e7dd7Smrg
2794f5e7dd7Smrg		if (ioctl(pcifd[dev->domain], PCIOCWRITE, &io) == -1)
2804f5e7dd7Smrg			return errno;
2814f5e7dd7Smrg
2824f5e7dd7Smrg		offset += 4;
2834f5e7dd7Smrg		data = (char *)data + 4;
2844f5e7dd7Smrg		size -= 4;
2854f5e7dd7Smrg		*bytes_written += 4;
2864f5e7dd7Smrg	}
2874f5e7dd7Smrg
2884f5e7dd7Smrg	return 0;
2894f5e7dd7Smrg}
2904f5e7dd7Smrg
2914f5e7dd7Smrgstatic void
2924f5e7dd7Smrgpci_system_openbsd_destroy(void)
2934f5e7dd7Smrg{
2944f5e7dd7Smrg	int domain;
2954f5e7dd7Smrg
2964f5e7dd7Smrg	for (domain = 0; domain < ndomains; domain++)
2974f5e7dd7Smrg		close(pcifd[domain]);
2984f5e7dd7Smrg	ndomains = 0;
2994f5e7dd7Smrg}
3004f5e7dd7Smrg
3014f5e7dd7Smrgstatic int
3024f5e7dd7Smrgpci_device_openbsd_probe(struct pci_device *device)
3034f5e7dd7Smrg{
3044f5e7dd7Smrg	struct pci_device_private *priv = (struct pci_device_private *)device;
3054f5e7dd7Smrg	struct pci_mem_region *region;
3064f5e7dd7Smrg	uint64_t reg64, size64;
3074f5e7dd7Smrg	uint32_t bar, reg, size;
3084f5e7dd7Smrg	int domain, bus, dev, func, err;
3094f5e7dd7Smrg
3104f5e7dd7Smrg	domain = device->domain;
3114f5e7dd7Smrg	bus = device->bus;
3124f5e7dd7Smrg	dev = device->dev;
3134f5e7dd7Smrg	func = device->func;
3144f5e7dd7Smrg
3154f5e7dd7Smrg	err = pci_read(domain, bus, dev, func, PCI_BHLC_REG, &reg);
3164f5e7dd7Smrg	if (err)
3174f5e7dd7Smrg		return err;
3184f5e7dd7Smrg
3194f5e7dd7Smrg	priv->header_type = PCI_HDRTYPE_TYPE(reg);
3204f5e7dd7Smrg	if (priv->header_type != 0)
3214f5e7dd7Smrg		return 0;
3224f5e7dd7Smrg
3234f5e7dd7Smrg	region = device->regions;
3244f5e7dd7Smrg	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
3254f5e7dd7Smrg	     bar += sizeof(uint32_t), region++) {
3264f5e7dd7Smrg		err = pci_read(domain, bus, dev, func, bar, &reg);
3274f5e7dd7Smrg		if (err)
3284f5e7dd7Smrg			return err;
3294f5e7dd7Smrg
3304f5e7dd7Smrg		/* Probe the size of the region. */
3314f5e7dd7Smrg		err = pci_write(domain, bus, dev, func, bar, ~0);
3324f5e7dd7Smrg		if (err)
3334f5e7dd7Smrg			return err;
3344f5e7dd7Smrg		pci_read(domain, bus, dev, func, bar, &size);
3354f5e7dd7Smrg		pci_write(domain, bus, dev, func, bar, reg);
3364f5e7dd7Smrg
3374f5e7dd7Smrg		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
3384f5e7dd7Smrg			region->is_IO = 1;
3394f5e7dd7Smrg			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
3404f5e7dd7Smrg			region->size = PCI_MAPREG_IO_SIZE(size);
3414f5e7dd7Smrg		} else {
3424f5e7dd7Smrg			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
3434f5e7dd7Smrg				region->is_prefetchable = 1;
3444f5e7dd7Smrg			switch(PCI_MAPREG_MEM_TYPE(reg)) {
3454f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT:
3464f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
3474f5e7dd7Smrg				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
3484f5e7dd7Smrg				region->size = PCI_MAPREG_MEM_SIZE(size);
3494f5e7dd7Smrg				break;
3504f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_64BIT:
3514f5e7dd7Smrg				region->is_64 = 1;
3524f5e7dd7Smrg
3534f5e7dd7Smrg				reg64 = reg;
3544f5e7dd7Smrg				size64 = size;
3554f5e7dd7Smrg
3564f5e7dd7Smrg				bar += sizeof(uint32_t);
3574f5e7dd7Smrg
3584f5e7dd7Smrg				err = pci_read(domain, bus, dev, func, bar, &reg);
3594f5e7dd7Smrg				if (err)
3604f5e7dd7Smrg					return err;
3614f5e7dd7Smrg				reg64 |= (uint64_t)reg << 32;
3624f5e7dd7Smrg
3634f5e7dd7Smrg				err = pci_write(domain, bus, dev, func, bar, ~0);
3644f5e7dd7Smrg				if (err)
3654f5e7dd7Smrg					return err;
3664f5e7dd7Smrg				pci_read(domain, bus, dev, func, bar, &size);
3674f5e7dd7Smrg				pci_write(domain, bus, dev, func, bar, reg64 >> 32);
3684f5e7dd7Smrg				size64 |= (uint64_t)size << 32;
3694f5e7dd7Smrg
3704f5e7dd7Smrg				region->base_addr = PCI_MAPREG_MEM64_ADDR(reg64);
3714f5e7dd7Smrg				region->size = PCI_MAPREG_MEM64_SIZE(size64);
3724f5e7dd7Smrg				region++;
3734f5e7dd7Smrg				break;
3744f5e7dd7Smrg			}
3754f5e7dd7Smrg		}
3764f5e7dd7Smrg	}
3774f5e7dd7Smrg
3784f5e7dd7Smrg	/* Probe expansion ROM if present */
3794f5e7dd7Smrg	err = pci_read(domain, bus, dev, func, PCI_ROM_REG, &reg);
3804f5e7dd7Smrg	if (err)
3814f5e7dd7Smrg		return err;
3824f5e7dd7Smrg	if (reg != 0) {
3834f5e7dd7Smrg		err = pci_write(domain, bus, dev, func, PCI_ROM_REG, ~PCI_ROM_ENABLE);
3844f5e7dd7Smrg		if (err)
3854f5e7dd7Smrg			return err;
3864f5e7dd7Smrg		pci_read(domain, bus, dev, func, PCI_ROM_REG, &size);
3874f5e7dd7Smrg		pci_write(domain, bus, dev, func, PCI_ROM_REG, reg);
3884f5e7dd7Smrg
3894f5e7dd7Smrg		if (PCI_ROM_ADDR(reg) != 0) {
3904f5e7dd7Smrg			priv->rom_base = PCI_ROM_ADDR(reg);
3914f5e7dd7Smrg			device->rom_size = PCI_ROM_SIZE(size);
3924f5e7dd7Smrg		}
3934f5e7dd7Smrg	}
3944f5e7dd7Smrg	return 0;
3954f5e7dd7Smrg}
3964f5e7dd7Smrg
397cad31331Smrg#if defined(__i386__) || defined(__amd64__)
398cad31331Smrg#include <machine/sysarch.h>
399cad31331Smrg#include <machine/pio.h>
400cad31331Smrg#endif
401cad31331Smrg
402cad31331Smrgstatic struct pci_io_handle *
403cad31331Smrgpci_device_openbsd_open_legacy_io(struct pci_io_handle *ret,
404cad31331Smrg    struct pci_device *dev, pciaddr_t base, pciaddr_t size)
405cad31331Smrg{
406cad31331Smrg#if defined(__i386__)
407cad31331Smrg	struct i386_iopl_args ia;
408cad31331Smrg
409cad31331Smrg	ia.iopl = 1;
410cad31331Smrg	if (sysarch(I386_IOPL, &ia))
411cad31331Smrg		return NULL;
412cad31331Smrg
413cad31331Smrg	ret->base = base;
414cad31331Smrg	ret->size = size;
415cad31331Smrg	return ret;
416cad31331Smrg#elif defined(__amd64__)
417cad31331Smrg	struct amd64_iopl_args ia;
418cad31331Smrg
419cad31331Smrg	ia.iopl = 1;
420cad31331Smrg	if (sysarch(AMD64_IOPL, &ia))
421cad31331Smrg		return NULL;
422cad31331Smrg
423cad31331Smrg	ret->base = base;
424cad31331Smrg	ret->size = size;
425cad31331Smrg	return ret;
426cad31331Smrg#elif defined(PCI_MAGIC_IO_RANGE)
427cad31331Smrg	ret->memory = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
428cad31331Smrg	    aperturefd, PCI_MAGIC_IO_RANGE + base);
429cad31331Smrg	if (ret->memory == MAP_FAILED)
430cad31331Smrg		return NULL;
431cad31331Smrg
432cad31331Smrg	ret->base = base;
433cad31331Smrg	ret->size = size;
434cad31331Smrg	return ret;
435cad31331Smrg#else
436cad31331Smrg	return NULL;
437cad31331Smrg#endif
438cad31331Smrg}
439cad31331Smrg
440cad31331Smrgstatic uint32_t
441cad31331Smrgpci_device_openbsd_read32(struct pci_io_handle *handle, uint32_t reg)
442cad31331Smrg{
443cad31331Smrg#if defined(__i386__) || defined(__amd64__)
444cad31331Smrg	return inl(handle->base + reg);
445cad31331Smrg#else
446cad31331Smrg	return *(uint32_t *)((uintptr_t)handle->memory + reg);
447cad31331Smrg#endif
448cad31331Smrg}
449cad31331Smrg
450cad31331Smrgstatic uint16_t
451cad31331Smrgpci_device_openbsd_read16(struct pci_io_handle *handle, uint32_t reg)
452cad31331Smrg{
453cad31331Smrg#if defined(__i386__) || defined(__amd64__)
454cad31331Smrg	return inw(handle->base + reg);
455cad31331Smrg#else
456cad31331Smrg	return *(uint16_t *)((uintptr_t)handle->memory + reg);
457cad31331Smrg#endif
458cad31331Smrg}
459cad31331Smrg
460cad31331Smrgstatic uint8_t
461cad31331Smrgpci_device_openbsd_read8(struct pci_io_handle *handle, uint32_t reg)
462cad31331Smrg{
463cad31331Smrg#if defined(__i386__) || defined(__amd64__)
464cad31331Smrg	return inb(handle->base + reg);
465cad31331Smrg#else
466cad31331Smrg	return *(uint8_t *)((uintptr_t)handle->memory + reg);
467cad31331Smrg#endif
468cad31331Smrg}
469cad31331Smrg
470cad31331Smrgstatic void
471cad31331Smrgpci_device_openbsd_write32(struct pci_io_handle *handle, uint32_t reg,
472cad31331Smrg    uint32_t data)
473cad31331Smrg{
474cad31331Smrg#if defined(__i386__) || defined(__amd64__)
475cad31331Smrg	outl(handle->base + reg, data);
476cad31331Smrg#else
477cad31331Smrg	*(uint16_t *)((uintptr_t)handle->memory + reg) = data;
478cad31331Smrg#endif
479cad31331Smrg}
480cad31331Smrg
481cad31331Smrgstatic void
482cad31331Smrgpci_device_openbsd_write16(struct pci_io_handle *handle, uint32_t reg,
483cad31331Smrg    uint16_t data)
484cad31331Smrg{
485cad31331Smrg#if defined(__i386__) || defined(__amd64__)
486cad31331Smrg	outw(handle->base + reg, data);
487cad31331Smrg#else
488cad31331Smrg	*(uint8_t *)((uintptr_t)handle->memory + reg) = data;
489cad31331Smrg#endif
490cad31331Smrg}
491cad31331Smrg
492cad31331Smrgstatic void
493cad31331Smrgpci_device_openbsd_write8(struct pci_io_handle *handle, uint32_t reg,
494cad31331Smrg    uint8_t data)
495cad31331Smrg{
496cad31331Smrg#if defined(__i386__) || defined(__amd64__)
497cad31331Smrg	outb(handle->base + reg, data);
498cad31331Smrg#else
499cad31331Smrg	*(uint32_t *)((uintptr_t)handle->memory + reg) = data;
500cad31331Smrg#endif
501cad31331Smrg}
502cad31331Smrg
503cad31331Smrgstatic int
504cad31331Smrgpci_device_openbsd_map_legacy(struct pci_device *dev, pciaddr_t base,
505cad31331Smrg    pciaddr_t size, unsigned map_flags, void **addr)
506cad31331Smrg{
507cad31331Smrg	struct pci_device_mapping map;
508cad31331Smrg	int err;
509cad31331Smrg
510cad31331Smrg	map.base = base;
511cad31331Smrg	map.size = size;
512cad31331Smrg	map.flags = map_flags;
513cad31331Smrg	map.memory = NULL;
514cad31331Smrg	err = pci_device_openbsd_map_range(dev, &map);
515cad31331Smrg	*addr = map.memory;
516cad31331Smrg
517cad31331Smrg	return err;
518cad31331Smrg}
519cad31331Smrg
520cad31331Smrgstatic int
521cad31331Smrgpci_device_openbsd_unmap_legacy(struct pci_device *dev, void *addr,
522cad31331Smrg    pciaddr_t size)
523cad31331Smrg{
524cad31331Smrg	struct pci_device_mapping map;
525cad31331Smrg
526cad31331Smrg	map.memory = addr;
527cad31331Smrg	map.size = size;
528cad31331Smrg	map.flags = 0;
529cad31331Smrg	return pci_device_openbsd_unmap_range(dev, &map);
530cad31331Smrg}
531cad31331Smrg
5324f5e7dd7Smrgstatic const struct pci_system_methods openbsd_pci_methods = {
5334f5e7dd7Smrg	pci_system_openbsd_destroy,
5344f5e7dd7Smrg	NULL,
5354f5e7dd7Smrg	pci_device_openbsd_read_rom,
5364f5e7dd7Smrg	pci_device_openbsd_probe,
5374f5e7dd7Smrg	pci_device_openbsd_map_range,
5384f5e7dd7Smrg	pci_device_openbsd_unmap_range,
5394f5e7dd7Smrg	pci_device_openbsd_read,
5404f5e7dd7Smrg	pci_device_openbsd_write,
541cad31331Smrg	pci_fill_capabilities_generic,
542cad31331Smrg	NULL,
543cad31331Smrg	NULL,
544cad31331Smrg	NULL,
545cad31331Smrg	NULL,
546cad31331Smrg	pci_device_openbsd_open_legacy_io,
547cad31331Smrg	NULL,
548cad31331Smrg	pci_device_openbsd_read32,
549cad31331Smrg	pci_device_openbsd_read16,
550cad31331Smrg	pci_device_openbsd_read8,
551cad31331Smrg	pci_device_openbsd_write32,
552cad31331Smrg	pci_device_openbsd_write16,
553cad31331Smrg	pci_device_openbsd_write8,
554cad31331Smrg	pci_device_openbsd_map_legacy,
555cad31331Smrg	pci_device_openbsd_unmap_legacy
5564f5e7dd7Smrg};
5574f5e7dd7Smrg
5584f5e7dd7Smrgint
5594f5e7dd7Smrgpci_system_openbsd_create(void)
5604f5e7dd7Smrg{
5614f5e7dd7Smrg	struct pci_device_private *device;
5624f5e7dd7Smrg	int domain, bus, dev, func, ndevs, nfuncs;
5634f5e7dd7Smrg	char path[MAXPATHLEN];
5644f5e7dd7Smrg	uint32_t reg;
5654f5e7dd7Smrg
5664f5e7dd7Smrg	if (ndomains > 0)
5674f5e7dd7Smrg		return 0;
5684f5e7dd7Smrg
5694f5e7dd7Smrg	for (domain = 0; domain < sizeof(pcifd) / sizeof(pcifd[0]); domain++) {
5704f5e7dd7Smrg		snprintf(path, sizeof(path), "/dev/pci%d", domain);
571cad31331Smrg	        pcifd[domain] = open(path, O_RDWR | O_CLOEXEC);
5724f5e7dd7Smrg		if (pcifd[domain] == -1)
5734f5e7dd7Smrg			break;
5744f5e7dd7Smrg		ndomains++;
5754f5e7dd7Smrg	}
5764f5e7dd7Smrg
5774f5e7dd7Smrg	if (ndomains == 0)
5784f5e7dd7Smrg		return ENXIO;
5794f5e7dd7Smrg
5804f5e7dd7Smrg	pci_sys = calloc(1, sizeof(struct pci_system));
5814f5e7dd7Smrg	if (pci_sys == NULL) {
5824f5e7dd7Smrg		for (domain = 0; domain < ndomains; domain++)
5834f5e7dd7Smrg			close(pcifd[domain]);
5844f5e7dd7Smrg		ndomains = 0;
5854f5e7dd7Smrg		return ENOMEM;
5864f5e7dd7Smrg	}
5874f5e7dd7Smrg
5884f5e7dd7Smrg	pci_sys->methods = &openbsd_pci_methods;
5894f5e7dd7Smrg
5904f5e7dd7Smrg	ndevs = 0;
5914f5e7dd7Smrg	for (domain = 0; domain < ndomains; domain++) {
5924f5e7dd7Smrg		for (bus = 0; bus < 256; bus++) {
5934f5e7dd7Smrg			for (dev = 0; dev < 32; dev++) {
5944f5e7dd7Smrg				nfuncs = pci_nfuncs(domain, bus, dev);
5954f5e7dd7Smrg				for (func = 0; func < nfuncs; func++) {
5964f5e7dd7Smrg					if (pci_read(domain, bus, dev, func,
5974f5e7dd7Smrg					    PCI_ID_REG, &reg) != 0)
5984f5e7dd7Smrg						continue;
5994f5e7dd7Smrg					if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
6004f5e7dd7Smrg					    PCI_VENDOR(reg) == 0)
6014f5e7dd7Smrg						continue;
6024f5e7dd7Smrg
6034f5e7dd7Smrg					ndevs++;
6044f5e7dd7Smrg				}
6054f5e7dd7Smrg			}
6064f5e7dd7Smrg		}
6074f5e7dd7Smrg	}
6084f5e7dd7Smrg
6094f5e7dd7Smrg	pci_sys->num_devices = ndevs;
6104f5e7dd7Smrg	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
6114f5e7dd7Smrg	if (pci_sys->devices == NULL) {
6124f5e7dd7Smrg		free(pci_sys);
6134f5e7dd7Smrg		pci_sys = NULL;
6144f5e7dd7Smrg		for (domain = 0; domain < ndomains; domain++)
6154f5e7dd7Smrg			close(pcifd[domain]);
6164f5e7dd7Smrg		ndomains = 0;
6174f5e7dd7Smrg		return ENOMEM;
6184f5e7dd7Smrg	}
6194f5e7dd7Smrg
6204f5e7dd7Smrg	device = pci_sys->devices;
6214f5e7dd7Smrg	for (domain = 0; domain < ndomains; domain++) {
6224f5e7dd7Smrg		for (bus = 0; bus < 256; bus++) {
6234f5e7dd7Smrg			for (dev = 0; dev < 32; dev++) {
6244f5e7dd7Smrg				nfuncs = pci_nfuncs(domain, bus, dev);
6254f5e7dd7Smrg				for (func = 0; func < nfuncs; func++) {
6264f5e7dd7Smrg					if (pci_read(domain, bus, dev, func,
6274f5e7dd7Smrg					    PCI_ID_REG, &reg) != 0)
6284f5e7dd7Smrg						continue;
6294f5e7dd7Smrg					if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
6304f5e7dd7Smrg					    PCI_VENDOR(reg) == 0)
6314f5e7dd7Smrg						continue;
6324f5e7dd7Smrg
6334f5e7dd7Smrg					device->base.domain = domain;
6344f5e7dd7Smrg					device->base.bus = bus;
6354f5e7dd7Smrg					device->base.dev = dev;
6364f5e7dd7Smrg					device->base.func = func;
6374f5e7dd7Smrg					device->base.vendor_id = PCI_VENDOR(reg);
6384f5e7dd7Smrg					device->base.device_id = PCI_PRODUCT(reg);
6394f5e7dd7Smrg
6404f5e7dd7Smrg					if (pci_read(domain, bus, dev, func,
6414f5e7dd7Smrg					    PCI_CLASS_REG, &reg) != 0)
6424f5e7dd7Smrg						continue;
6434f5e7dd7Smrg
6444f5e7dd7Smrg					device->base.device_class =
6454f5e7dd7Smrg					    PCI_INTERFACE(reg) |
6464f5e7dd7Smrg					    PCI_CLASS(reg) << 16 |
6474f5e7dd7Smrg					    PCI_SUBCLASS(reg) << 8;
6484f5e7dd7Smrg					device->base.revision = PCI_REVISION(reg);
6494f5e7dd7Smrg
6504f5e7dd7Smrg					if (pci_read(domain, bus, dev, func,
6514f5e7dd7Smrg					    PCI_SUBVEND_0, &reg) != 0)
6524f5e7dd7Smrg						continue;
6534f5e7dd7Smrg
6544f5e7dd7Smrg					device->base.subvendor_id = PCI_VENDOR(reg);
6554f5e7dd7Smrg					device->base.subdevice_id = PCI_PRODUCT(reg);
6564f5e7dd7Smrg
657cad31331Smrg					device->base.vgaarb_rsrc =
658cad31331Smrg					    VGA_ARB_RSRC_LEGACY_IO |
659cad31331Smrg					    VGA_ARB_RSRC_LEGACY_MEM;
660cad31331Smrg
6614f5e7dd7Smrg					device++;
6624f5e7dd7Smrg				}
6634f5e7dd7Smrg			}
6644f5e7dd7Smrg		}
6654f5e7dd7Smrg	}
6664f5e7dd7Smrg
6674f5e7dd7Smrg	return 0;
6684f5e7dd7Smrg}
6694f5e7dd7Smrg
6704f5e7dd7Smrgvoid
6714f5e7dd7Smrgpci_system_openbsd_init_dev_mem(int fd)
6724f5e7dd7Smrg{
6734f5e7dd7Smrg	aperturefd = fd;
6744f5e7dd7Smrg}
675cad31331Smrg
676cad31331Smrgint
677cad31331Smrgpci_device_vgaarb_init(void)
678cad31331Smrg{
679cad31331Smrg	struct pci_device *dev = pci_sys->vga_target;
680cad31331Smrg	struct pci_device_iterator *iter;
681cad31331Smrg	struct pci_id_match vga_match = {
682cad31331Smrg		PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY, PCI_MATCH_ANY,
683cad31331Smrg		(PCI_CLASS_DISPLAY << 16) | (PCI_SUBCLASS_DISPLAY_VGA << 8),
684cad31331Smrg		0x00ffff00
685cad31331Smrg	};
686cad31331Smrg	struct pci_vga pv;
687cad31331Smrg	int err;
688cad31331Smrg
689cad31331Smrg	pv.pv_sel.pc_bus = 0;
690cad31331Smrg	pv.pv_sel.pc_dev = 0;
691cad31331Smrg	pv.pv_sel.pc_func = 0;
692cad31331Smrg	err = ioctl(pcifd[0], PCIOCGETVGA, &pv);
693cad31331Smrg	if (err)
694cad31331Smrg		return err;
695cad31331Smrg
696cad31331Smrg	pci_sys->vga_target = pci_device_find_by_slot(0, pv.pv_sel.pc_bus,
697cad31331Smrg	    pv.pv_sel.pc_dev, pv.pv_sel.pc_func);
698cad31331Smrg
699cad31331Smrg	/* Count the number of VGA devices in domain 0. */
700cad31331Smrg	iter = pci_id_match_iterator_create(&vga_match);
701cad31331Smrg	if (iter == NULL)
702cad31331Smrg		return -1;
703cad31331Smrg	pci_sys->vga_count = 0;
704cad31331Smrg	while ((dev = pci_device_next(iter)) != NULL) {
705cad31331Smrg		if (dev->domain == 0)
706cad31331Smrg			pci_sys->vga_count++;
707cad31331Smrg	}
708cad31331Smrg	pci_iterator_destroy(iter);
709cad31331Smrg
710cad31331Smrg	return 0;
711cad31331Smrg}
712cad31331Smrg
713cad31331Smrgvoid
714cad31331Smrgpci_device_vgaarb_fini(void)
715cad31331Smrg{
716cad31331Smrg	struct pci_device *dev;
717cad31331Smrg	struct pci_vga pv;
718cad31331Smrg
719cad31331Smrg	if (pci_sys == NULL)
720cad31331Smrg		return;
721cad31331Smrg	dev = pci_sys->vga_target;
722cad31331Smrg	if (dev == NULL)
723cad31331Smrg		return;
724cad31331Smrg
725cad31331Smrg	pv.pv_sel.pc_bus = dev->bus;
726cad31331Smrg	pv.pv_sel.pc_dev = dev->dev;
727cad31331Smrg	pv.pv_sel.pc_func = dev->func;
728cad31331Smrg	pv.pv_lock = PCI_VGA_UNLOCK;
729cad31331Smrg	ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv);
730cad31331Smrg}
731cad31331Smrg
732cad31331Smrgint
733cad31331Smrgpci_device_vgaarb_set_target(struct pci_device *dev)
734cad31331Smrg{
735cad31331Smrg	pci_sys->vga_target = dev;
736cad31331Smrg	return (0);
737cad31331Smrg}
738cad31331Smrg
739cad31331Smrgint
740cad31331Smrgpci_device_vgaarb_lock(void)
741cad31331Smrg{
742cad31331Smrg	struct pci_device *dev = pci_sys->vga_target;
743cad31331Smrg	struct pci_vga pv;
744cad31331Smrg
745cad31331Smrg	if (dev == NULL)
746cad31331Smrg		return -1;
747cad31331Smrg
748cad31331Smrg#if 0
749cad31331Smrg	if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
750cad31331Smrg		return 0;
751cad31331Smrg#else
752cad31331Smrg	if (pci_sys->vga_count == 1)
753cad31331Smrg		return 0;
754cad31331Smrg#endif
755cad31331Smrg
756cad31331Smrg	pv.pv_sel.pc_bus = dev->bus;
757cad31331Smrg	pv.pv_sel.pc_dev = dev->dev;
758cad31331Smrg	pv.pv_sel.pc_func = dev->func;
759cad31331Smrg	pv.pv_lock = PCI_VGA_LOCK;
760cad31331Smrg	return ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv);
761cad31331Smrg}
762cad31331Smrg
763cad31331Smrgint
764cad31331Smrgpci_device_vgaarb_unlock(void)
765cad31331Smrg{
766cad31331Smrg	struct pci_device *dev = pci_sys->vga_target;
767cad31331Smrg	struct pci_vga pv;
768cad31331Smrg
769cad31331Smrg	if (dev == NULL)
770cad31331Smrg		return -1;
771cad31331Smrg
772cad31331Smrg#if 0
773cad31331Smrg	if (dev->vgaarb_rsrc == 0 || pci_sys->vga_count == 1)
774cad31331Smrg		return 0;
775cad31331Smrg#else
776cad31331Smrg	if (pci_sys->vga_count == 1)
777cad31331Smrg		return 0;
778cad31331Smrg#endif
779cad31331Smrg
780cad31331Smrg	pv.pv_sel.pc_bus = dev->bus;
781cad31331Smrg	pv.pv_sel.pc_dev = dev->dev;
782cad31331Smrg	pv.pv_sel.pc_func = dev->func;
783cad31331Smrg	pv.pv_lock = PCI_VGA_UNLOCK;
784cad31331Smrg	return ioctl(pcifd[dev->domain], PCIOCSETVGA, &pv);
785cad31331Smrg}
786cad31331Smrg
787cad31331Smrgint
788cad31331Smrgpci_device_vgaarb_get_info(struct pci_device *dev, int *vga_count,
789cad31331Smrg    int *rsrc_decodes)
790cad31331Smrg{
791cad31331Smrg	*vga_count = pci_sys->vga_count;
792cad31331Smrg
793cad31331Smrg	if (dev)
794cad31331Smrg		*rsrc_decodes = dev->vgaarb_rsrc;
795cad31331Smrg
796cad31331Smrg	return 0;
797cad31331Smrg}
798cad31331Smrg
799cad31331Smrgint
800cad31331Smrgpci_device_vgaarb_decodes(int rsrc_decodes)
801cad31331Smrg{
802cad31331Smrg	struct pci_device *dev = pci_sys->vga_target;
803cad31331Smrg
804cad31331Smrg	if (dev == NULL)
805cad31331Smrg		return -1;
806cad31331Smrg
807cad31331Smrg	dev->vgaarb_rsrc = rsrc_decodes;
808cad31331Smrg	return 0;
809cad31331Smrg}
810