408 lines
11 KiB
Diff
Executable File
408 lines
11 KiB
Diff
Executable File
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 <linux/kernel.h>
|
|
+#include <linux/pci.h>
|
|
+#include <linux/pci_regs.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/cooperative_pci.h>
|
|
+#include <linux/cooperative_internal.h>
|
|
+#include "pci.h"
|
|
+
|
|
+#include <linux/copci.h>
|
|
+
|
|
+#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 <mj@ucw.cz>
|
|
+ */
|
|
+
|
|
+#include <linux/types.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/pci.h>
|
|
+#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
|