VMX: change msr emulation to pass in the vm trapframe
[akaros.git] / kern / arch / x86 / pci.h
index 289a780..a1f40b9 100644 (file)
@@ -4,21 +4,14 @@
  * Barret Rhoden <brho@cs.berkeley.edu>
  * Original by Paul Pearce <pearce@eecs.berkeley.edu> */
 
-#ifndef ROS_ARCH_PCI_H
-#define ROS_ARCH_PCI_H
+#pragma once
 
 #include <ros/common.h>
 #include <sys/queue.h>
+#include <atomic.h>
+#include <arch/pci_regs.h>
 
-#define pci_debug(...)  printk(__VA_ARGS__)  
-
-// Macro for creating the address fed to the PCI config register 
-// TODO: get rid of this, in favor of the helpers
-#define MK_CONFIG_ADDR(BUS, DEV, FUNC, REG) (unsigned long)(((BUS) << 16)   |  \
-                                                            ((DEV) << 11)   |  \
-                                                            ((FUNC) << 8)   |  \
-                                                            ((REG) & 0xfc)  |  \
-                                                            (0x80000000))
+#define pci_debug(...)  printk(__VA_ARGS__)
 
 #define PCI_CONFIG_ADDR     0xCF8
 #define PCI_CONFIG_DATA     0xCFC
 /* TODO: gut this (when the IOAPIC is fixed) */
 #define INVALID_BUS                    0xFFFF
 
-#define PCI_IO_MASK                    0xfff8
-#define PCI_MEM_MASK           0xfffffff0
-#define PCI_IRQLINE_MASK       0x000000ff
-#define PCI_IRQPIN_MASK                0x0000ff00
-#define PCI_IRQPIN_SHFT                8
-#define PCI_VENDOR_MASK                0xffff
-#define PCI_DEVICE_OFFSET      0x10
-
 #define PCI_NOINT                      0x00
 #define PCI_INTA                       0x01
 #define PCI_INTB                       0x02
 #define PCI_INTD                       0x04
 
 /* PCI Register Config Space */
-#define PCI_DEV_VEND_REG       0x00
-#define PCI_STAT_CMD_REG       0x04
-#define PCI_CLASS_REG          0x08
-#define PCI_HEADER_REG         0x0c
+#define PCI_DEV_VEND_REG       0x00    /* for the 32 bit read of dev/vend */
+#define PCI_VENDID_REG         0x00
+#define PCI_DEVID_REG          0x02
+#define PCI_CMD_REG                    0x04
+#define PCI_STATUS_REG         0x06
+#define PCI_REVID_REG          0x08
+#define PCI_PROGIF_REG         0x09
+#define PCI_SUBCLASS_REG       0x0a
+#define PCI_CLASS_REG          0x0b
+#define PCI_CLSZ_REG           0x0c
+#define PCI_LATTIM_REG         0x0d
+#define PCI_HEADER_REG         0x0e
+#define PCI_BIST_REG           0x0f
 /* Config space for header type 0x00  (Standard) */
 #define PCI_BAR0_STD           0x10
 #define PCI_BAR1_STD           0x14
 #define PCI_BAR5_STD           0x24
 #define PCI_BAR_OFF                    0x04
 #define PCI_CARDBUS_STD                0x28
-#define PCI_SUBSYSTEM_STD      0x2C
+#define PCI_SUBSYSVEN_STD      0x2c
+#define PCI_SUBSYSID_STD       0x2e
 #define PCI_EXPROM_STD         0x30
 #define PCI_CAPAB_STD          0x34
-#define PCI_IRQ_STD                    0x3c
+#define PCI_IRQLINE_STD                0x3c
+#define PCI_IRQPIN_STD         0x3d
+#define PCI_MINGRNT_STD                0x3e
+#define PCI_MAXLAT_STD         0x3f
 /* Config space for header type 0x01 (PCI-PCI bridge) */
+/* None of these have been used, so if you use them, check them against
+ * http://wiki.osdev.org/PCI#PCI_Device_Structure */
 #define PCI_BAR0_BR                    0x10
 #define PCI_BAR1_BR                    0x14
-#define PCI_BUSINFO_BR         0x18
-#define PCI_IOINFO_BR          0x1c
-#define PCI_MEM_BR                     0x20
-#define PCI_MEM_PRFC_BR                0x24
-#define PCI_PRFC_BASE_BR       0x28
-#define PCI_PRFC_LIM_BR                0x2C
-#define PCI_IO_LIM_BR          0x30
+#define PCI_BUS1_BR                    0x18
+#define PCI_BUS2_BR                    0x19
+#define PCI_SUBBUS_BR          0x1a
+#define PCI_LATTIM2_BR         0x1b
+#define PCI_IOBASE_BR          0x1c
+#define PCI_IOLIM_BR           0x1d
+#define PCI_STATUS2_BR         0x1e
+#define PCI_MEMBASE_BR         0x20
+#define PCI_MEMLIM_BR          0x22
+#define PCI_PREMEMBASE_BR      0x24
+#define PCI_PREMEMLIM_BR       0x26
+#define PCI_PREBASEUP32_BR     0x28
+#define PCI_PRELIMUP32_BR      0x2c
+#define PCI_IOBASEUP16_BR      0x30
+#define PCI_IOLIMUP16_BR       0x32
 #define PCI_CAPAB_BR           0x34
-#define PCI_IRQ_BDG_BR         0x3c
+#define PCI_EXPROM_BR          0x38
+#define PCI_IRQLINE_BR         0x3c
+#define PCI_IRQPIN_BR          0x3d
+#define PCI_BDGCTL_BR          0x3e
 /* Config space for header type 0x02 (PCI-Cardbus bridge) */
+/* None of these have been used, so if you use them, check them against
+ * http://wiki.osdev.org/PCI#PCI_Device_Structure */
 #define PCI_SOC_BASE_CB                0x10
-#define PCI_SEC_STAT_CB                0x14
-#define PCI_BUS_INFO_CB                0x18
+#define PCI_OFF_CAP_CB         0x14
+#define PCI_SEC_STAT_CB                0x16
+#define PCI_BUS_NR_CB          0x18
+#define PCI_CARDBUS_NR_CB      0x19
+#define PCI_SUBBUS_NR_CB       0x1a
+#define PCI_CARD_LAT_CB                0x1b
 #define PCI_MEM_BASE0_CB       0x1c
 #define PCI_MEM_LIMIT0_CB      0x20
 #define PCI_MEM_BASE1_CB       0x24
 #define PCI_IO_LIMIT0_CB       0x30
 #define PCI_IO_BASE1_CB                0x34
 #define PCI_IO_LIMIT1_CB       0x38
-#define PCI_IRQ_CB                     0x3c
-#define PCI_SUBSYS_CB          0x40
+#define PCI_IRQLINE_CB         0x3c
+#define PCI_IRQPIN_CB          0x3d
+#define PCI_BDGCTL_CB          0x3e
+#define PCI_SUBDEVID_CB                0x40
+#define PCI_SUBVENID_CB                0x42
 #define PCI_16BIT_CB           0x44
 
-/* Legacy Paul-mapping */
-#define PCI_IRQ_REG                    PCI_IRQ_STD
-
 /* Command Register Flags */
 #define PCI_CMD_IO_SPC         (1 << 0)
 #define PCI_CMD_MEM_SPC                (1 << 1)
 #define PCI_ST_PAR_ERR         (1 << 15)
 
 /* BARS: Base Address Registers */
-#define PCI_BAR_IO_MASK                0x1
-#define PCI_BAR_IO PCI_BAR_IO_MASK
+#define PCI_BAR_IO                     0x1                     /* 1 == IO, 0 == Mem */
+#define PCI_BAR_IO_MASK                0xfffffffc
+#define PCI_BAR_MEM_MASK       0xfffffff0
 #define PCI_MEMBAR_TYPE        (3 << 1)
 #define PCI_MEMBAR_32BIT       0x0
 #define PCI_MEMBAR_RESV        0x2                     /* type 0x1 shifted to MEMBAR_TYPE */
 #define PCI_MAX_DEV                    32
 #define PCI_MAX_FUNC           8
 
-// Offset used for indexing IRQs. Why isnt this defined elsewhere?
-#define NUM_IRQS                       256
-#define KERNEL_IRQ_OFFSET      32
-
 // Run the PCI Code to loop over the PCI BARs. For now we don't use the BARs,
 // dont check em.
 #define CHECK_BARS                     0
 
-/* Struct for some meager contents of a PCI device */
+#define MAX_PCI_BAR                    6
+
+
+struct pci_bar {
+       uint32_t                                        raw_bar;
+       uint32_t                                        pio_base;
+       uint32_t                                        mmio_base32;
+       uint64_t                                        mmio_base64;
+       uint32_t                                        mmio_sz;
+};
+
 struct pci_device {
        STAILQ_ENTRY(pci_device)        all_dev;        /* list of all devices */
        SLIST_ENTRY(pci_device)         irq_dev;        /* list of all devs off an irq */
+       char                                            name[9];
+       spinlock_t                                      lock;
+       void                                            *dev_data;      /* device private pointer */
+       bool                                            in_use;         /* prevent double discovery */
        uint8_t                                         bus;
        uint8_t                                         dev;
        uint8_t                                         func;
@@ -149,33 +177,118 @@ struct pci_device {
        uint16_t                                        ven_id;
        uint8_t                                         irqline;
        uint8_t                                         irqpin;
+       char                                            *header_type;
        uint8_t                                         class;
        uint8_t                                         subclass;
        uint8_t                                         progif;
+       bool                                            msi_ready;
+       uint32_t                                        msi_msg_addr_hi;
+       uint32_t                                        msi_msg_addr_lo;
+       uint32_t                                        msi_msg_data;
+       uint8_t                                         nr_bars;
+       struct pci_bar                          bar[MAX_PCI_BAR];
+       uint32_t                                        caps[PCI_CAP_ID_MAX + 1];
+       uintptr_t                                       msix_tbl_paddr;
+       uintptr_t                                       msix_tbl_vaddr;
+       uintptr_t                                       msix_pba_paddr;
+       uintptr_t                                       msix_pba_vaddr;
+       unsigned int                            msix_nr_vec;
+       bool                                            msix_ready;
+};
+
+struct msix_entry {
+       uint32_t addr_lo, addr_hi, data, vector;
+};
+
+struct msix_irq_vector {
+       struct pci_device *pcidev;
+       struct msix_entry *entry;
+       uint32_t addr_lo;
+       uint32_t addr_hi;
+       uint32_t data;
 };
 
 /* List of all discovered devices */
 STAILQ_HEAD(pcidev_stailq, pci_device);
-SLIST_HEAD(pcidev_slist, pci_device);
 extern struct pcidev_stailq pci_devices;
-/* Mapping of irq -> PCI device (TODO: make this PCI-agnostic) */
-extern struct pci_device *irq_pci_map[NUM_IRQS];
+
+/* Sync rules for PCI: once a device is added to the list, it is never removed,
+ * and its read-only fields can be accessed at any time.  There is no need for
+ * refcnts or things like that.
+ *
+ * The device list is built early on when we're single threaded, so I'm not
+ * bothering with locks for that yet.  Append-only, singly-linked-list reads
+ * don't need a lock either.
+ *
+ * Other per-device accesses (like read-modify-writes to config space or MSI
+ * fields) require the device's lock.  If we ever want to unplug, we'll probably
+ * work out an RCU-like scheme for the pci_devices list.
+ *
+ * Note this is in addition to the config space global locking done by every
+ * pci_read or write call. */
 
 void pci_init(void);
 void pcidev_print_info(struct pci_device *pcidev, int verbosity);
+uint32_t pci_config_addr(uint8_t bus, uint8_t dev, uint8_t func, uint32_t reg);
 
 /* Read and write helpers (Eventually, we should have these be statics, since no
  * device should touch PCI config space). */
-uint32_t pci_read32(unsigned short bus, unsigned short dev, unsigned short func,
-                    unsigned short offset);
-void pci_write32(unsigned short bus, unsigned short dev, unsigned short func,
-                    unsigned short offset, uint32_t value);
-uint32_t pcidev_read32(struct pci_device *pcidev, unsigned short offset);
-void pcidev_write32(struct pci_device *pcidev, unsigned short offset,
-                    uint32_t value);
-uint32_t pci_getbar(struct pci_device *pcidev, unsigned int bar);
-bool pci_is_iobar(uint32_t bar);
-uint32_t pci_getmembar32(uint32_t bar);
-uint32_t pci_getiobar32(uint32_t bar);
-
-#endif /* ROS_ARCH_PCI_H */
+uint32_t pci_read32(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset);
+void pci_write32(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset,
+                 uint32_t value);
+uint16_t pci_read16(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset);
+void pci_write16(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset,
+                 uint16_t value);
+uint8_t pci_read8(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset);
+void pci_write8(uint8_t bus, uint8_t dev, uint8_t func, uint32_t offset,
+                uint8_t value);
+uint32_t pcidev_read32(struct pci_device *pcidev, uint32_t offset);
+void pcidev_write32(struct pci_device *pcidev, uint32_t offset, uint32_t value);
+uint16_t pcidev_read16(struct pci_device *pcidev, uint32_t offset);
+void pcidev_write16(struct pci_device *pcidev, uint32_t offset, uint16_t value);
+uint8_t pcidev_read8(struct pci_device *pcidev, uint32_t offset);
+void pcidev_write8(struct pci_device *pcidev, uint32_t offset, uint8_t value);
+
+/* Other common PCI functions */
+void pci_set_bus_master(struct pci_device *pcidev);
+void pci_clr_bus_master(struct pci_device *pcidev);
+struct pci_device *pci_match_tbdf(int tbdf);
+uintptr_t pci_get_membar(struct pci_device *pcidev, int bir);
+uintptr_t pci_get_iobar(struct pci_device *pcidev, int bir);
+uint32_t pci_get_membar_sz(struct pci_device *pcidev, int bir);
+uint16_t pci_get_vendor(struct pci_device *pcidev);
+uint16_t pci_get_device(struct pci_device *pcidev);
+uint16_t pci_get_subvendor(struct pci_device *pcidev);
+uint16_t pci_get_subdevice(struct pci_device *pcidev);
+void pci_dump_config(struct pci_device *pcidev, size_t len);
+int pci_find_cap(struct pci_device *pcidev, uint8_t cap_id, uint32_t *cap_reg);
+unsigned int pci_to_tbdf(struct pci_device *pcidev);
+uintptr_t pci_map_membar(struct pci_device *dev, int bir);
+static inline void pci_set_drvdata(struct pci_device *pcidev, void *data);
+static inline void *pci_get_drvdata(struct pci_device *pcidev);
+
+/* MSI functions, msi.c */
+int pci_msi_enable(struct pci_device *p, uint64_t vec);
+int pci_msix_init(struct pci_device *p);
+struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec);
+void pci_msi_mask(struct pci_device *p);
+void pci_msi_unmask(struct pci_device *p);
+void pci_msi_route(struct pci_device *p, int dest);
+void pci_msix_mask_vector(struct msix_irq_vector *linkage);
+void pci_msix_unmask_vector(struct msix_irq_vector *linkage);
+void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest);
+
+/* TODO: this is quite the Hacke */
+#define explode_tbdf(tbdf) {pcidev.bus = tbdf >> 16;\
+               pcidev.dev = (tbdf>>11)&0x1f;\
+               pcidev.func = (tbdf>>8)&3;}
+
+static inline void pci_set_drvdata(struct pci_device *pcidev, void *data)
+{
+       pcidev->dev_data = data;
+}
+
+static inline void *pci_get_drvdata(struct pci_device *pcidev)
+{
+       return pcidev->dev_data;
+}