Index: linux-2.6.25-source/arch/x86/pci/copci.c =================================================================== --- /dev/null +++ linux-2.6.25-source/arch/x86/pci/copci.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2008 Steve Shoecraft + * + * Cooperative Linux PCI Driver implementation + */ + +#include +#include +#include +#include +#include +#include +#include "pci.h" + +#include + +#define COPCI_DEBUG 0 +#define COPCI_DEBUG_IO 0 + +/* For PCI or other memory-mapped resources */ +/* Symbol needed, dummy for coLinux. (copied from arch/x86/kernel/e820.c) */ +unsigned long pci_mem_start = 0x10000000; +EXPORT_SYMBOL(pci_mem_start); + +struct device_list { + int type; + int bus; + int device; + int func; + unsigned char regs[256]; + struct device_list *next; +}; + +static struct device_list *devices = 0, *last_device; + +#define pci_byte(r,l) *((unsigned char *)(&r[l])) +#define pci_short(r,l) *((unsigned short *)(&r[l])) +#define pci_long(r,l) *((unsigned long *)(&r[l])) + +static int copci_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) +{ + int device, func; + struct device_list *dp; + + /* Linux has encoded the device & func; split them */ + device = devfn >> 3; + func = devfn & 7; + + if (reg + len > 255) { + *value = -1; + return -EINVAL; + } + + spin_lock(&pci_config_lock); + + *value = 0; + for(dp = devices; dp; dp = dp->next) { + if (bus == dp->bus && device == dp->device && func == dp->func) { +#if COPCI_DEBUG_IO + if (dp->type == CO_DEVICE_NETWORK) printk(KERN_INFO "copci_read: bus: %02x, devfn: %02x " + "(device: %02x, func: %02x), reg: %02x, len: %d\n", bus, devfn, device, func, reg, len); +#endif + switch(len) { + case 1: + *value = pci_byte(dp->regs, reg); + break; + case 2: + *value = pci_short(dp->regs, reg); + break; + case 4: + *value = pci_long(dp->regs, reg); + break; + } +#if COPCI_DEBUG_IO + if (dp->type == CO_DEVICE_NETWORK) printk(KERN_INFO "copci_read: value: 0x%08x\n", *value); +#endif + } + } + + spin_unlock(&pci_config_lock); + + return 0; +} + +static int copci_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { + struct device_list *dp; + int rc, device, func; + + device = devfn >> 3; + func = devfn & 7; + + if (reg + len > 255) return -EINVAL; + + spin_lock(&pci_config_lock); + + rc = -EPERM; + for(dp = devices; dp; dp = dp->next) { + if (bus == dp->bus && device == dp->device && func == dp->func) { +#if COPCI_DEBUG_IO + if (dp->type == CO_DEVICE_NETWORK) printk(KERN_INFO "copci_read: bus: %02x, devfn: %02x " + "(device: %02x, func: %02x), reg: %02x, len: %d, value: %08X\n", + bus, devfn, device, func, reg, len, value); +#endif + switch(len) { + case 1: +// pci_byte(dp->regs, reg) = *value; + break; + case 2: +// pci_short(dp->regs, reg) = *value; + break; + case 4: +// pci_long(dp->regs, reg) = value; + break; + } +#if COPCI_DEBUG_IO + if (dp->type == CO_DEVICE_NETWORK) printk(KERN_INFO "copci_read: value: 0x%08x\n", value); +#endif + } + } + + spin_unlock(&pci_config_lock); + + return rc; +} + +struct pci_raw_ops copci_ops = { + .read = copci_read, + .write = copci_write, +}; + +static int get_mac(int unit, unsigned char *address) +{ + unsigned long flags; + co_network_request_t *net_request; + int result; + + co_passage_page_assert_valid(); + co_passage_page_acquire(&flags); + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_NETWORK; + net_request = (typeof(net_request))&co_passage_page->params[1]; + net_request->unit = unit; + net_request->type = CO_NETWORK_GET_MAC; + co_switch_wrapper(); + memcpy(address, net_request->mac_address, 6); + result = net_request->result; + co_passage_page_release(flags); + + return result; +} + +#if 0 +static int get_irq(int type) { + unsigned long flags; + co_network_request_t *net_request; + int result, irq; + + co_passage_page_assert_valid(); + co_passage_page_acquire(&flags); + co_passage_page->operation = CO_OPERATION_IRQ; + co_passage_page->params[0] = type; + co_switch_wrapper(); + irq = co_passage_page->params[0]; + co_passage_page_release(flags); + result = co_passage_page->params[1]; + + + return result; +} +#endif + +static int add_new(int b, int d, int f, int id, int class, int type, int irq) { + struct device_list *newdev; + +#if COPCI_DEBUG + printk("add_new: d: %d, f: %d, id: %d, class: %x, type: %d, irq: %d\n", d, f, id, class, type, irq); +#endif + newdev = kzalloc(sizeof(struct device_list), GFP_KERNEL); + if (!newdev) { + printk(KERN_ERR "COPCI: no memory for device info!\n"); + return -ENOMEM; + } + memset(newdev, 0, sizeof(*newdev)); + newdev->type = type; + newdev->bus = b; + newdev->device = d; + newdev->func = f; + pci_short(newdev->regs, PCI_VENDOR_ID) = PCI_VENDOR_ID_CO; + pci_short(newdev->regs, PCI_DEVICE_ID) = id; + pci_short(newdev->regs, PCI_COMMAND) = PCI_COMMAND_FAST_BACK; + pci_short(newdev->regs, PCI_STATUS) = (PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_FAST); + pci_short(newdev->regs, PCI_HEADER_TYPE) = 0x80; + pci_short(newdev->regs, PCI_CLASS_DEVICE) = class; + pci_byte(newdev->regs, PCI_INTERRUPT_LINE) = irq; + pci_byte(newdev->regs, PCI_INTERRUPT_PIN) = 1; + if (devices) { + last_device->next = newdev; + last_device = newdev; + } else { + devices = newdev; + last_device = newdev; + } + + return 0; +} + +void pci_cooperative_init(void) { + struct device_list *dp; + copci_config_t *host_cp, *guest_cp, *cp; + unsigned char addr[6]; + unsigned long flags; + int x,id,class,count,unit,irq; + const int max_count = COPCI_MAX_SLOTS * COPCI_MAX_FUNCS; + +#if COPCI_DEBUG + printk(KERN_INFO "COPCI: Initializing max slots:%d max func:%d size:%d\n", + COPCI_MAX_SLOTS, COPCI_MAX_FUNCS, COPCI_MAX_SLOTS*COPCI_MAX_FUNCS*sizeof(*cp)); +#endif + + guest_cp = kmalloc(max_count * sizeof(*cp), GFP_KERNEL); + BUG_ON(!guest_cp); + + /* Get our config */ + co_passage_page_assert_valid(); + co_passage_page_acquire(&flags); + co_passage_page->operation = CO_OPERATION_DEVICE; + co_passage_page->params[0] = CO_DEVICE_PCI; + co_passage_page->params[1] = COPCI_GET_CONFIG; + co_passage_page->params[2] = 0; + co_switch_wrapper(); + count = co_passage_page->params[0]; + BUG_ON(count>max_count); + host_cp = (copci_config_t *) &co_passage_page->params[1]; + memcpy(guest_cp, host_cp, count * sizeof(*cp)); + co_passage_page_release(flags); + + cp = guest_cp; + for(x=0; x < count; x++, cp++) { + switch(cp->type) { +#ifdef CONFIG_COOPERATIVE_VIDEO + case CO_DEVICE_VIDEO: + id = PCI_DEVICE_ID_COVIDEO; + class = PCI_CLASS_DISPLAY_OTHER; + irq = 0; + break; +#endif +#ifdef CONFIG_COOPERATIVE_AUDIO + case CO_DEVICE_AUDIO: + id = PCI_DEVICE_ID_COAUDIO; + class = PCI_CLASS_MULTIMEDIA_AUDIO; + irq = SOUND_IRQ; + break; +#endif + case CO_DEVICE_SCSI: + id = PCI_DEVICE_ID_COSCSI; + class = PCI_CLASS_STORAGE_SCSI; + irq = SCSI_IRQ; + break; +#ifdef CO_DEVICE_IDE + case CO_DEVICE_IDE: + id = PCI_DEVICE_ID_COIDE; + class = PCI_CLASS_STORAGE_IDE; + irq = 0x14; + break; +#endif + case CO_DEVICE_NETWORK: + id = PCI_DEVICE_ID_CONET; + class = PCI_CLASS_NETWORK_ETHERNET; + irq = NETWORK_IRQ; + break; + default: + id = class = irq = 0; + } + if (id) { + add_new(0, cp->dev, cp->func, id, class, cp->type, irq); + pci_byte(last_device->regs, PCI_CO_UNIT) = cp->unit; + } + } + kfree(guest_cp); + +#if COPCI_DEBUG + printk(KERN_INFO "COPCI: config:\n"); + for(dp = devices; dp; dp = dp->next) + printk(KERN_INFO "dev: %d, func: %d, type: %d\n", dp->device, dp->func, dp->type); +#endif + + /* For each network device, get the HW address */ + for(dp = devices; dp; dp = dp->next) { + if (dp->type == CO_DEVICE_NETWORK) { + unit = pci_byte(dp->regs, PCI_CO_UNIT); + if (get_mac(unit, addr) != 0) { +#if COPCI_DEBUG + printk(KERN_INFO "COPCI: got MAC for host unit %d\n", unit); +#endif + pci_byte(dp->regs, PCI_CO_MAC1) = addr[0]; + pci_byte(dp->regs, PCI_CO_MAC2) = addr[1]; + pci_byte(dp->regs, PCI_CO_MAC3) = addr[2]; + pci_byte(dp->regs, PCI_CO_MAC4) = addr[3]; + pci_byte(dp->regs, PCI_CO_MAC5) = addr[4]; + pci_byte(dp->regs, PCI_CO_MAC6) = addr[5]; + } + } + } + + raw_pci_ops = &copci_ops; +} Index: linux-2.6.25-source/arch/x86/pci/irq_cooperative.c =================================================================== --- /dev/null +++ linux-2.6.25-source/arch/x86/pci/irq_cooperative.c @@ -0,0 +1,26 @@ +/* + * Low-Level PCI Support for PC -- Routing of Interrupts + * + * (c) 1999--2000 Martin Mares + */ + +#include +#include +#include +#include "pci.h" + +/* + * Never use: 0, 1, 2 (timer, keyboard, and cascade) + */ +unsigned int pcibios_irq_mask = 0xfff8; + +static int copci_enable_irq(struct pci_dev *pdev) { + return 0; +} + +static void copci_disable_irq(struct pci_dev *pdev) { + return; +} + +int (*pcibios_enable_irq)(struct pci_dev *dev) = copci_enable_irq; +void (*pcibios_disable_irq)(struct pci_dev *dev) = copci_disable_irq; Index: linux-2.6.25-source/include/linux/cooperative_pci.h =================================================================== --- /dev/null +++ linux-2.6.25-source/include/linux/cooperative_pci.h @@ -0,0 +1,22 @@ + +#ifndef __LINUX_COOPERATIVE_PCI_H +#define __LINUX_COOPERATIVE_PCI_H + +/* PCI ids */ +#define PCI_VENDOR_ID_CO 0x1a55 +#define PCI_DEVICE_ID_COVIDEO 0x0001 +#define PCI_DEVICE_ID_COIDE 0x0002 +#define PCI_DEVICE_ID_COSCSI 0x0003 +#define PCI_DEVICE_ID_COAUDIO 0x0004 +#define PCI_DEVICE_ID_CONET 0x0005 + +/* NIC vendor registers */ +#define PCI_CO_UNIT 0x40 +#define PCI_CO_MAC1 0x41 +#define PCI_CO_MAC2 0x42 +#define PCI_CO_MAC3 0x43 +#define PCI_CO_MAC4 0x44 +#define PCI_CO_MAC5 0x45 +#define PCI_CO_MAC6 0x46 + +#endif Index: linux-2.6.25-source/include/linux/copci.h =================================================================== --- /dev/null +++ linux-2.6.25-source/include/linux/copci.h @@ -0,0 +1,33 @@ + +#ifndef _LINUX_COPCI_H_ +#define _LINUX_COPCI_H_ + +#if defined(CO_KERNEL) || defined(CO_HOST_KERNEL) + +enum COPCI_DEVICE_REQUEST { + COPCI_GET_CONFIG, +}; + +typedef struct { + unsigned char dev; + unsigned char func; + unsigned char type; + unsigned char unit; +} copci_config_t; + +#endif + +/* 32 devices, 8 funcs per device (fixed) */ +#define COPCI_MAX_SLOTS 32 +#define COPCI_MAX_FUNCS 8 + +enum COPCI_DEVICE_TYPE { + COPCI_DT_NONE=0, + COPCI_DT_VIDEO, + COPCI_DT_AUDIO, + COPCI_DT_IDE, + COPCI_DT_SCSI, + COPCI_DT_NET, +}; + +#endif