From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.33) id 1BaJwj-0002Av-Cv for qemu-devel@nongnu.org; Tue, 15 Jun 2004 15:50:13 -0400 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.33) id 1BaJwh-00025x-4f for qemu-devel@nongnu.org; Tue, 15 Jun 2004 15:50:13 -0400 Received: from [199.232.76.173] (helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.33) id 1BaJwh-00025m-2G for qemu-devel@nongnu.org; Tue, 15 Jun 2004 15:50:11 -0400 Received: from [213.146.130.142] (helo=trantor.org.uk) by monty-python.gnu.org with esmtp (TLSv1:DES-CBC3-SHA:168) (Exim 4.34) id 1BaJvf-0001l5-UF for qemu-devel@nongnu.org; Tue, 15 Jun 2004 15:49:08 -0400 From: Gianni Tedesco Content-Type: multipart/mixed; boundary="=-012qxE1cLjnW295/yljG" Date: Tue, 15 Jun 2004 20:48:40 +0100 Message-Id: <1087328920.3375.51.camel@sherbert> Mime-Version: 1.0 Subject: [Qemu-devel] [PATCH]: Qemu Host PCI Proxy v0.3 Reply-To: qemu-devel@nongnu.org List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: qemu-devel@nongnu.org --=-012qxE1cLjnW295/yljG Content-Type: text/plain Content-Transfer-Encoding: 7bit Hi all, This is a patch for qemu to allow using PCI devices on the host machine from within the guest machine (for now, only as long as Linux is the host OS). This is in order to facilitate the easy reverse engineering of PCI devices. You can see it working in this screenshot: http://www.scaramanga.co.uk/stuff/qemu-pciproxy-0.3.png where I have a Broadcom 94306 802.11g card running under win2k. For the interested I have uploaded a (1.4MB) log of its I/O while installing the win2k driver to this URL: http://www.scaramanga.co.uk/stuff/bcm94306-win2k-install-driver.txt Features since 0.2: o Forward PCI config reads/writes as necessary. o Support IRQ delivery via. Linux kernel patch. The IRQ delivery kernel patch is against 2.6.6 and available at: http://www.scaramanga.co.uk/stuff/linux-sigirq-0.1.diff This means that most hardware will actually work now (apart from PCI-DMA). For now I will be turning my attentions to reverse engineering the bcm94306 and looking at doing the same thing for USB ;) Still TODO: o Update kernel patch to work with shared IRQs on host. o Documentation (thats still a WIP) o Fast logging support o AGP support o Support for PCI-DMA with patched guest OS (any win2k hackers out there wanna help on this?). Until this is done the code won't be ready for normal users... o Lock the PCI device o Figure out cause of win2k-server / 3c905c problem. -- // Gianni Tedesco (gianni at scaramanga dot co dot uk) lynx --source www.scaramanga.co.uk/scaramanga.asc | gpg --import 8646BE7D: 6D9F 2287 870E A2C9 8F60 3A3C 91B5 7669 8646 BE7D --=-012qxE1cLjnW295/yljG Content-Disposition: attachment; filename=qemu-pciproxy-0.3.diff Content-Type: text/x-patch; name=qemu-pciproxy-0.3.diff; charset=UTF-8 Content-Transfer-Encoding: 7bit diff -urN qemu.orig/Makefile.target qemu/Makefile.target --- qemu.orig/Makefile.target 2004-06-15 03:02:36.000000000 +0100 +++ qemu/Makefile.target 2004-06-15 03:03:49.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-15 03:02:36.000000000 +0100 +++ qemu/configure 2004-06-15 03:03:49.000000000 +0100 @@ -391,6 +391,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-15 03:02:36.000000000 +0100 +++ qemu/hw/pc.c 2004-06-15 03:03:49.000000000 +0100 @@ -394,6 +394,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-15 03:23:06.000000000 +0100 @@ -0,0 +1,744 @@ +/* + * QEMU PCI Host proxy v0.3 + * 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 Data logging support + * o Support multiple devices fully + * o Consistent error messages + * o Test with PCMCIA + * o AGP support +*/ + +static const char copyright[] = + "pciproxy.c: v0.3 Copyright (c) Gianni Tedesco 2004"; + +#include "vl.h" +#include +#include +#if HAVE_SYS_IO_H +#include +#endif +#include + +/* 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 proxyres { + uint32_t qemu_map; + uint32_t len; + uint32_t bar; + void *map; + int iomem_region; + short io_allowed; + short rom; +}; + +struct pciproxy { + struct PCIDevice pcidev; + struct proxyres res[PCI_NUM_REGIONS]; + int fd; + int dev, bus, fn; +}; + +#if 0 +#define lprintf printf +#else +#define lprintf(...) +#endif + +#define PROC_DEVICES "/proc/bus/pci/devices" + +static const char sigirq_patch[]="*** WARNING: You will not be able to " + "receive IRQs for your pciproxy devices without patching your kernel " + "with: http://www.scaramanga.co.uk/stuff/linux-sigirq.diff"; + +static __inline__ const char *sys_err(void) +{ + return strerror(errno); +} + +/* Explode, for parsing proc files */ +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; +} + +/* Whether a config space read/write needs forwarding to device or not */ +static int config_space_forwarded(uint32_t addr, uint32_t len) +{ + static const struct { + uint32_t begin; + uint32_t end; + }nofwd[]={ + {0x10, 0x28}, /* BARs */ + {0x30, 0x34}, /* ROM Base */ + {0x3c, 0x3e}, /* IRQ/INT */ + }; + size_t num = sizeof(nofwd)/sizeof(*nofwd); + uint32_t end = addr + len; + int i; + + /* If our request is not totally inside one of our no-forward + * ranges, then by definition we need to forward.. + */ + for(i=0; i < num; i++) { + if ( addr >= nofwd[i].begin && end < nofwd[i].end ) + return 0; + } + + return 1; +} + +/* Perform a read from an MMIO region */ +static uint32_t mem_read(struct proxyres *res, target_phys_addr_t addr, int len) +{ + uint32_t ret, ofs; + + ofs = addr - res->qemu_map; + switch ( len ) { + case 1: + ret = *(uint8_t *)(res->map + ofs); + break; + case 2: + ret = *(uint16_t *)(res->map + ofs); + break; + case 4: + ret = *(uint32_t *)(res->map + ofs); + break; + default: + return 0xffffffff; + } + + printf("mem read: 0x%.8x %u@0x%.4x\n", ret, len, ofs); + return ret; +} + +/* Perform a write to a MMIO region */ +static void mem_write(struct proxyres *res, + target_phys_addr_t addr, + int len, uint32_t value) +{ + uint32_t ofs; + + ofs = addr - res->qemu_map; + switch ( len ) { + case 1: + *(uint8_t *)(res->map + ofs) = value; + break; + case 2: + *(uint16_t *)(res->map + ofs) = value; + break; + case 4: + *(uint32_t *)(res->map + ofs) = value; + break; + default: + return; + } + + lprintf("mem write: 0x%.8x %u@0x%.4x\n", value, len, ofs); +} + +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, +}; + +/* 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->res[i].len = 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 proxyres *res) +{ +#if HAVE_IOPL + int ret; + + ret = iopl(3); + if ( ret == 0 ) { + res->io_allowed = 1; + return 1; + } + + fprintf(stderr, "iopl(): %s\n", 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 proxyres *res, uint32_t bar) +{ +#if HAVE_IOPERM + u_int32_t from, to; + int ret; + + from = bar & ~0x3; + to = from + res->len; + + ret = ioperm(from, to, 1); + if ( ret < 0 ) + return map_with_iopl(res); + res->io_allowed = 1; + return 1; +#else + if ( !map_with_iopl(res) ) { + fprintf(stderr, "ioperm() slot %i: %s\n", i, sys_err()); + } + + return 0; +#endif +} + +/* Check if resource is mapped and accessible */ +static int resource_is_mapped(struct proxyres *res) +{ + if ( res->map ) + return 1; + if ( res->io_allowed ) + return 1; + return 0; +} + +/* Try to mmap a given PCI resource from the /proc/bus/pci/XX/YY.Z file */ +static int map_resource(struct proxyres *res, int fd) +{ + uint32_t base, flags, len, ofs, pgmask, bar; + int prot, ioc; + void *map; + + len = res->len; + bar = res->bar; + + if ( res->rom ) { + prot = PROT_READ; + }else{ + prot = PROT_READ|PROT_WRITE; + } + + if ( res->rom || !(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(fd, ioc) ) { + fprintf(stderr, "ioctl(): %s\n", sys_err()); + return 0; + } + + map = mmap(NULL, len, prot, MAP_SHARED, fd, base); + if ( (map == MAP_FAILED) ) { + int ret = 0 ; + + if ( !res->rom && (flags & PCI_ADDRESS_SPACE_IO ) ) + ret = map_with_ioperm(res, bar); + + if ( ret == 0 ) + fprintf(stderr, "mmap(): %s\n", sys_err()); + + return ret; + } + + res->map = 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()); + goto err_close; + } + + /* 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->res[0].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x10)); + pci->res[1].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x14)); + pci->res[2].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x18)); + pci->res[3].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x1c)); + pci->res[4].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x20)); + pci->res[5].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x24)); + pci->res[6].bar = le32_to_cpu(*(uint32_t *)(pci->pcidev.config + 0x30)); + pci->res[6].rom = 1; + + return 1; +err_close: + if ( close(pci->fd) && errno == EINTR ) + goto err_close; + return 0; +} + +/* Perform a write to an I/O mapped region */ +static void iowrite(void *ptr, uint32_t addr, uint32_t data, uint32_t len) +{ + struct proxyres *res= (struct proxyres *)ptr; + uint32_t ofs; + return; + + ofs = addr - res->qemu_map; + addr = res->bar & ~0x03; + + if ( res->map ) { + switch( len ) { + case 1: + *(uint8_t *)(res->map + ofs) = data; + break; + case 2: + *(uint16_t *)(res->map + ofs) = data; + break; + case 4: + *(uint32_t *)(res->map + ofs) = data; + break; + default: + return; + } + }else{ +#if HAVE_SYS_IO_H + if ( !res->io_allowed ) { + fprintf(stderr, "pciproxy: illegal write on slot\n"); + return; + } + + switch ( len ) { + case 1: + outb(data, addr + ofs); + break; + case 2: + outw(data, addr + ofs); + break; + case 4: + outl(data, addr + ofs); + break; + default: + return; + } +#endif + } + + lprintf("io write: 0x%.8x %u@0x%.4x\n", data, len, ofs); +} + +/* Perform a read from an I/O mapped region */ +static uint32_t ioread(void *ptr, uint32_t addr, uint32_t len) +{ + struct proxyres *res = (struct proxyres *)ptr; + uint32_t ret, ofs; + + ofs = addr - res->qemu_map; + addr = res->bar & ~0x03; + + if ( res->map ) { + switch( len ) { + case 1: + ret = *(uint8_t *)(res->map + ofs); + break; + case 2: + ret = *(uint16_t *)(res->map + ofs); + break; + case 4: + ret = *(uint32_t *)(res->map + ofs); + break; + default: + return 0xffffffff; + } +#if HAVE_SYS_IO_H + }else{ + if ( !res->io_allowed ) { + fprintf(stderr, "pciproxy: illegal read on slot\n"); + return 0xffffffff; + } + + switch ( len ) { + case 1: + ret = inb(addr + ofs); + break; + case 2: + ret = inw(addr + ofs); + break; + case 4: + ret = inl(addr + ofs); + break; + default: + return 0xffffffff; + } +#endif + } + + lprintf("io read: 0x%.8x %u@0x%.4x\n", ret, len, ofs); + return ret; +} + +static void iowritel(void *ptr, uint32_t addr, uint32_t data) +{ + iowrite(ptr, addr, data, 4); +} +static void iowritew(void *ptr, uint32_t addr, uint32_t data) +{ + iowrite(ptr, addr, data, 2); +} +static void iowriteb(void *ptr, uint32_t addr, uint32_t data) +{ + iowrite(ptr, addr, data, 1); +} +static uint32_t ioreadl(void *ptr, uint32_t addr) +{ + return ioread(ptr, addr, 4); +} +static uint32_t ioreadw(void *ptr, uint32_t addr) +{ + return ioread(ptr, addr, 2); +} +static uint32_t ioreadb(void *ptr, uint32_t addr) +{ + return ioread(ptr, addr, 1); +} + +static void mapfunc(PCIDevice *pci_dev, int i, + uint32_t addr, uint32_t size, int type) +{ + struct pciproxy *pci = (struct pciproxy *)pci_dev; + struct proxyres *res = &pci->res[i]; + + if ( type == PCI_ADDRESS_SPACE_IO ) { + uint32_t len = res->len; + + if ( !resource_is_mapped(res) && + !map_resource(res, pci->fd) ) + return; + + res->qemu_map = addr; + + register_ioport_read(addr, len, 4, ioreadl, res); + register_ioport_read(addr, len, 2, ioreadw, res); + register_ioport_read(addr, len, 1, ioreadb, res); + register_ioport_write(addr, len, 4, iowritel, res); + register_ioport_write(addr, len, 2, iowritew, res); + register_ioport_write(addr, len, 1, iowriteb, res); + }else{ + unsigned long size; + + if ( !resource_is_mapped(res) && + !map_resource(res, pci->fd) ) + return; + + size = (res->len + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; + + if ( res->iomem_region == 0 ) + res->iomem_region = cpu_register_io_memory(0, + cpu_callback_read, + cpu_callback_write, + res); + + cpu_register_physical_memory(addr, size, + res->iomem_region); + + res->qemu_map = addr; + } +} + +/* Signal (IRQ) handler */ +static void sighand(int signum, siginfo_t *si, void *ptr) +{ + lprintf("pciproxy IRQ\n"); + pci_set_irq(ptr, 0, 1); +} + +/* Setup irq handling on a PCI device */ +static void receive_irqs(struct pciproxy *pci) +{ + struct pci_sigirq si; + struct sigaction sa; + int signum = SIGRTMIN; + + sa.sa_sigaction = sighand; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sigaction(signum, &sa, NULL); + + si.sig = signum; + si.ptr = pci; + + if ( ioctl(pci->fd, PCIIOC_SIGIRQ, &si) == 0 ) + return; + + if ( errno == EINVAL ) { + fprintf(stderr, "%s\n", sigirq_patch); + }else if ( errno == ESRCH ) { + /* No IRQ handler is needed for this device */ + return; + }else if ( errno == EIO ) { + fprintf(stderr, "%.2x:%.2x.%x: Host device can't be allowed " + "to share an IRQ yet, sorry, interrupts disabled.\n", + pci->bus, pci->dev, pci->fn); + }else{ + fprintf(stderr, "%.2x:%.2x.%x: ioctl(SIGIRQ): %s\n", + pci->bus, pci->dev, pci->fn, sys_err()); + } +} + +static uint32_t cread(PCIDevice *pci_dev, uint32_t addr, int len) +{ + struct pciproxy *pci = (struct pciproxy *)pci_dev; + ssize_t sysret; + int ret; + int f; + + f = config_space_forwarded(addr, len); + if ( !f ) + goto no_forward; + + sysret = pread(pci->fd, pci_dev->config + addr, len, addr); + if ( sysret != (ssize_t)len ) + fprintf(stderr, "pread() of config space: %s\n", sys_err()); + +no_forward: + ret = pci_default_read_config(pci_dev, addr, len); + lprintf("config read: 0x%.8x %u@0x%.2x%s\n", + ret, len, addr, f ? " (forwarded)" : ""); + return ret; +} + +static void cwrite(PCIDevice *pci_dev, uint32_t addr, uint32_t data, int len) +{ + struct pciproxy *pci = (struct pciproxy *)pci_dev; + ssize_t sysret; + int f; + + f = config_space_forwarded(addr, len); + pci_default_write_config(pci_dev, addr, data, len); + + lprintf("config write: 0x%.8x %u@0x%.2x%s\n", + data, len, addr, f ? " (forwarded)" : ""); + + if ( f == 0 ) + return; + + sysret = pwrite(pci->fd, pci_dev->config + addr, len, addr); + if ( sysret != (ssize_t)len ) + fprintf(stderr, "pwrite() of config space: %s\n", sys_err()); +} + +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, + cread, cwrite); + + 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++) { + struct proxyres *res = &pci->res[i]; + uint32_t type; + + type = (res->bar & PCI_ADDRESS_SPACE_IO) ? + PCI_ADDRESS_SPACE_IO : + PCI_ADDRESS_SPACE_MEM ; + + if ( res->rom ) + type = PCI_ADDRESS_SPACE_MEM; + + if ( res->bar ) + pci_register_io_region((struct PCIDevice *)pci, + i, res->len, type, mapfunc); + } + + receive_irqs(pci); +} diff -urN qemu.orig/vl.c qemu/vl.c --- qemu.orig/vl.c 2004-06-15 03:02:36.000000000 +0100 +++ qemu/vl.c 2004-06-15 20:33:35.000000000 +0100 @@ -104,6 +104,7 @@ /* XXX: use a two level table to limit memory usage */ #define MAX_IOPORTS 65536 +char *pciproxy_devpath = NULL; const char *bios_dir = CONFIG_QEMU_SHAREDIR; char phys_ram_file[1024]; CPUState *global_env; @@ -1081,7 +1082,7 @@ return -1; } memset(&ifr, 0, sizeof(ifr)); - ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_ONE_QUEUE; pstrcpy(ifr.ifr_name, IFNAMSIZ, "tun%d"); ret = ioctl(fd, TUNSETIFF, (void *) &ifr); if (ret != 0) { @@ -2052,6 +2053,7 @@ QEMU_OPTION_L, QEMU_OPTION_no_code_copy, QEMU_OPTION_pci, + QEMU_OPTION_pciproxy, QEMU_OPTION_prep, QEMU_OPTION_localtime, QEMU_OPTION_cirrusvga, @@ -2102,6 +2104,7 @@ #ifdef TARGET_PPC { "prep", 0, QEMU_OPTION_prep }, #endif + { "pciproxy", HAS_ARG, QEMU_OPTION_pciproxy }, { "localtime", 0, QEMU_OPTION_localtime }, /* temporary options */ @@ -2389,6 +2392,9 @@ case QEMU_OPTION_cirrusvga: cirrus_vga_enabled = 1; 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-15 03:02:36.000000000 +0100 +++ qemu/vl.h 2004-06-15 03:03:49.000000000 +0100 @@ -515,6 +515,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) --=-012qxE1cLjnW295/yljG--