x86_pci.c revision 86ea1d58
128d65773Smrg/*
286ea1d58Smrg * Copyright (c) 2009, 2012 Samuel Thibault
328d65773Smrg * Heavily inspired from the freebsd, netbsd, and openbsd backends
428d65773Smrg * (C) Copyright Eric Anholt 2006
528d65773Smrg * (C) Copyright IBM Corporation 2006
628d65773Smrg * Copyright (c) 2008 Juan Romero Pardines
728d65773Smrg * Copyright (c) 2008 Mark Kettenis
828d65773Smrg *
928d65773Smrg * Permission to use, copy, modify, and distribute this software for any
1028d65773Smrg * purpose with or without fee is hereby granted, provided that the above
1128d65773Smrg * copyright notice and this permission notice appear in all copies.
1228d65773Smrg *
1328d65773Smrg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1428d65773Smrg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1528d65773Smrg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1628d65773Smrg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1728d65773Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1828d65773Smrg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1928d65773Smrg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2028d65773Smrg */
2128d65773Smrg
2228d65773Smrg#define _GNU_SOURCE
2328d65773Smrg#include <unistd.h>
2428d65773Smrg#include <stdio.h>
2528d65773Smrg#include <stdlib.h>
2628d65773Smrg#include <errno.h>
2728d65773Smrg#include <fcntl.h>
2828d65773Smrg#include <sys/mman.h>
2928d65773Smrg#include <string.h>
3028d65773Smrg#include <strings.h>
3128d65773Smrg
3228d65773Smrg#include "pciaccess.h"
3328d65773Smrg#include "pciaccess_private.h"
3428d65773Smrg
3528d65773Smrg#if defined(__GNU__)
3628d65773Smrg
3728d65773Smrg#include <sys/io.h>
3828d65773Smrg
3928d65773Smrgstatic int
4028d65773Smrgx86_enable_io(void)
4128d65773Smrg{
4228d65773Smrg    if (!ioperm(0, 0xffff, 1))
4328d65773Smrg        return 0;
4428d65773Smrg    return errno;
4528d65773Smrg}
4628d65773Smrg
4728d65773Smrgstatic int
4828d65773Smrgx86_disable_io(void)
4928d65773Smrg{
5028d65773Smrg    if (!ioperm(0, 0xffff, 0))
5128d65773Smrg        return 0;
5228d65773Smrg    return errno;
5328d65773Smrg}
5428d65773Smrg
5528d65773Smrg#elif defined(__GLIBC__)
5628d65773Smrg
5728d65773Smrg#include <sys/io.h>
5828d65773Smrg
5928d65773Smrgstatic int
6028d65773Smrgx86_enable_io(void)
6128d65773Smrg{
6228d65773Smrg    if (!iopl(3))
6328d65773Smrg        return 0;
6428d65773Smrg    return errno;
6528d65773Smrg}
6628d65773Smrg
6728d65773Smrgstatic int
6828d65773Smrgx86_disable_io(void)
6928d65773Smrg{
7028d65773Smrg    if (!iopl(0))
7128d65773Smrg        return 0;
7228d65773Smrg    return errno;
7328d65773Smrg}
7428d65773Smrg
7528d65773Smrg#else
7628d65773Smrg
7728d65773Smrg#error How to enable IO ports on this system?
7828d65773Smrg
7928d65773Smrg#endif
8028d65773Smrg
8128d65773Smrg#define PCI_VENDOR(reg)		((reg) & 0xFFFF)
8228d65773Smrg#define PCI_VENDOR_INVALID	0xFFFF
8328d65773Smrg
8428d65773Smrg#define PCI_VENDOR_ID		0x00
8528d65773Smrg#define PCI_SUB_VENDOR_ID	0x2c
8628d65773Smrg#define PCI_VENDOR_ID_COMPAQ		0x0e11
8728d65773Smrg#define PCI_VENDOR_ID_INTEL		0x8086
8828d65773Smrg
8928d65773Smrg#define PCI_DEVICE(reg)		(((reg) >> 16) & 0xFFFF)
9028d65773Smrg#define PCI_DEVICE_INVALID	0xFFFF
9128d65773Smrg
9228d65773Smrg#define PCI_CLASS		0x08
9328d65773Smrg#define PCI_CLASS_DEVICE	0x0a
9428d65773Smrg#define PCI_CLASS_DISPLAY_VGA		0x0300
9528d65773Smrg#define PCI_CLASS_BRIDGE_HOST		0x0600
9628d65773Smrg
9728d65773Smrg#define	PCIC_DISPLAY	0x03
9828d65773Smrg#define	PCIS_DISPLAY_VGA	0x00
9928d65773Smrg
10028d65773Smrg#define PCI_HDRTYPE	0x0E
10128d65773Smrg#define PCI_IRQ		0x3C
10228d65773Smrg
10328d65773Smrgstruct pci_system_x86 {
10428d65773Smrg    struct pci_system system;
10528d65773Smrg    int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size);
10628d65773Smrg    int (*write)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size);
10728d65773Smrg};
10828d65773Smrg
10928d65773Smrgstatic int
11028d65773Smrgpci_system_x86_conf1_probe(void)
11128d65773Smrg{
11228d65773Smrg    unsigned long sav;
11328d65773Smrg    int res = ENODEV;
11428d65773Smrg
11528d65773Smrg    outb(0x01, 0xCFB);
11628d65773Smrg    sav = inl(0xCF8);
11728d65773Smrg    outl(0x80000000, 0xCF8);
11828d65773Smrg    if (inl(0xCF8) == 0x80000000)
11928d65773Smrg	res = 0;
12028d65773Smrg    outl(sav, 0xCF8);
12128d65773Smrg
12228d65773Smrg    return res;
12328d65773Smrg}
12428d65773Smrg
12528d65773Smrgstatic int
12628d65773Smrgpci_system_x86_conf1_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size)
12728d65773Smrg{
12828d65773Smrg    unsigned addr = 0xCFC + (reg & 3);
12928d65773Smrg    unsigned long sav;
13028d65773Smrg    int ret = 0;
13128d65773Smrg
13228d65773Smrg    if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3)
13328d65773Smrg	return EIO;
13428d65773Smrg
13528d65773Smrg    sav = inl(0xCF8);
13628d65773Smrg    outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8);
13728d65773Smrg    /* NOTE: x86 is already LE */
13828d65773Smrg    switch (size) {
13928d65773Smrg	case 1: {
14028d65773Smrg	    uint8_t *val = data;
14128d65773Smrg	    *val = inb(addr);
14228d65773Smrg	    break;
14328d65773Smrg	}
14428d65773Smrg	case 2: {
14528d65773Smrg	    uint16_t *val = data;
14628d65773Smrg	    *val = inw(addr);
14728d65773Smrg	    break;
14828d65773Smrg	}
14928d65773Smrg	case 4: {
15028d65773Smrg	    uint32_t *val = data;
15128d65773Smrg	    *val = inl(addr);
15228d65773Smrg	    break;
15328d65773Smrg	}
15428d65773Smrg    }
15528d65773Smrg    outl(sav, 0xCF8);
15628d65773Smrg
15728d65773Smrg    return ret;
15828d65773Smrg}
15928d65773Smrg
16028d65773Smrgstatic int
16128d65773Smrgpci_system_x86_conf1_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size)
16228d65773Smrg{
16328d65773Smrg    unsigned addr = 0xCFC + (reg & 3);
16428d65773Smrg    unsigned long sav;
16528d65773Smrg    int ret = 0;
16628d65773Smrg
16728d65773Smrg    if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 || size == 3)
16828d65773Smrg	return EIO;
16928d65773Smrg
17028d65773Smrg    sav = inl(0xCF8);
17128d65773Smrg    outl(0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), 0xCF8);
17228d65773Smrg    /* NOTE: x86 is already LE */
17328d65773Smrg    switch (size) {
17428d65773Smrg	case 1: {
17528d65773Smrg	    const uint8_t *val = data;
17628d65773Smrg	    outb(*val, addr);
17728d65773Smrg	    break;
17828d65773Smrg	}
17928d65773Smrg	case 2: {
18028d65773Smrg	    const uint16_t *val = data;
18128d65773Smrg	    outw(*val, addr);
18228d65773Smrg	    break;
18328d65773Smrg	}
18428d65773Smrg	case 4: {
18528d65773Smrg	    const uint32_t *val = data;
18628d65773Smrg	    outl(*val, addr);
18728d65773Smrg	    break;
18828d65773Smrg	}
18928d65773Smrg    }
19028d65773Smrg    outl(sav, 0xCF8);
19128d65773Smrg
19228d65773Smrg    return ret;
19328d65773Smrg}
19428d65773Smrg
19528d65773Smrgstatic int
19628d65773Smrgpci_system_x86_conf2_probe(void)
19728d65773Smrg{
19828d65773Smrg    outb(0, 0xCFB);
19928d65773Smrg    outb(0, 0xCF8);
20028d65773Smrg    outb(0, 0xCFA);
20128d65773Smrg    if (inb(0xCF8) == 0 && inb(0xCFA) == 0)
20228d65773Smrg	return 0;
20328d65773Smrg
20428d65773Smrg    return ENODEV;
20528d65773Smrg}
20628d65773Smrg
20728d65773Smrgstatic int
20828d65773Smrgpci_system_x86_conf2_read(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size)
20928d65773Smrg{
21028d65773Smrg    unsigned addr = 0xC000 | dev << 8 | reg;
21128d65773Smrg    int ret = 0;
21228d65773Smrg
21328d65773Smrg    if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
21428d65773Smrg	return EIO;
21528d65773Smrg
21628d65773Smrg    outb((func << 1) | 0xF0, 0xCF8);
21728d65773Smrg    outb(bus, 0xCFA);
21828d65773Smrg    /* NOTE: x86 is already LE */
21928d65773Smrg    switch (size) {
22028d65773Smrg	case 1: {
22128d65773Smrg	    uint8_t *val = data;
22228d65773Smrg	    *val = inb(addr);
22328d65773Smrg	    break;
22428d65773Smrg	}
22528d65773Smrg	case 2: {
22628d65773Smrg	    uint16_t *val = data;
22728d65773Smrg	    *val = inw(addr);
22828d65773Smrg	    break;
22928d65773Smrg	}
23028d65773Smrg	case 4: {
23128d65773Smrg	    uint32_t *val = data;
23228d65773Smrg	    *val = inl(addr);
23328d65773Smrg	    break;
23428d65773Smrg	}
23528d65773Smrg	default:
23628d65773Smrg	    ret = EIO;
23728d65773Smrg	    break;
23828d65773Smrg    }
23928d65773Smrg    outb(0, 0xCF8);
24028d65773Smrg
24128d65773Smrg    return ret;
24228d65773Smrg}
24328d65773Smrg
24428d65773Smrgstatic int
24528d65773Smrgpci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size)
24628d65773Smrg{
24728d65773Smrg    unsigned addr = 0xC000 | dev << 8 | reg;
24828d65773Smrg    int ret = 0;
24928d65773Smrg
25028d65773Smrg    if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100)
25128d65773Smrg	return EIO;
25228d65773Smrg
25328d65773Smrg    outb((func << 1) | 0xF0, 0xCF8);
25428d65773Smrg    outb(bus, 0xCFA);
25528d65773Smrg    /* NOTE: x86 is already LE */
25628d65773Smrg    switch (size) {
25728d65773Smrg	case 1: {
25828d65773Smrg	    const uint8_t *val = data;
25928d65773Smrg	    outb(*val, addr);
26028d65773Smrg	    break;
26128d65773Smrg	}
26228d65773Smrg	case 2: {
26328d65773Smrg	    const uint16_t *val = data;
26428d65773Smrg	    outw(*val, addr);
26528d65773Smrg	    break;
26628d65773Smrg	}
26728d65773Smrg	case 4: {
26828d65773Smrg	    const uint32_t *val = data;
26928d65773Smrg	    outl(*val, addr);
27028d65773Smrg	    break;
27128d65773Smrg	}
27228d65773Smrg	default:
27328d65773Smrg	    ret = EIO;
27428d65773Smrg	    break;
27528d65773Smrg    }
27628d65773Smrg    outb(0, 0xCF8);
27728d65773Smrg
27828d65773Smrg    return ret;
27928d65773Smrg}
28028d65773Smrg
28128d65773Smrg/* Check that this really looks like a PCI configuration. */
28228d65773Smrgstatic int
28328d65773Smrgpci_system_x86_check(struct pci_system_x86 *pci_sys_x86)
28428d65773Smrg{
28528d65773Smrg    int dev;
28628d65773Smrg    uint16_t class, vendor;
28728d65773Smrg
28828d65773Smrg    /* Look on bus 0 for a device that is a host bridge, a VGA card,
28928d65773Smrg     * or an intel or compaq device.  */
29028d65773Smrg
29128d65773Smrg    for (dev = 0; dev < 32; dev++) {
29228d65773Smrg	if (pci_sys_x86->read(0, dev, 0, PCI_CLASS_DEVICE, &class, sizeof(class)))
29328d65773Smrg	    continue;
29428d65773Smrg	if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA)
29528d65773Smrg	    return 0;
29628d65773Smrg	if (pci_sys_x86->read(0, dev, 0, PCI_VENDOR_ID, &vendor, sizeof(vendor)))
29728d65773Smrg	    continue;
29828d65773Smrg	if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ)
29928d65773Smrg	    return 0;
30028d65773Smrg    }
30128d65773Smrg
30228d65773Smrg    return ENODEV;
30328d65773Smrg}
30428d65773Smrg
30528d65773Smrgstatic int
30628d65773Smrgpci_nfuncs(struct pci_system_x86 *pci_sys_x86, int bus, int dev)
30728d65773Smrg{
30828d65773Smrg    uint8_t hdr;
30928d65773Smrg    int err;
31028d65773Smrg
31128d65773Smrg    err = pci_sys_x86->read(bus, dev, 0, PCI_HDRTYPE, &hdr, sizeof(hdr));
31228d65773Smrg
31328d65773Smrg    if (err)
31428d65773Smrg	return err;
31528d65773Smrg
31628d65773Smrg    return hdr & 0x80 ? 8 : 1;
31728d65773Smrg}
31828d65773Smrg
31928d65773Smrg/**
32028d65773Smrg * Read a VGA rom using the 0xc0000 mapping.
32128d65773Smrg */
32228d65773Smrgstatic int
32328d65773Smrgpci_device_x86_read_rom(struct pci_device *dev, void *buffer)
32428d65773Smrg{
32528d65773Smrg    void *bios;
32628d65773Smrg    int memfd;
32728d65773Smrg
32828d65773Smrg    if ((dev->device_class & 0x00ffff00) !=
32928d65773Smrg	 ((PCIC_DISPLAY << 16) | ( PCIS_DISPLAY_VGA << 8))) {
33028d65773Smrg	return ENOSYS;
33128d65773Smrg    }
33228d65773Smrg
333cad31331Smrg    memfd = open("/dev/mem", O_RDONLY | O_CLOEXEC);
33428d65773Smrg    if (memfd == -1)
33528d65773Smrg	return errno;
33628d65773Smrg
33728d65773Smrg    bios = mmap(NULL, dev->rom_size, PROT_READ, 0, memfd, 0xc0000);
33828d65773Smrg    if (bios == MAP_FAILED) {
33928d65773Smrg	close(memfd);
34028d65773Smrg	return errno;
34128d65773Smrg    }
34228d65773Smrg
34328d65773Smrg    memcpy(buffer, bios, dev->rom_size);
34428d65773Smrg
34528d65773Smrg    munmap(bios, dev->rom_size);
34628d65773Smrg    close(memfd);
34728d65773Smrg
34828d65773Smrg    return 0;
34928d65773Smrg}
35028d65773Smrg
35128d65773Smrg/** Returns the number of regions (base address registers) the device has */
35228d65773Smrgstatic int
35328d65773Smrgpci_device_x86_get_num_regions(uint8_t header_type)
35428d65773Smrg{
35528d65773Smrg    switch (header_type & 0x7f) {
35628d65773Smrg	case 0:
35728d65773Smrg	    return 6;
35828d65773Smrg	case 1:
35928d65773Smrg	    return 2;
36028d65773Smrg	case 2:
36128d65773Smrg	    return 1;
36228d65773Smrg	default:
36328d65773Smrg	    fprintf(stderr,"unknown header type %02x\n", header_type);
36428d65773Smrg	    return 0;
36528d65773Smrg    }
36628d65773Smrg}
36728d65773Smrg
36828d65773Smrg/** Masks out the flag bigs of the base address register value */
36928d65773Smrgstatic uint32_t
37028d65773Smrgget_map_base( uint32_t val )
37128d65773Smrg{
37228d65773Smrg    if (val & 0x01)
37328d65773Smrg	return val & ~0x03;
37428d65773Smrg    else
37528d65773Smrg	return val & ~0x0f;
37628d65773Smrg}
37728d65773Smrg
37828d65773Smrg/** Returns the size of a region based on the all-ones test value */
37928d65773Smrgstatic unsigned
38028d65773Smrgget_test_val_size( uint32_t testval )
38128d65773Smrg{
38228d65773Smrg    unsigned size = 1;
38328d65773Smrg
38428d65773Smrg    if (testval == 0)
38528d65773Smrg	return 0;
38628d65773Smrg
38728d65773Smrg    /* Mask out the flag bits */
38828d65773Smrg    testval = get_map_base( testval );
38928d65773Smrg    if (!testval)
39028d65773Smrg	return 0;
39128d65773Smrg
39228d65773Smrg    while ((testval & 1) == 0) {
39328d65773Smrg	size <<= 1;
39428d65773Smrg	testval >>= 1;
39528d65773Smrg    }
39628d65773Smrg
39728d65773Smrg    return size;
39828d65773Smrg}
39928d65773Smrg
40028d65773Smrgstatic int
40128d65773Smrgpci_device_x86_probe(struct pci_device *dev)
40228d65773Smrg{
40328d65773Smrg    uint8_t irq, hdrtype;
40428d65773Smrg    int err, i, bar;
40528d65773Smrg
40628d65773Smrg    /* Many of the fields were filled in during initial device enumeration.
40728d65773Smrg     * At this point, we need to fill in regions, rom_size, and irq.
40828d65773Smrg     */
40928d65773Smrg
41028d65773Smrg    err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ);
41128d65773Smrg    if (err)
41228d65773Smrg	return err;
41328d65773Smrg    dev->irq = irq;
41428d65773Smrg
41528d65773Smrg    err = pci_device_cfg_read_u8(dev, &hdrtype, PCI_HDRTYPE);
41628d65773Smrg    if (err)
41728d65773Smrg	return err;
41828d65773Smrg
41928d65773Smrg    bar = 0x10;
42028d65773Smrg    for (i = 0; i < pci_device_x86_get_num_regions(hdrtype); i++, bar += 4) {
42128d65773Smrg	uint32_t addr, testval;
42228d65773Smrg
42328d65773Smrg	/* Get the base address */
42428d65773Smrg	err = pci_device_cfg_read_u32(dev, &addr, bar);
42528d65773Smrg	if (err != 0)
42628d65773Smrg	    continue;
42728d65773Smrg
42828d65773Smrg	/* Test write all ones to the register, then restore it. */
42928d65773Smrg	err = pci_device_cfg_write_u32(dev, 0xffffffff, bar);
43028d65773Smrg	if (err != 0)
43128d65773Smrg	    continue;
43228d65773Smrg	pci_device_cfg_read_u32(dev, &testval, bar);
43328d65773Smrg	err = pci_device_cfg_write_u32(dev, addr, bar);
43428d65773Smrg
43528d65773Smrg	if (addr & 0x01)
43628d65773Smrg	    dev->regions[i].is_IO = 1;
43728d65773Smrg	if (addr & 0x04)
43828d65773Smrg	    dev->regions[i].is_64 = 1;
43928d65773Smrg	if (addr & 0x08)
44028d65773Smrg	    dev->regions[i].is_prefetchable = 1;
44128d65773Smrg
44228d65773Smrg	/* Set the size */
44328d65773Smrg	dev->regions[i].size = get_test_val_size(testval);
44428d65773Smrg
44528d65773Smrg	/* Set the base address value */
44628d65773Smrg	if (dev->regions[i].is_64) {
44728d65773Smrg	    uint32_t top;
44828d65773Smrg
44928d65773Smrg	    err = pci_device_cfg_read_u32(dev, &top, bar + 4);
45028d65773Smrg	    if (err != 0)
45128d65773Smrg		continue;
45228d65773Smrg
45328d65773Smrg	    dev->regions[i].base_addr = ((uint64_t)top << 32) |
45428d65773Smrg					get_map_base(addr);
45528d65773Smrg	    bar += 4;
45628d65773Smrg	    i++;
45728d65773Smrg	} else {
45828d65773Smrg	    dev->regions[i].base_addr = get_map_base(addr);
45928d65773Smrg	}
46028d65773Smrg    }
46128d65773Smrg
46228d65773Smrg    /* If it's a VGA device, set up the rom size for read_rom using the
46328d65773Smrg     * 0xc0000 mapping.
46428d65773Smrg     */
46528d65773Smrg    if ((dev->device_class & 0x00ffff00) ==
46628d65773Smrg	((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8)))
46728d65773Smrg    {
46828d65773Smrg	dev->rom_size = 64 * 1024;
46928d65773Smrg    }
47028d65773Smrg
47128d65773Smrg    return 0;
47228d65773Smrg}
47328d65773Smrg
47428d65773Smrgstatic int
47528d65773Smrgpci_device_x86_map_range(struct pci_device *dev,
47628d65773Smrg    struct pci_device_mapping *map)
47728d65773Smrg{
478cad31331Smrg    int memfd = open("/dev/mem", O_RDWR | O_CLOEXEC);
47928d65773Smrg    int prot = PROT_READ;
48028d65773Smrg
48128d65773Smrg    if (memfd == -1)
48228d65773Smrg	return errno;
48328d65773Smrg
48428d65773Smrg    if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE)
48528d65773Smrg	prot |= PROT_WRITE;
48628d65773Smrg
48728d65773Smrg    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, memfd, map->base);
48828d65773Smrg    close(memfd);
48928d65773Smrg    if (map->memory == MAP_FAILED)
49028d65773Smrg	return errno;
49128d65773Smrg
49228d65773Smrg    return 0;
49328d65773Smrg}
49428d65773Smrg
49528d65773Smrgstatic int
49628d65773Smrgpci_device_x86_read(struct pci_device *dev, void *data,
49728d65773Smrg    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
49828d65773Smrg{
49928d65773Smrg    struct pci_system_x86 *pci_sys_x86 = (struct pci_system_x86 *) pci_sys;
50028d65773Smrg    int err;
50128d65773Smrg
50228d65773Smrg    *bytes_read = 0;
50328d65773Smrg    while (size > 0) {
50428d65773Smrg	int toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
50528d65773Smrg	if (toread > size)
50628d65773Smrg	    toread = size;
50728d65773Smrg
50828d65773Smrg	err = pci_sys_x86->read(dev->bus, dev->dev, dev->func, offset, data, toread);
50928d65773Smrg	if (err)
51028d65773Smrg	    return err;
51128d65773Smrg
51228d65773Smrg	offset += toread;
51328d65773Smrg	data = (char*)data + toread;
51428d65773Smrg	size -= toread;
51528d65773Smrg	*bytes_read += toread;
51628d65773Smrg    }
51728d65773Smrg    return 0;
51828d65773Smrg}
51928d65773Smrg
52028d65773Smrgstatic int
52128d65773Smrgpci_device_x86_write(struct pci_device *dev, const void *data,
52228d65773Smrg    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
52328d65773Smrg{
52428d65773Smrg    struct pci_system_x86 *pci_sys_x86 = (struct pci_system_x86 *) pci_sys;
52528d65773Smrg    int err;
52628d65773Smrg
52728d65773Smrg    *bytes_written = 0;
52828d65773Smrg    while (size > 0) {
52928d65773Smrg	int towrite = 4;
53028d65773Smrg	if (towrite > size)
53128d65773Smrg	    towrite = size;
53228d65773Smrg	if (towrite > 4 - (offset & 0x3))
53328d65773Smrg	    towrite = 4 - (offset & 0x3);
53428d65773Smrg
53528d65773Smrg	err = pci_sys_x86->write(dev->bus, dev->dev, dev->func, offset, data, towrite);
53628d65773Smrg	if (err)
53728d65773Smrg	    return err;
53828d65773Smrg
53928d65773Smrg	offset += towrite;
54028d65773Smrg	data = (const char*)data + towrite;
54128d65773Smrg	size -= towrite;
54228d65773Smrg	*bytes_written += towrite;
54328d65773Smrg    }
54428d65773Smrg    return 0;
54528d65773Smrg}
54628d65773Smrg
54728d65773Smrgstatic void
54828d65773Smrgpci_system_x86_destroy(void)
54928d65773Smrg{
55028d65773Smrg    x86_disable_io();
55128d65773Smrg}
55228d65773Smrg
55386ea1d58Smrgstatic struct pci_io_handle *
55486ea1d58Smrgpci_device_x86_open_legacy_io(struct pci_io_handle *ret,
55586ea1d58Smrg    struct pci_device *dev, pciaddr_t base, pciaddr_t size)
55686ea1d58Smrg{
55786ea1d58Smrg    x86_enable_io();
55886ea1d58Smrg
55986ea1d58Smrg    ret->base = base;
56086ea1d58Smrg    ret->size = size;
56186ea1d58Smrg
56286ea1d58Smrg    return ret;
56386ea1d58Smrg}
56486ea1d58Smrg
56586ea1d58Smrgstatic void
56686ea1d58Smrgpci_device_x86_close_io(struct pci_device *dev, struct pci_io_handle *handle)
56786ea1d58Smrg{
56886ea1d58Smrg    /* Like in the Linux case, do not disable I/O, as it may be opened several
56986ea1d58Smrg     * times, and closed fewer times. */
57086ea1d58Smrg    /* x86_disable_io(); */
57186ea1d58Smrg}
57286ea1d58Smrg
57386ea1d58Smrgstatic uint32_t
57486ea1d58Smrgpci_device_x86_read32(struct pci_io_handle *handle, uint32_t reg)
57586ea1d58Smrg{
57686ea1d58Smrg    return inl(reg + handle->base);
57786ea1d58Smrg}
57886ea1d58Smrg
57986ea1d58Smrgstatic uint16_t
58086ea1d58Smrgpci_device_x86_read16(struct pci_io_handle *handle, uint32_t reg)
58186ea1d58Smrg{
58286ea1d58Smrg    return inw(reg + handle->base);
58386ea1d58Smrg}
58486ea1d58Smrg
58586ea1d58Smrgstatic uint8_t
58686ea1d58Smrgpci_device_x86_read8(struct pci_io_handle *handle, uint32_t reg)
58786ea1d58Smrg{
58886ea1d58Smrg    return inb(reg + handle->base);
58986ea1d58Smrg}
59086ea1d58Smrg
59186ea1d58Smrgstatic void
59286ea1d58Smrgpci_device_x86_write32(struct pci_io_handle *handle, uint32_t reg,
59386ea1d58Smrg		       uint32_t data)
59486ea1d58Smrg{
59586ea1d58Smrg    outl(data, reg + handle->base);
59686ea1d58Smrg}
59786ea1d58Smrg
59886ea1d58Smrgstatic void
59986ea1d58Smrgpci_device_x86_write16(struct pci_io_handle *handle, uint32_t reg,
60086ea1d58Smrg		       uint16_t data)
60186ea1d58Smrg{
60286ea1d58Smrg    outw(data, reg + handle->base);
60386ea1d58Smrg}
60486ea1d58Smrg
60586ea1d58Smrgstatic void
60686ea1d58Smrgpci_device_x86_write8(struct pci_io_handle *handle, uint32_t reg,
60786ea1d58Smrg		      uint8_t data)
60886ea1d58Smrg{
60986ea1d58Smrg    outb(data, reg + handle->base);
61086ea1d58Smrg}
61186ea1d58Smrg
61286ea1d58Smrgstatic int
61386ea1d58Smrgpci_device_x86_map_legacy(struct pci_device *dev, pciaddr_t base,
61486ea1d58Smrg    pciaddr_t size, unsigned map_flags, void **addr)
61586ea1d58Smrg{
61686ea1d58Smrg    struct pci_device_mapping map;
61786ea1d58Smrg    int err;
61886ea1d58Smrg
61986ea1d58Smrg    map.base = base;
62086ea1d58Smrg    map.size = size;
62186ea1d58Smrg    map.flags = map_flags;
62286ea1d58Smrg    err = pci_device_x86_map_range(dev, &map);
62386ea1d58Smrg    *addr = map.memory;
62486ea1d58Smrg
62586ea1d58Smrg    return err;
62686ea1d58Smrg}
62786ea1d58Smrg
62886ea1d58Smrgstatic int
62986ea1d58Smrgpci_device_x86_unmap_legacy(struct pci_device *dev, void *addr,
63086ea1d58Smrg    pciaddr_t size)
63186ea1d58Smrg{
63286ea1d58Smrg    struct pci_device_mapping map;
63386ea1d58Smrg
63486ea1d58Smrg    map.size = size;
63586ea1d58Smrg    map.flags = 0;
63686ea1d58Smrg    map.memory = addr;
63786ea1d58Smrg
63886ea1d58Smrg    return pci_device_generic_unmap_range(dev, &map);
63986ea1d58Smrg}
64086ea1d58Smrg
64128d65773Smrgstatic const struct pci_system_methods x86_pci_methods = {
64228d65773Smrg    .destroy = pci_system_x86_destroy,
64328d65773Smrg    .read_rom = pci_device_x86_read_rom,
64428d65773Smrg    .probe = pci_device_x86_probe,
64528d65773Smrg    .map_range = pci_device_x86_map_range,
64628d65773Smrg    .unmap_range = pci_device_generic_unmap_range,
64728d65773Smrg    .read = pci_device_x86_read,
64828d65773Smrg    .write = pci_device_x86_write,
64928d65773Smrg    .fill_capabilities = pci_fill_capabilities_generic,
65086ea1d58Smrg    .open_legacy_io = pci_device_x86_open_legacy_io,
65186ea1d58Smrg    .close_io = pci_device_x86_close_io,
65286ea1d58Smrg    .read32 = pci_device_x86_read32,
65386ea1d58Smrg    .read16 = pci_device_x86_read16,
65486ea1d58Smrg    .read8 = pci_device_x86_read8,
65586ea1d58Smrg    .write32 = pci_device_x86_write32,
65686ea1d58Smrg    .write16 = pci_device_x86_write16,
65786ea1d58Smrg    .write8 = pci_device_x86_write8,
65886ea1d58Smrg    .map_legacy = pci_device_x86_map_legacy,
65986ea1d58Smrg    .unmap_legacy = pci_device_x86_unmap_legacy,
66028d65773Smrg};
66128d65773Smrg
66228d65773Smrgstatic int pci_probe(struct pci_system_x86 *pci_sys_x86)
66328d65773Smrg{
66428d65773Smrg    if (pci_system_x86_conf1_probe() == 0) {
66528d65773Smrg	pci_sys_x86->read = pci_system_x86_conf1_read;
66628d65773Smrg	pci_sys_x86->write = pci_system_x86_conf1_write;
66728d65773Smrg	if (pci_system_x86_check(pci_sys_x86) == 0)
66828d65773Smrg	    return 0;
66928d65773Smrg    }
67028d65773Smrg
67128d65773Smrg    if (pci_system_x86_conf2_probe() == 0) {
67228d65773Smrg	pci_sys_x86->read = pci_system_x86_conf2_read;
67328d65773Smrg	pci_sys_x86->write = pci_system_x86_conf2_write;
67428d65773Smrg	if (pci_system_x86_check(pci_sys_x86) == 0)
67528d65773Smrg	    return 0;
67628d65773Smrg    }
67728d65773Smrg
67828d65773Smrg    return ENODEV;
67928d65773Smrg}
68028d65773Smrg
68128d65773Smrg_pci_hidden int
68228d65773Smrgpci_system_x86_create(void)
68328d65773Smrg{
68428d65773Smrg    struct pci_device_private *device;
68528d65773Smrg    int ret, bus, dev, ndevs, func, nfuncs;
68628d65773Smrg    struct pci_system_x86 *pci_sys_x86;
68728d65773Smrg    uint32_t reg;
68828d65773Smrg
68928d65773Smrg    ret = x86_enable_io();
69028d65773Smrg    if (ret)
69128d65773Smrg	return ret;
69228d65773Smrg
69328d65773Smrg    pci_sys_x86 = calloc(1, sizeof(struct pci_system_x86));
69428d65773Smrg    if (pci_sys_x86 == NULL) {
69528d65773Smrg	x86_disable_io();
69628d65773Smrg	return ENOMEM;
69728d65773Smrg    }
69828d65773Smrg    pci_sys = &pci_sys_x86->system;
69928d65773Smrg
70028d65773Smrg    ret = pci_probe(pci_sys_x86);
70128d65773Smrg    if (ret) {
70228d65773Smrg	x86_disable_io();
70328d65773Smrg	free(pci_sys_x86);
70428d65773Smrg	pci_sys = NULL;
70528d65773Smrg	return ret;
70628d65773Smrg    }
70728d65773Smrg
70828d65773Smrg    pci_sys->methods = &x86_pci_methods;
70928d65773Smrg
71028d65773Smrg    ndevs = 0;
71128d65773Smrg    for (bus = 0; bus < 256; bus++) {
71228d65773Smrg	for (dev = 0; dev < 32; dev++) {
71328d65773Smrg	    nfuncs = pci_nfuncs(pci_sys_x86, bus, dev);
71428d65773Smrg	    for (func = 0; func < nfuncs; func++) {
71528d65773Smrg		if (pci_sys_x86->read(bus, dev, func, PCI_VENDOR_ID, &reg, sizeof(reg)) != 0)
71628d65773Smrg		    continue;
71728d65773Smrg		if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
71828d65773Smrg		    PCI_VENDOR(reg) == 0)
71928d65773Smrg		    continue;
72028d65773Smrg		ndevs++;
72128d65773Smrg	    }
72228d65773Smrg	}
72328d65773Smrg    }
72428d65773Smrg
72528d65773Smrg    pci_sys->num_devices = ndevs;
72628d65773Smrg    pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
72728d65773Smrg    if (pci_sys->devices == NULL) {
72828d65773Smrg	x86_disable_io();
72928d65773Smrg	free(pci_sys_x86);
73028d65773Smrg	pci_sys = NULL;
73128d65773Smrg	return ENOMEM;
73228d65773Smrg    }
73328d65773Smrg
73428d65773Smrg    device = pci_sys->devices;
73528d65773Smrg    for (bus = 0; bus < 256; bus++) {
73628d65773Smrg	for (dev = 0; dev < 32; dev++) {
73728d65773Smrg	    nfuncs = pci_nfuncs(pci_sys_x86, bus, dev);
73828d65773Smrg	    for (func = 0; func < nfuncs; func++) {
73928d65773Smrg		if (pci_sys_x86->read(bus, dev, func, PCI_VENDOR_ID, &reg, sizeof(reg)) != 0)
74028d65773Smrg		    continue;
74128d65773Smrg		if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
74228d65773Smrg		    PCI_VENDOR(reg) == 0)
74328d65773Smrg		    continue;
74428d65773Smrg		device->base.domain = 0;
74528d65773Smrg		device->base.bus = bus;
74628d65773Smrg		device->base.dev = dev;
74728d65773Smrg		device->base.func = func;
74828d65773Smrg		device->base.vendor_id = PCI_VENDOR(reg);
74928d65773Smrg		device->base.device_id = PCI_DEVICE(reg);
75028d65773Smrg
75128d65773Smrg		if (pci_sys_x86->read(bus, dev, func, PCI_CLASS, &reg, sizeof(reg)) != 0)
75228d65773Smrg		    continue;
75328d65773Smrg		device->base.device_class = reg >> 8;
75428d65773Smrg		device->base.revision = reg & 0xFF;
75528d65773Smrg
75628d65773Smrg		if (pci_sys_x86->read(bus, dev, func, PCI_SUB_VENDOR_ID, &reg, sizeof(reg)) != 0)
75728d65773Smrg		    continue;
75828d65773Smrg		device->base.subvendor_id = PCI_VENDOR(reg);
75928d65773Smrg		device->base.subdevice_id = PCI_DEVICE(reg);
76028d65773Smrg
76128d65773Smrg		device++;
76228d65773Smrg	    }
76328d65773Smrg	}
76428d65773Smrg    }
76528d65773Smrg
76628d65773Smrg    return 0;
76728d65773Smrg}
768