diff -urN qemu.orig/Makefile.target qemu/Makefile.target --- qemu.orig/Makefile.target 2004-06-03 16:30:57.000000000 +0100 +++ qemu/Makefile.target 2004-06-03 16:31:34.000000000 +0100 @@ -232,7 +232,7 @@ endif # must use static linking to avoid leaving stuff in virtual address space -VL_OBJS=vl.o osdep.o block.o monitor.o pci.o +VL_OBJS=vl.o osdep.o block.o monitor.o pci.o pciproxy.o ifeq ($(TARGET_ARCH), i386) # Hardware support diff -urN qemu.orig/configure qemu/configure --- qemu.orig/configure 2004-06-03 16:30:57.000000000 +0100 +++ qemu/configure 2004-06-03 16:31:13.000000000 +0100 @@ -390,6 +390,11 @@ elif test -f "/usr/include/byteswap.h" ; then echo "#define HAVE_BYTESWAP_H 1" >> $config_h fi +if test -f "/usr/include/sys/io.h"; then + echo "#define HAVE_SYS_IO_H 1" >> $config_h + echo "#define HAVE_IOPL 1" >> $config_h + echo "#define HAVE_IOPERM 1" >> $config_h +fi if test "$gdbstub" = "yes" ; then echo "CONFIG_GDBSTUB=yes" >> $config_mak echo "#define CONFIG_GDBSTUB 1" >> $config_h diff -urN qemu.orig/hw/pc.c qemu/hw/pc.c --- qemu.orig/hw/pc.c 2004-06-03 16:30:57.000000000 +0100 +++ qemu/hw/pc.c 2004-06-03 16:31:42.000000000 +0100 @@ -389,6 +389,8 @@ if (pci_enabled) { i440fx_init(); piix3_init(); + if ( pciproxy_devpath ) + pciproxy_add_device(pciproxy_devpath); } /* init basic PC hardware */ diff -urN qemu.orig/hw/pciproxy.c qemu/hw/pciproxy.c --- qemu.orig/hw/pciproxy.c 1970-01-01 01:00:00.000000000 +0100 +++ qemu/hw/pciproxy.c 2004-06-03 17:23:03.000000000 +0100 @@ -0,0 +1,675 @@ +/* + * QEMU PCI Host proxy v0.2 + * Copyright (c) 2004 Gianni Tedesco + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This code is dedicated towards my muse, my inspiration, Broadcom Corporation. + * Without your spite I would not have been compelled to write this code. + * + * TODO: + * o Map IRQs, fixup PCIIOC_SIGIRQ. + * o Data logging support + * o Test with PCMCIA + * o AGP support +*/ + +static const char copyright[] = + "pciproxy.c: v0.2 Copyright (c) Gianni Tedesco 2004"; + +#include "vl.h" +#include +#include +#if HAVE_SYS_IO_H +#include +#endif + +#define PROC_DEVICES "/proc/bus/pci/devices" + +/* Ioctls for /proc/bus/pci/X/Y nodes. */ +#define PCIIOC_BASE ('P' << 24 | 'C' << 16 | 'I' << 8) +/* Get controller for PCI device. */ +#define PCIIOC_CONTROLLER (PCIIOC_BASE | 0x00) +/* Set mmap state to I/O space. */ +#define PCIIOC_MMAP_IS_IO (PCIIOC_BASE | 0x01) +/* Set mmap state to MEM space. */ +#define PCIIOC_MMAP_IS_MEM (PCIIOC_BASE | 0x02) +/* Enable/disable write-combining. */ +#define PCIIOC_WRITE_COMBINE (PCIIOC_BASE | 0x03) +/* Send user-defined signal for irqs */ +#define PCIIOC_SIGIRQ (PCIIOC_BASE | 0x04) +struct pci_sigirq { + int sig; + void *ptr; +}; + +struct pciproxy { + struct PCIDevice pcidev; + uint32_t len[PCI_NUM_REGIONS]; + uint32_t qemu_map[PCI_NUM_REGIONS]; + void *map[PCI_NUM_REGIONS]; + uint32_t io_allowed; + uint32_t bar[PCI_NUM_REGIONS]; + int dev, bus, fn; + int fd; + int iomem_region; +}; + +static __inline__ const char *sys_err(void) +{ + return strerror(errno); +} + +static int easy_explode(char *str, char split, char **toks, int max_toks) +{ + char *tmp; + int tok; + int state; + + for(tmp=str,state=tok=0; *tmp && tok < max_toks; tmp++) { + if ( state == 0 ) { + if ( *tmp == split ) { + toks[tok++] = NULL; + }else if ( !isspace(*tmp) ) { + state = 1; + toks[tok++] = tmp; + } + }else if ( state == 1 ) { + if ( *tmp == split || isspace(*tmp) ) { + *tmp = '\0'; + state = 0; + } + } + } + + return tok; +} + +static int resource_lookup(struct pciproxy *pci, + target_phys_addr_t begin, + target_phys_addr_t end, + int type) +{ + int i; + + for(i=0; i < PCI_NUM_REGIONS; i++) { + target_phys_addr_t b, e; + + if ( pci->bar[i] == 0 ) + continue; + if ( type != PCI_ADDRESS_SPACE_IO ) { + if ( i != PCI_ROM_SLOT && + pci->bar[i] & PCI_ADDRESS_SPACE_IO ) + continue; + }else{ + if ( !(pci->bar[i] & PCI_ADDRESS_SPACE_IO) ) + continue; + } + + b = pci->qemu_map[i]; + e = b + pci->len[i]; + + if ( begin >= b && end <= e ) { + return i; + } + } + + fprintf(stderr, "resource_lookup: %x/%u not found\n", begin, end-begin); + return -1; +} + +static uint32_t mem_read(struct pciproxy *pci, target_phys_addr_t addr, int len) +{ + uint32_t ret, ofs; + int i; + + i = resource_lookup(pci, addr, addr + len, PCI_ADDRESS_SPACE_MEM); + if ( i < 0 ) + return 0xffffffff; + + ofs = addr - pci->qemu_map[i]; + switch ( len ) { + case 1: + ret = *(uint8_t *)(pci->map[i] + ofs); + break; + case 2: + ret = *(uint16_t *)(pci->map[i] + ofs); + break; + case 4: + ret = *(uint32_t *)(pci->map[i] + ofs); + break; + default: + return 0xffffffff; + } + + return ret; +} + +static void mem_write(struct pciproxy *pci, + target_phys_addr_t addr, + int len, uint32_t value) +{ + uint32_t ofs; + int i; + + i = resource_lookup(pci, addr, addr + len, PCI_ADDRESS_SPACE_MEM); + if ( i < 0 ) + return; + + ofs = addr - pci->qemu_map[i]; + switch ( len ) { + case 1: + *(uint8_t *)(pci->map[i] + ofs) = value; + break; + case 2: + *(uint16_t *)(pci->map[i] + ofs) = value; + break; + case 4: + *(uint32_t *)(pci->map[i] + ofs) = value; + break; + default: + return; + } +} + +static uint32_t mem_readb(void *ptr, target_phys_addr_t addr) +{ + return mem_read(ptr, addr, 1); +} +static uint32_t mem_readw(void *ptr, target_phys_addr_t addr) +{ + return mem_read(ptr, addr, 2); +} +static uint32_t mem_readl(void *ptr, target_phys_addr_t addr) +{ + return mem_read(ptr, addr, 4); +} +static void mem_writeb(void *ptr, target_phys_addr_t addr, uint32_t value) +{ + return mem_write(ptr, addr, 1, value); +} +static void mem_writew(void *ptr, target_phys_addr_t addr, uint32_t value) +{ + return mem_write(ptr, addr, 2, value); +} +static void mem_writel(void *ptr, target_phys_addr_t addr, uint32_t value) +{ + return mem_write(ptr, addr, 4, value); +} + +static CPUReadMemoryFunc *cpu_callback_read[3]={ + mem_readb, + mem_readw, + mem_readl, +}; +static CPUWriteMemoryFunc *cpu_callback_write[3]={ + mem_writeb, + mem_writew, + mem_writel, +}; + +static int register_cbmem(struct pciproxy *pci, int i, + target_phys_addr_t start_addr) +{ + unsigned long size; + + size = (pci->len[i] + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; + + if ( pci->iomem_region == 0 ) + pci->iomem_region = cpu_register_io_memory(0, + cpu_callback_read, + cpu_callback_write, + pci); + + cpu_register_physical_memory(start_addr, size, pci->iomem_region); + + pci->qemu_map[i] = start_addr; + + return 1; +} + +/* Uses /proc/bus/devices to grab the length of the resources on our PCI + * device in a platform independant way. + */ +static int peek_resources(struct pciproxy *pci) +{ + FILE *f; + char buf[512]; + char *tok[18]; + uint32_t num; + int n, i; + + f = fopen(PROC_DEVICES, "r"); + if ( f == NULL ) { + fprintf(stderr, "%s: open(): %s\n", PROC_DEVICES, sys_err()); + return 0; + } + + while ( fgets(buf, sizeof(buf), f) ) { + char *ptr; + + ptr = strchr(buf, '\r'); + if ( ptr == NULL ) + ptr = strchr(buf, '\n'); + if ( ptr == NULL ) + break; + *ptr = '\0'; + + n = easy_explode(buf, '\0', tok, 18); + if ( n < 17 ) + continue; + + num = strtoul(tok[0], NULL, 16); + if ( pci->bus != ((num & 0xff00) >> 8) ) + continue; + if ( pci->dev != ((num & 0x00ff) >> 3) ) + continue; + if ( pci->fn != (num & 0x7) ) + continue; + + for(i=0; i < PCI_NUM_REGIONS; i++) { + num = strtoul(tok[10 + i], NULL, 16); + pci->len[i] = num; + } + + fclose(f); + return 1; + } + + fclose(f); + return 0; +} + +/* Final fallback path if mmap and ioperm fails, we just use iopl to raise + * our I/O priv level so that we can do any I/O we like. + */ +static int map_with_iopl(struct pciproxy *pci, int i) +{ +#if HAVE_IOPL + int ret; + + ret = iopl(3); + if ( ret == 0 ) { + pci->io_allowed |= (1<bus, pci->dev, pci->fn, i, sys_err()); +#endif + return 0; +} + +/* First fallback path if mmap failes, try to use ioperm for granular + * permission granting to the I/O ports. + */ +static int map_with_ioperm(struct pciproxy *pci, int i, uint32_t bar) +{ +#if HAVE_IOPERM + u_int32_t from, to; + int ret; + + from = bar & ~0x3; + to = from + pci->len[i]; + + ret = ioperm(from, to, 1); + if ( ret < 0 ) + return map_with_iopl(pci, i); + pci->io_allowed |= (1<bus, pci->dev, pci->fn, i, sys_err()); + } + + return 0; +#endif +} + + +static int resource_is_mapped(struct pciproxy *pci, int i) +{ + if ( pci->map[i] ) + return 1; + if ( pci->io_allowed & (1<len[i]; + bar = pci->bar[i]; + + if ( i == PCI_ROM_SLOT ) { + prot = PROT_READ; + }else{ + prot = PROT_READ|PROT_WRITE; + } + + if ( i == PCI_ROM_SLOT || !(bar & PCI_ADDRESS_SPACE_IO) ) { + flags = (bar & 0xf); + base = (bar & ~0xf); + ioc = PCIIOC_MMAP_IS_MEM; + }else{ + flags = (bar & 0x3); + base = (bar & ~0x3); + ioc = PCIIOC_MMAP_IS_IO; + } + + /* Round base address down to the nearest page boundary and length + * up to the the nearest page boundary in order that we may mmap + * the resource range. + * + * XXX: Assumes page size is a power of two. (big deal) + */ + pgmask = sysconf(_SC_PAGESIZE) - 1; + + ofs = base & pgmask; + base &= ~pgmask; + len += pgmask; + len &= ~pgmask; + + if ( ioctl(pci->fd, ioc) ) { + fprintf(stderr, "%.2x:%.2x.%x ioctl() slot %i: %s\n", + pci->bus, pci->dev, pci->fn, i, sys_err()); + return 0; + } + + map = mmap(NULL, len, prot, MAP_SHARED, pci->fd, base); + if ( (map == MAP_FAILED) ) { + int ret = 0 ; + + if ( i != PCI_ROM_SLOT && (flags & PCI_ADDRESS_SPACE_IO ) ) + ret = map_with_ioperm(pci, i, bar); + + if ( ret == 0 ) + fprintf(stderr, "%.2x:%.2x.%x mmap() slot %i: %s\n", + pci->bus, pci->dev, pci->fn, i, sys_err()); + + return ret; + } + + pci->map[i] = map + ofs; + return 1; +} + +/* Construct the filename of the /proc/bus/pci/XX/YY.Z file to open for our + * device and open it. + */ +static int open_proc_bus_pci(struct pciproxy *pci) +{ + char path[1024]; + ssize_t ret; + + snprintf(path, sizeof(path), "/proc/bus/pci/%.2x/%.2x.%x", + pci->bus, pci->dev, pci->fn); + + pci->fd = open(path, O_RDWR); + if ( pci->fd < 0 ) { + fprintf(stderr, "%s: open(): %s\n", path, sys_err()); + return 0; + } + + ret = pread(pci->fd, pci->pcidev.config, sizeof(pci->pcidev.config), 0); + if ( ret != (ssize_t)sizeof(pci->pcidev.config) ) { + fprintf(stderr, "%s: pread %u bytes of config space: %s\n", + path, sizeof(pci->pcidev.config), sys_err()); +intr: + if ( close(pci->fd) && errno == EINTR ) + goto intr; + return 0; + } + + /* Directly read the bar because on some architectures like PPC, the + * kernels mapped base doesn't correspond to the address we need + * to mmap on this FD. Also we need this info later on... + */ + pci->bar[0] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x10)); + pci->bar[1] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x14)); + pci->bar[2] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x18)); + pci->bar[3] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x1c)); + pci->bar[4] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x20)); + pci->bar[5] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x24)); + pci->bar[6] = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x30)); + + return 1; +} + +static void iowrite(void *ptr, uint32_t addr, uint32_t data, uint32_t len) +{ + struct pciproxy *pci = (struct pciproxy *)ptr; + uint32_t ofs; + int i; + + i = resource_lookup(pci, addr, addr + len, PCI_ADDRESS_SPACE_IO); + if ( i < 0 ) + return; + + ofs = addr - pci->qemu_map[i]; + addr = pci->bar[i] & ~0x03; + + if ( pci->map[i] ) { + switch( len ) { + case 1: + *(uint8_t *)(pci->map[i] + ofs) = data; + break; + case 2: + *(uint16_t *)(pci->map[i] + ofs) = data; + break; + case 4: + *(uint32_t *)(pci->map[i] + ofs) = data; + break; + default: + return; + } + }else{ +#if HAVE_SYS_IO_H + if ( !(pci->io_allowed & (1<qemu_map[i]; + addr = pci->bar[i] & ~0x03; + + if ( pci->map[i] ) { + switch( len ) { + case 1: + ret = *(uint8_t *)(pci->map[i] + ofs); + break; + case 2: + ret = *(uint16_t *)(pci->map[i] + ofs); + break; + case 4: + ret = *(uint32_t *)(pci->map[i] + ofs); + break; + default: + return 0xffffffff; + } +#if HAVE_SYS_IO_H + }else{ + if ( !(pci->io_allowed & (1<len[i]; + + if ( !resource_is_mapped(pci, i) && + !map_resource(pci, i) ) + return; + + pci->qemu_map[i] = addr; + + register_ioport_read(addr, len, 4, ioreadl, pci); + register_ioport_read(addr, len, 2, ioreadw, pci); + register_ioport_read(addr, len, 1, ioreadb, pci); + register_ioport_write(addr, len, 4, iowritel, pci); + register_ioport_write(addr, len, 2, iowritew, pci); + register_ioport_write(addr, len, 1, iowriteb, pci); + + printf("Mapped io slot #%i [0x%.8x] 0x%.8x (len=%u)\n", + i, pci->bar[i], addr, len); + }else{ + if ( !resource_is_mapped(pci, i) && + !map_resource(pci, i) ) + return; + + register_cbmem(pci, i, addr); + + printf("Mapped mem slot #%i [0x%.8x] 0x%.8x\n", + i, pci->bar[i], pci->qemu_map[i]); + } +} + +void pciproxy_add_device(char *devpath) +{ + char *tok[3] = {NULL}; + struct pciproxy *pci; + int i; + + easy_explode(devpath, '.', &tok[1], 2); + if ( easy_explode(devpath, ':', &tok[0], 2) == 1 ) { + tok[1] = tok[0]; + tok[0] = NULL; + } + for(i=0; i < 3; i++) + if ( tok[i] == NULL ) + tok[i] = "0"; + + pci = (struct pciproxy *)pci_register_device("pciproxy", + sizeof(*pci), 0, -1, + NULL, NULL); + + if ( pci == NULL ) { + fprintf(stderr, "pciproxy: Adding device failed\n"); + return; + } + + pci->bus = strtol(tok[0], NULL, 16); + pci->dev = strtol(tok[1], NULL, 16); + pci->fn = strtol(tok[2], NULL, 16); + + printf("Adding PCI Host Proxy device: %.2x:%.2x:%x\n", + pci->bus, pci->dev, pci->fn); + + peek_resources(pci); + + if ( !open_proc_bus_pci(pci) ) + return; + + for(i=0; i < PCI_NUM_REGIONS; i++) { + uint32_t type; + + type = (pci->bar[i] & PCI_ADDRESS_SPACE_IO) ? + PCI_ADDRESS_SPACE_IO : + PCI_ADDRESS_SPACE_MEM ; + + if ( i == PCI_ROM_SLOT ) + type = PCI_ADDRESS_SPACE_MEM; + + if ( pci->bar[i] ) + pci_register_io_region((struct PCIDevice *)pci, + i, pci->len[i], type, mapfunc); + } + + /* TODO: Setup sigirg if present, if not, warn */ +} diff -urN qemu.orig/vl.c qemu/vl.c --- qemu.orig/vl.c 2004-06-03 16:30:57.000000000 +0100 +++ qemu/vl.c 2004-06-03 17:23:19.000000000 +0100 @@ -130,6 +130,7 @@ int pci_enabled = 0; int prep_enabled = 0; int rtc_utc = 1; +char *pciproxy_devpath = NULL; /***********************************************************/ /* x86 ISA bus support */ @@ -2018,6 +2019,7 @@ QEMU_OPTION_pci, QEMU_OPTION_prep, QEMU_OPTION_localtime, + QEMU_OPTION_pciproxy, }; typedef struct QEMUOption { @@ -2068,6 +2070,7 @@ #ifdef TARGET_PPC { "prep", 0, QEMU_OPTION_prep }, #endif + { "pciproxy", HAS_ARG, QEMU_OPTION_pciproxy }, { "localtime", 0, QEMU_OPTION_localtime }, { NULL }, }; @@ -2348,6 +2351,9 @@ case QEMU_OPTION_localtime: rtc_utc = 0; break; + case QEMU_OPTION_pciproxy: + pciproxy_devpath = (char *)optarg; + break; } } } diff -urN qemu.orig/vl.h qemu/vl.h --- qemu.orig/vl.h 2004-06-03 16:30:57.000000000 +0100 +++ qemu/vl.h 2004-06-03 16:31:27.000000000 +0100 @@ -437,6 +437,10 @@ void pci_pmac_init(void); void pci_ppc_bios_init(void); +/* pciproxy.c */ +extern char *pciproxy_devpath; +void pciproxy_add_device(char *devpath); + /* vga.c */ #define VGA_RAM_SIZE (4096 * 1024)