diff -urN qemu.orig/Makefile.target qemu/Makefile.target --- qemu.orig/Makefile.target 2004-05-31 16:53:24.000000000 +0100 +++ qemu/Makefile.target 2004-05-31 16:53:19.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-05-31 16:53:24.000000000 +0100 +++ qemu/configure 2004-05-31 16:53:19.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-05-31 16:53:24.000000000 +0100 +++ qemu/hw/pc.c 2004-05-31 16:53:19.000000000 +0100 @@ -386,6 +386,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-05-31 17:33:13.000000000 +0100 @@ -0,0 +1,577 @@ +/* + * QEMU PCI Host proxy + * Copyright (c) 2004 Gianni Tedesco + * This code is released under the terms of the GNU GPL v2. + * + * 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 Support memory mapped resources + * o Support ROM resources + * o Map IRQs +*/ + +#include "vl.h" +#include +#include +#if HAVE_SYS_IO_H +#include +#endif + +#define PROC_DEVICES "/proc/bus/pci/devices" +#define PCIPROXY_RESOURCES 7 +#define PCIPROXY_ROM_SLOT 6 + +/* 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[PCIPROXY_RESOURCES]; + uint32_t qemu_map[PCIPROXY_RESOURCES]; + void *map[PCIPROXY_RESOURCES]; + uint32_t io_allowed; + uint32_t bar[PCIPROXY_RESOURCES]; + int dev, bus, fn; + int fd; +}; + +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; +} + +#if 0 +static int read_config_dword(struct pciproxy *pci, uint32_t idx, uint32_t *word) +{ + ssize_t ret = pread(pci->fd, word, sizeof(*word), idx); + *word = le32_to_cpu(*word); + return (ret == (ssize_t)sizeof(*word)); +} + +static int read_config_word(struct pciproxy *pci, uint32_t idx, uint16_t *word) +{ + ssize_t ret = pread(pci->fd, word, sizeof(*word), idx); + *word = le16_to_cpu(*word); + return (ret == (ssize_t)sizeof(*word)); +} + +static int read_config_byte(struct pciproxy *pci, uint32_t idx, uint8_t *word) +{ + ssize_t ret = pread(pci->fd, word, sizeof(*word), idx); + return (ret == (ssize_t)sizeof(*word)); +} + +static int write_config_dword(struct pciproxy *pci, uint32_t idx, uint32_t word) +{ + ssize_t ret; + word = cpu_to_le32(word); + ret = pwrite(pci->fd, &word, sizeof(word), idx); + return (ret == (ssize_t)sizeof(word)); +} + +static int write_config_word(struct pciproxy *pci, uint32_t idx, uint16_t word) +{ + ssize_t ret; + word = cpu_to_le16(word); + ret = pwrite(pci->fd, &word, sizeof(word), idx); + return (ret == (ssize_t)sizeof(word)); +} + +static int write_config_byte(struct pciproxy *pci, uint32_t idx, uint8_t word) +{ + ssize_t ret; + ret = pwrite(pci->fd, &word, sizeof(word), idx); + return (ret == (ssize_t)sizeof(word)); +} +#endif + +/* 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 < PCIPROXY_RESOURCES; 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_IOPERM + int ret; + + ret = iopl(3); + if ( ret == 0 ) { + pci->io_allowed |= (1<bus, pci->dev, pci->fn, i, sys_err()); +#else + fprintf(stderr, "%.2x:%.2x.%x ioperm() slot %i: %s\n", + pci->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 +} + +/* Try to mmap a given PCI resource from the /proc/bus/pci/XX/YY.Z file */ +static int map_resource(struct pciproxy *pci, int i, void *start_addr) +{ + uint32_t base, flags, len, ofs, pgmask, bar; + int prot, ioc, mmflags; + void *map; + + len = pci->len[i]; + bar = pci->bar[i]; + + if ( start_addr ) { + mmflags = MAP_SHARED | MAP_FIXED; + }else{ + mmflags = MAP_SHARED; + } + + if ( i == PCIPROXY_ROM_SLOT ) { + prot = PROT_READ; + }else{ + prot = PROT_READ|PROT_WRITE; + } + + if ( bar & PCI_ADDRESS_SPACE_IO ) { + flags = (bar & 0x3); + base = (bar & ~0x3); + ioc = PCIIOC_MMAP_IS_IO; + }else{ + flags = (bar & 0xf); + base = (bar & ~0xf); + ioc = PCIIOC_MMAP_IS_MEM; + } + + /* 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(start_addr, len, prot, mmflags, pci->fd, base); + if ( (map == MAP_FAILED) ) { + if ( (flags & PCI_ADDRESS_SPACE_IO ) ) + return map_with_ioperm(pci, i, bar); + fprintf(stderr, "%.2x:%.2x.%x mmap() slot %i: %s\n", + pci->bus, pci->dev, pci->fn, i, sys_err()); + return 0; + } + + 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) ) { +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; + + for(i=0; i < PCIPROXY_RESOURCES; i++) { + if ( pci->bar[i] && + addr >= pci->qemu_map[i] && + addr <= pci->qemu_map[i] + pci->len[i] ) { + break; + } + } + + if ( i >= PCIPROXY_RESOURCES ) { + fprintf(stderr, "Write to unknown I/O resource (0x%.8x/%u)\n", + addr, len); + return; + } + + ofs = addr - pci->qemu_map[i]; + addr = pci->bar[i] & ~0x03; + + if ( pci->map[i] ) { + /* TODO */ + return; + }else{ +#if HAVE_SYS_IO_H + if ( !(pci->io_allowed & (1<qemu_map[i] + ofs, ofs, data); +} + +static uint32_t ioread(void *ptr, uint32_t addr, uint32_t len) +{ + struct pciproxy *pci = (struct pciproxy *)ptr; + uint32_t ret, ofs; + int i; + + for(i=0; i < PCIPROXY_RESOURCES; i++) { + if ( pci->bar[i] && + addr >= pci->qemu_map[i] && + addr <= pci->qemu_map[i] + pci->len[i] ) { + break; + } + } + + if ( i >= PCIPROXY_RESOURCES ) { + fprintf(stderr, "Read from unknown I/O resource (0x%.8x/%u)\n", + addr, len); + return 0xffffffff; + } + + ofs = addr - pci->qemu_map[i]; + addr = pci->bar[i] & ~0x03; + + if ( pci->map[i] ) { + /* TODO */ + printf("Memory mapped I/O registers\n"); + return 0xffffffff; + }else{ +#if HAVE_SYS_IO_H + if ( !(pci->io_allowed & (1<qemu_map[i] + ofs, ofs, ret); + 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; + + pci->qemu_map[i] = addr; + +#if 0 + if ( pci->map[i] ) { + uint32_t map; + uint32_t len; + uint32_t pgmask = sysconf(_SC_PAGESIZE) - 1; + void *ret; + + map = (uint32_t)pci->map[i]; + len = pci->len[i]; + + map &= ~pgmask; + len += pgmask; + len &= ~pgmask; + printf("Unmapping slot #%i 0x%.8x %u\n", i, map, len); + munmap((void *)map, (size_t)len); + ret = mmap((void *)map, (size_t)len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if ( ret != (void *)map ) + printf("FAILED\n"); + } +#endif + + if ( type == PCI_ADDRESS_SPACE_IO ) { + uint32_t len = pci->len[i]; + + if ( !map_resource(pci, i, NULL) ) + return; + + 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 0 + if ( !map_resource(pci, i, phys_ram_base + addr) ) + return; + + printf("Mapped mem slot #%i [0x%.8x] 0x%.8x / %p - %p\n", + i, pci->bar[i], addr, phys_ram_base, + phys_ram_base + addr); +#endif + } +} + +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) ) { + printf("OI\n"); + return; + } + + for(i=0; i < PCIPROXY_RESOURCES; i++) { + uint32_t type; + + /* XXX: Not yet implemented */ + if ( i == PCIPROXY_ROM_SLOT ) + continue; + + type = (pci->bar[i] & PCI_ADDRESS_SPACE_IO) ? + PCI_ADDRESS_SPACE_IO : + PCI_ADDRESS_SPACE_MEM ; + + if ( pci->bar[i] ) { + printf("Registering region %i\n", 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-05-31 16:53:24.000000000 +0100 +++ qemu/vl.c 2004-05-31 17:35:45.000000000 +0100 @@ -129,6 +129,7 @@ int audio_enabled = 0; int pci_enabled = 0; int prep_enabled = 0; +char *pciproxy_devpath = NULL; /***********************************************************/ /* x86 ISA bus support */ @@ -2015,6 +2016,7 @@ QEMU_OPTION_no_code_copy, QEMU_OPTION_pci, QEMU_OPTION_prep, + QEMU_OPTION_pciproxy, }; typedef struct QEMUOption { @@ -2065,6 +2067,7 @@ #ifdef TARGET_PPC { "prep", 0, QEMU_OPTION_prep }, #endif + { "pciproxy", HAS_ARG, QEMU_OPTION_pciproxy }, { NULL }, }; @@ -2341,6 +2344,9 @@ case QEMU_OPTION_prep: prep_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-05-31 16:53:24.000000000 +0100 +++ qemu/vl.h 2004-05-31 16:53:19.000000000 +0100 @@ -434,6 +434,10 @@ void pci_pmac_init(void); void pci_ppc_bios_init(void); +/* pciproxy.c */ +void pciproxy_add_device(char *devpath); +extern char *pciproxy_devpath; + /* vga.c */ #define VGA_RAM_SIZE (4096 * 1024)