--- /dev/null
+/*
+amddev.c
+
+Driver for the AMD Device Exclusion Vector (DEV)
+*/
+
+#define _SYSTEM
+#define _MINIX
+
+#include <minix/config.h>
+#include <minix/type.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/vm.h>
+#include <minix/com.h>
+#include <minix/const.h>
+#include <minix/ipc.h>
+#include <minix/syslib.h>
+#include <minix/sysutil.h>
+#include <ibm/pci.h>
+
+/* Offsets from capability pointer */
+#define DEV_OP 4 /* Selects control/status register to access */
+#define DEV_OP_FUNC_SHIFT 8 /* Function part in OP reg. */
+#define DEV_DATA 8 /* Read/write to access reg. selected */
+
+/* Functions */
+#define DEVF_BASE_LO 0
+#define DEVF_BASE_HI 1
+#define DEVF_MAP 2
+#define DEVF_CAP 3
+#define DEVF_CAP_MAPS_MASK 0x00ff0000
+#define DEVF_CAP_MAPS_SHIFT 16
+#define DEVF_CAP_DOMS_MASK 0x0000ff00
+#define DEVF_CAP_DOMS_SHIFT 8
+#define DEVF_CAP_REV_MASK 0x000000ff
+#define DEVF_CAP_REV_SHIFT 0
+#define DEVF_CR 4
+#define DEVF_ERR_STATUS 5
+#define DEVF_ERR_ADDR_LO 6
+#define DEVF_ERR_ADDR_HI 7
+
+static int dev_devind;
+static u8_t dev_capptr;
+static u8_t *table;
+
+static int init(void);
+static int find_dev(int *devindp, u8_t *capaddrp);
+static u32_t read_reg(int function, int index);
+static void write_reg(int function, int index, u32_t value);
+static void init_domain(int index);
+static void init_map(int index);
+static int do_add_phys(message *m);
+static int do_del_phys(message *m);
+static int do_add4pci(message *m);
+static void add_range(u32_t busaddr, u32_t size);
+static void del_range(u32_t busaddr, u32_t size);
+static int do_pm_notify(message *m);
+static void report_exceptions(void);
+
+int main(void)
+{
+ int r;
+ message m;
+
+ printf("amddev: starting\n");
+
+ init();
+
+ for(;;)
+ {
+ report_exceptions();
+
+ r= receive(ANY, &m);
+ if (r != OK)
+ panic(__FILE__, "receive failed", r);
+ switch(m.m_type)
+ {
+ case PROC_EVENT:
+ do_pm_notify(&m);
+ continue;
+
+ case IOMMU_ADD:
+ r= do_add4pci(&m);
+ m.m_type= r;
+ send(m.m_source, &m);
+ continue;
+ }
+ printf("amddev: got message from %d\n", m.m_source);
+ }
+}
+
+static int init()
+{
+ int r, n_maps, n_domains, revision;
+ u16_t flags;
+ u32_t bits;
+
+ r= find_dev(&dev_devind, &dev_capptr);
+ if (!r)
+ return r;
+ flags= pci_attr_r16(dev_devind, dev_capptr+CAP_SD_INFO);
+ printf("amddev`init: flags = 0x%x\n", flags);
+
+ bits= read_reg(DEVF_CAP, 0);
+ n_maps= ((bits & DEVF_CAP_MAPS_MASK) >> DEVF_CAP_MAPS_SHIFT);
+ n_domains= ((bits & DEVF_CAP_DOMS_MASK) >> DEVF_CAP_DOMS_SHIFT);
+ revision= ((bits & DEVF_CAP_REV_MASK) >> DEVF_CAP_REV_SHIFT);
+ printf("amddev`init: DEVF_CAP = 0x%x (%d maps, %d domains, rev 0x%x)\n",
+ bits, n_maps, n_domains, revision);
+
+ printf("status = 0x%x, addr-lo = 0x%x, addr-hi = 0x%x\n",
+ read_reg(DEVF_ERR_STATUS, 0),
+ read_reg(DEVF_ERR_ADDR_LO, 0),
+ read_reg(DEVF_ERR_ADDR_HI, 0));
+
+ init_domain(0);
+ init_map(0);
+#if 0
+ init_domain(1);
+#endif
+
+ write_reg(DEVF_CR, 0, 0x10 | 0x8 | 0x4 | 1);
+
+ printf("after write: DEVF_CR: 0x%x\n", read_reg(DEVF_CR, 0));
+}
+
+static int find_dev(devindp, capaddrp)
+int *devindp;
+u8_t *capaddrp;
+{
+ int r, devind, first;
+ u8_t capptr, type, next, subtype;
+ u16_t vid, did, status;
+
+ pci_init();
+
+ first= 1;
+ for(;;)
+ {
+ if (first)
+ {
+ first= 0;
+ r= pci_first_dev(&devind, &vid, &did);
+ if (!r)
+ {
+ printf("amddev`find_dev: no first dev\n");
+ return;
+ }
+ }
+ else
+ {
+ r= pci_next_dev(&devind, &vid, &did);
+ if (!r)
+ {
+ printf("amddev`find_dev: no next dev\n");
+ return;
+ }
+ }
+
+ printf("amddev`find_dev: got devind %d, vid 0x%x, did 0x%x\n",
+ devind, vid, did);
+
+ /* Check capabilities bit in the device status register */
+ status= pci_attr_r16(devind, PCI_SR);
+ if (!(status & PSR_CAPPTR))
+ continue;
+
+ capptr= (pci_attr_r8(devind, PCI_CAPPTR) & PCI_CP_MASK);
+ while (capptr != 0)
+ {
+ type = pci_attr_r8(devind, capptr+CAP_TYPE);
+ next= (pci_attr_r8(devind, capptr+CAP_NEXT) &
+ PCI_CP_MASK);
+ if (type == CAP_T_SECURE_DEV)
+ {
+ printf(
+ "amddev`find_dev: found secure device\n");
+ subtype= (pci_attr_r8(devind, capptr+
+ CAP_SD_INFO) & CAP_SD_SUBTYPE_MASK);
+ if (subtype == CAP_T_SD_DEV)
+ {
+ printf("amddev`find_dev: AMD DEV\n");
+ pci_reserve(devind);
+ *devindp= devind;
+ *capaddrp= capptr;
+ return 1;
+ }
+ }
+ capptr= next;
+ }
+ }
+ return 0;
+}
+
+static u32_t read_reg(int function, int index)
+{
+ pci_attr_w32(dev_devind, dev_capptr + DEV_OP, ((function <<
+ DEV_OP_FUNC_SHIFT) | index));
+ return pci_attr_r32(dev_devind, dev_capptr + DEV_DATA);
+}
+
+static void write_reg(int function, int index, u32_t value)
+{
+ pci_attr_w32(dev_devind, dev_capptr + DEV_OP, ((function <<
+ DEV_OP_FUNC_SHIFT) | index));
+ pci_attr_w32(dev_devind, dev_capptr + DEV_DATA, value);
+}
+
+static void init_domain(int index)
+{
+ int r;
+ size_t o, size, memsize;
+ phys_bytes busaddr;
+
+ size= 0x100000 / 8;
+ table= malloc(size + PAGE_SIZE);
+ if (table == NULL)
+ panic("AMDDEV","malloc failed", NO_NUM);
+ o= (unsigned)table & (PAGE_SIZE-1);
+ if (o)
+ table += PAGE_SIZE-o;
+ if (index == 0)
+ {
+ memset(table, 0, size);
+ memsize= 0x37000 / 8;
+ printf("memsize = 0x%x / 8\n", memsize*8);
+ memset(table, 0xff, memsize);
+ }
+ else
+ {
+ memset(table, 0xff, size);
+ memset(table, 0x00, size);
+ }
+
+ r= sys_umap(SELF, D, (vir_bytes)table, size, &busaddr);
+ if (r != OK)
+ panic("AMDDEV","sys_umap failed", r);
+printf("init_domain: busaddr = %p\n", busaddr);
+
+ write_reg(DEVF_BASE_HI, index, 0);
+ write_reg(DEVF_BASE_LO, index, busaddr | 3);
+
+ printf("after write: DEVF_BASE_LO: 0x%x\n",
+ read_reg(DEVF_BASE_LO, index));
+}
+
+static void init_map(int index)
+{
+ u32_t v, dom, busno, unit0, unit1;
+
+ dom= 1;
+ busno= 7;
+ unit1= 9;
+ unit0= 9;
+ v= (dom << 26) | (dom << 20) | (busno << 12) |
+ (0 << 11) | (unit1 << 6) |
+ (0 << 5) | (unit0 << 0);
+ write_reg(DEVF_MAP, index, v);
+
+ printf("after write: DEVF_MAP: 0x%x\n", read_reg(DEVF_MAP, index));
+}
+
+static int do_add(message *m)
+{
+ int r;
+ endpoint_t proc;
+ vir_bytes start;
+ size_t size;
+ phys_bytes busaddr;
+
+ proc= m->m_source;
+ start= m->m2_l1;
+ size= m->m2_l2;
+
+#if 0
+ printf("amddev`do_add: got request for 0x%x@0x%x from %d\n",
+ size, start, proc);
+#endif
+
+ if (start % PAGE_SIZE)
+ {
+ printf("amddev`do_add: bad start 0x%x from proc %d\n",
+ start, proc);
+ return EINVAL;
+ }
+ if (size % PAGE_SIZE)
+ {
+ printf("amddev`do_add: bad size 0x%x from proc %d\n",
+ size, proc);
+ return EINVAL;
+ }
+ r= sys_umap(proc, D, (vir_bytes)start, size, &busaddr);
+ if (r != OK)
+ {
+ printf("amddev`do_add: umap failed for 0x%x@0x%x, proc %d\n",
+ size, start, proc);
+ return r;
+ }
+ add_range(busaddr, size);
+
+}
+
+static int do_add_phys(message *m)
+{
+ int i, r;
+ phys_bytes start;
+ size_t size;
+
+ start= m->m2_l1;
+ size= m->m2_l2;
+
+#if 0
+ printf("amddev`do_add_phys: got request for 0x%x@0x%x\n",
+ size, start);
+#endif
+
+ if (start % PAGE_SIZE)
+ {
+ printf("amddev`do_add_phys: bad start 0x%x\n", start);
+ return EINVAL;
+ }
+ if (size % PAGE_SIZE)
+ {
+ printf("amddev`do_add_phys: bad size 0x%x\n", size);
+ return EINVAL;
+ }
+ add_range(start, size);
+
+ write_reg(DEVF_CR, 0, 0x10);
+ for (i= 0; i<1000000; i++)
+ {
+ if (read_reg(DEVF_CR, 0) & 0x10)
+ continue;
+ return OK;
+ }
+ return EBUSY;
+}
+
+static int do_del_phys(message *m)
+{
+ int r;
+ phys_bytes start;
+ size_t size;
+
+ start= m->m2_l1;
+ size= m->m2_l2;
+
+#if 0
+ printf("amddev`do_del_phys: got request for 0x%x@0x%x\n",
+ size, start);
+#endif
+
+ if (start % PAGE_SIZE)
+ {
+ printf("amddev`do_del_phys: bad start 0x%x\n", start);
+ return EINVAL;
+ }
+ if (size % PAGE_SIZE)
+ {
+ printf("amddev`do_del_phys: bad size 0x%x\n", size);
+ return EINVAL;
+ }
+ del_range(start, size);
+
+ write_reg(DEVF_CR, 0, 0x10);
+
+ return OK;
+}
+
+static int do_add4pci(message *m)
+{
+ int r, pci_bus, pci_dev, pci_func;
+ endpoint_t proc;
+ vir_bytes start;
+ size_t size;
+ phys_bytes busaddr;
+
+ proc= m->m_source;
+ start= m->m2_l1;
+ size= m->m2_l2;
+ pci_bus= m->m1_i1;
+ pci_dev= m->m1_i2;
+ pci_func= m->m1_i3;
+
+ printf(
+"amddev`do_add4pci: got request for 0x%x@0x%x from %d for pci dev %u.%u.%u\n",
+ size, start, proc, pci_bus, pci_dev, pci_func);
+
+ if (start % PAGE_SIZE)
+ {
+ printf("amddev`do_add4pci: bad start 0x%x from proc %d\n",
+ start, proc);
+ return EINVAL;
+ }
+ if (size % PAGE_SIZE)
+ {
+ printf("amddev`do_add4pci: bad size 0x%x from proc %d\n",
+ size, proc);
+ return EINVAL;
+ }
+
+ printf("amddev`do_add4pci: should check with PCI\n");
+
+ r= sys_umap(proc, D, (vir_bytes)start, size, &busaddr);
+ if (r != OK)
+ {
+ printf(
+ "amddev`do_add4pci: umap failed for 0x%x@0x%x, proc %d: %d\n",
+ size, start, proc, r);
+ return r;
+ }
+
+ r= adddma(proc, busaddr, size);
+ if (r != 0)
+ {
+ r= -errno;
+ printf(
+ "amddev`do_add4pci: adddma failed for 0x%x@0x%x, proc %d: %d\n",
+ size, start, proc, r);
+ return r;
+ }
+
+ add_range(busaddr, size);
+
+ return OK;
+}
+
+
+static void add_range(u32_t busaddr, u32_t size)
+{
+ u32_t o, bit;
+
+#if 0
+ printf("add_range: mapping 0x%x@0x%x\n", size, busaddr);
+#endif
+
+ for (o= 0; o<size; o += PAGE_SIZE)
+ {
+ bit= (busaddr+o)/PAGE_SIZE;
+ table[bit/8] &= ~(1 << (bit % 8));
+ }
+}
+
+static void del_range(u32_t busaddr, u32_t size)
+{
+ u32_t o, bit;
+
+#if 0
+ printf("del_range: mapping 0x%x@0x%x\n", size, busaddr);
+#endif
+
+ for (o= 0; o<size; o += PAGE_SIZE)
+ {
+ bit= (busaddr+o)/PAGE_SIZE;
+ table[bit/8] |= (1 << (bit % 8));
+ }
+}
+
+static int do_pm_notify(message *m)
+{
+ int r;
+ endpoint_t proc_e;
+ phys_bytes base, size;
+
+ if (m->m_source != PM_PROC_NR)
+ {
+ printf("amddev`do_pm_notify: notify not from PM (from %d)\n",
+ m->m_source);
+ return;
+ }
+
+ for (;;)
+ {
+ r= getdma(&proc_e, &base, &size);
+ if (r == -1)
+ {
+ if (errno != -EAGAIN)
+ {
+ printf(
+ "amddev`do_pm_notify: getdma failed: %d\n",
+ errno);
+ }
+ break;
+ }
+
+ printf(
+ "amddev`do_pm_notify: deleting 0x%x@0x%x for proc %d\n",
+ size, base, proc_e);
+ del_range(base, size);
+ r= deldma(proc_e, base, size);
+ if (r == -1)
+ {
+ printf("amddev`do_pm_notify: deldma failed: %d\n",
+ errno);
+ break;
+ }
+ }
+}
+
+static void report_exceptions(void)
+{
+ u32_t status;
+
+ status= read_reg(DEVF_ERR_STATUS, 0);
+ if (!(status & 0x80000000))
+ return;
+ printf("amddev: status = 0x%x, addr-lo = 0x%x, addr-hi = 0x%x\n",
+ status, read_reg(DEVF_ERR_ADDR_LO, 0),
+ read_reg(DEVF_ERR_ADDR_HI, 0));
+ write_reg(DEVF_ERR_STATUS, 0, 0);
+}