From ff94280f7774b2bccfbaf48b0deea2488a35c0ca Mon Sep 17 00:00:00 2001 From: fslongjin Date: Tue, 23 Aug 2022 21:10:48 +0800 Subject: [PATCH] =?UTF-8?q?new:=20msix=E4=B8=AD=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/driver/pci/msi.c | 125 +++++++++++++++++++++++++++++++++- kernel/driver/pci/msi.h | 41 ++++++++++- kernel/driver/pci/pci.c | 5 +- kernel/driver/pci/pci.h | 11 ++- kernel/driver/usb/xhci/xhci.c | 1 + kernel/mm/mm.c | 3 +- 6 files changed, 176 insertions(+), 10 deletions(-) diff --git a/kernel/driver/pci/msi.c b/kernel/driver/pci/msi.c index 9172a166..9b2cb180 100644 --- a/kernel/driver/pci/msi.c +++ b/kernel/driver/pci/msi.c @@ -1,6 +1,7 @@ #include "msi.h" #include "pci.h" #include +#include /** * @brief 生成msi消息 @@ -10,6 +11,112 @@ */ extern struct msi_msg_t *msi_arch_get_msg(struct msi_desc_t *msi_desc); +/** + * @brief 读取msix的capability list + * + * @param msi_desc msi描述符 + * @param cap_off capability list的offset + * @return struct pci_msix_cap_t 对应的capability list + */ +static __always_inline struct pci_msix_cap_t __msi_read_msix_cap_list(struct msi_desc_t *msi_desc, uint32_t cap_off) +{ + struct pci_msix_cap_t cap_list = {0}; + uint32_t dw0; + dw0 = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off); + cap_list.cap_id = dw0 & 0xff; + cap_list.next_off = (dw0 >> 8) & 0xff; + cap_list.msg_ctrl = (dw0 >> 16) & 0xffff; + + cap_list.dword1 = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x4); + cap_list.dword2 = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x8); + return cap_list; +} + +static __always_inline struct pci_msi_cap_t __msi_read_cap_list(struct msi_desc_t *msi_desc, uint32_t cap_off) +{ + struct pci_msi_cap_t cap_list; + uint32_t dw0; + dw0 = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off); + cap_list.cap_id = dw0 & 0xff; + cap_list.next_off = (dw0 >> 8) & 0xff; + cap_list.msg_ctrl = (dw0 >> 16) & 0xffff; + + cap_list.msg_addr_lo = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x4); + uint16_t msg_data_off = 0xc; + if (cap_list.msg_ctrl & (1 << 7)) // 64位 + { + cap_list.msg_addr_hi = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x8); + } + else + { + cap_list.msg_addr_hi = 0; + msg_data_off = 0x8; + } + + cap_list.msg_data = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + msg_data_off) & 0xffff; + + cap_list.mask = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x10); + cap_list.pending = pci_read_config(msi_desc->pci_dev->bus, msi_desc->pci_dev->device, msi_desc->pci_dev->func, cap_off + 0x14); + + return cap_list; +} + +/** + * @brief 映射设备的msix表 + * + * @param pci_dev pci设备信息结构体 + * @param msix_cap msix capability list的结构体 + * @return int 错误码 + */ +static __always_inline int __msix_map_table(struct pci_device_structure_header_t *pci_dev, struct pci_msix_cap_t *msix_cap) +{ + // 计算bar寄存器的offset + uint32_t bar_off = 0x10 + 4 * (msix_cap->dword1 & 0x7); + + // msix table相对于bar寄存器中存储的地址的offset + pci_dev->msix_offset = msix_cap->dword1 & (~0x7); + pci_dev->msix_table_size = (msix_cap->msg_ctrl & 0x7ff) + 1; + pci_dev->msix_mmio_size = pci_dev->msix_table_size * 16 + pci_dev->msix_offset; + + // 申请mmio空间 + mmio_create(pci_dev->msix_mmio_size, VM_IO | VM_DONTCOPY, &pci_dev->msix_mmio_vaddr, &pci_dev->msix_mmio_size); + pci_dev->msix_mmio_vaddr &= (~0xf); + uint32_t bar = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->func, bar_off); + kdebug("pci_dev->msix_mmio_vaddr=%#018lx, bar=%#010lx, table offset=%#010lx, table_size=%#010lx, mmio_size=%d", pci_dev->msix_mmio_vaddr, bar, pci_dev->msix_offset, pci_dev->msix_table_size, pci_dev->msix_mmio_size); + + // 将msix table映射到页表 + mm_map(&initial_mm, pci_dev->msix_mmio_vaddr, pci_dev->msix_mmio_size, bar); + return 0; +} + +/** + * @brief 将msi_desc中的数据填写到msix表的指定表项处 + * + * @param pci_dev pci设备结构体 + * @param msi_desc msi描述符 + */ +static __always_inline void __msix_set_entry(struct msi_desc_t *msi_desc) +{ + uint64_t *ptr = (uint64_t *)(msi_desc->pci_dev->msix_mmio_vaddr + msi_desc->pci_dev->msix_offset + msi_desc->msi_index * 16); + *ptr = (msi_desc->msg.address_hi << 32) | (msi_desc->msg.address_lo); + ++ptr; + *ptr = (msi_desc->msg.vector_control << 32) | (msi_desc->msg.data); +} + +/** + * @brief 清空设备的msix table的指定表项 + * + * @param pci_dev pci设备 + * @param msi_index 表项号 + */ +static __always_inline void __msix_clear_entry(struct pci_device_structure_header_t *pci_dev, uint16_t msi_index) +{ + uint64_t *ptr = (uint64_t *)(pci_dev->msix_mmio_vaddr + pci_dev->msix_offset + msi_index * 16); + *ptr = 0; + ++ptr; + *ptr = 0; +} + /** * @brief 启用 Message Signaled Interrupts * @@ -51,8 +158,20 @@ int pci_enable_msi(struct msi_desc_t *msi_desc) // 获取msi消息 msi_arch_get_msg(msi_desc); - if (msi_desc->pci.msi_attribute.is_msix) + if (msi_desc->pci.msi_attribute.is_msix) // MSI-X { + // 读取msix的信息 + struct pci_msix_cap_t cap = __msi_read_msix_cap_list(msi_desc, cap_ptr); + // 映射msix table + __msix_map_table(msi_desc->pci_dev, &cap); + + // 设置msix的中断 + __msix_set_entry(msi_desc); + + // 使能msi + tmp = pci_read_config(ptr->bus, ptr->device, ptr->func, cap_ptr); // 读取cap+0x0处的值 + tmp |= (1 << 16); + pci_write_config(ptr->bus, ptr->device, ptr->func, cap_ptr, tmp); } else { @@ -124,7 +243,7 @@ int pci_start_msi(void *header) if (tmp & 0xff != 0x5) return -ENOSYS; - //使能msi + // 使能msi tmp = pci_read_config(ptr->bus, ptr->device, ptr->func, cap_ptr); // 读取cap+0x0处的值 tmp |= (1 << 16); pci_write_config(ptr->bus, ptr->device, ptr->func, cap_ptr, tmp); @@ -182,7 +301,7 @@ int pci_disable_msi(void *header) if (tmp & 0xff != 0x5) return -ENOSYS; - //禁用msi + // 禁用msi tmp = pci_read_config(ptr->bus, ptr->device, ptr->func, cap_ptr); // 读取cap+0x0处的值 tmp &= (~(1 << 16)); pci_write_config(ptr->bus, ptr->device, ptr->func, cap_ptr, tmp); diff --git a/kernel/driver/pci/msi.h b/kernel/driver/pci/msi.h index 9e5291e1..734775b2 100644 --- a/kernel/driver/pci/msi.h +++ b/kernel/driver/pci/msi.h @@ -10,6 +10,7 @@ struct msi_msg_t uint32_t address_lo; uint32_t address_hi; uint32_t data; + uint32_t vector_control; }; struct pci_msi_desc_t { @@ -27,6 +28,42 @@ struct pci_msi_desc_t } msi_attribute; }; +/** + * @brief msi capability list的结构 + * + */ +struct pci_msi_cap_t +{ + uint8_t cap_id; + uint8_t next_off; + uint16_t msg_ctrl; + + uint32_t msg_addr_lo; + uint32_t msg_addr_hi; + + uint16_t msg_data; + uint16_t Rsvd; + + uint32_t mask; + uint32_t pending; +}; + +/** + * @brief MSI-X的capability list结构体 + * + */ +struct pci_msix_cap_t +{ + uint8_t cap_id; + uint8_t next_off; + uint16_t msg_ctrl; + + uint32_t dword1; // 该DWORD的组成为:[Table Offset][BIR2:0]. + // 由于Table Offset是8字节对齐的,因此mask掉该dword的BIR部分,就是table offset的值 + uint32_t dword2; // 该DWORD的组成为:[Pending Bit Offset][Pending Bit BIR2:0]. + // 由于Pending Bit Offset是8字节对齐的,因此mask掉该dword的BIR部分,就是Pending Bit Offset的值 +}; + /** * @brief msi描述符 * @@ -40,7 +77,7 @@ struct msi_desc_t struct pci_device_structure_header_t *pci_dev; // 对应的pci设备的结构体 struct msi_msg_t msg; // msi消息 uint16_t msi_index; // msi描述符的index - struct pci_msi_desc_t pci; // 与pci相关的msi描述符数据 + struct pci_msi_desc_t pci; // 与pci相关的msi描述符数据 }; /** @@ -54,7 +91,7 @@ struct msi_desc_t * * @return 返回码 */ -int pci_enable_msi(struct msi_desc_t * msi_desc); +int pci_enable_msi(struct msi_desc_t *msi_desc); /** * @brief 禁用指定设备的msi diff --git a/kernel/driver/pci/pci.c b/kernel/driver/pci/pci.c index f3b54df3..2fecadca 100644 --- a/kernel/driver/pci/pci.c +++ b/kernel/driver/pci/pci.c @@ -503,7 +503,7 @@ void pci_get_device_structure(uint8_t class_code, uint8_t sub_class, struct pci_ * @param cap_type c要寻找的capability类型 * @return uint64_t cap list的偏移量 */ -uint32_t pci_enumerate_capability_list(struct pci_device_structure_header_t *pci_dev, int cap_type) +int32_t pci_enumerate_capability_list(struct pci_device_structure_header_t *pci_dev, uint32_t cap_type) { uint32_t cap_offset; switch (pci_dev->HeaderType) @@ -524,7 +524,8 @@ uint32_t pci_enumerate_capability_list(struct pci_device_structure_header_t *pci while (1) { tmp = pci_read_config(pci_dev->bus, pci_dev->device, pci_dev->func, cap_offset); - if (tmp & 0xff != cap_type) + kdebug("tmp & 0xff=%#010lx",tmp & 0xff); + if ((tmp & 0xff) != cap_type) { if ((tmp & 0xff00) >> 8) { diff --git a/kernel/driver/pci/pci.h b/kernel/driver/pci/pci.h index 559b3da3..ea181331 100644 --- a/kernel/driver/pci/pci.h +++ b/kernel/driver/pci/pci.h @@ -19,6 +19,13 @@ void pci_init(); struct pci_device_structure_header_t { struct List list; + + // 包含msix table地址的bar的mmio基地址 + uint64_t msix_mmio_vaddr; + uint64_t msix_mmio_size; // msix映射长度 + uint32_t msix_offset; // msix表的offset + uint16_t msix_table_size; // msix表的表项数量 + // ==== 以下三个变量表示该结构体所处的位置 uint8_t bus; uint8_t device; @@ -43,7 +50,7 @@ struct pci_device_structure_header_t // | bit7 | bit6 | Bits 5-4 | Bits 3-0 | // | BIST Capable | Start BIST | Reserved | Completion Code | // for more details, please visit https://wiki.osdev.org/PCI -} __attribute__((packed)); +}; /** * @brief 表头类型为0x0的pci设备结构 @@ -215,4 +222,4 @@ void pci_get_device_structure(uint8_t class_code, uint8_t sub_class, struct pci_ * @param cap_type c要寻找的capability类型 * @return uint64_t cap list的偏移量 */ -uint32_t pci_enumerate_capability_list(struct pci_device_structure_header_t *pci_dev, int cap_type); \ No newline at end of file +int32_t pci_enumerate_capability_list(struct pci_device_structure_header_t *pci_dev, uint32_t cap_type); \ No newline at end of file diff --git a/kernel/driver/usb/xhci/xhci.c b/kernel/driver/usb/xhci/xhci.c index 92346644..dcfaa3e6 100644 --- a/kernel/driver/usb/xhci/xhci.c +++ b/kernel/driver/usb/xhci/xhci.c @@ -593,6 +593,7 @@ uint64_t xhci_hc_irq_install(uint64_t irq_num, void *arg) msi_desc.edge_trigger = info->edge_trigger; msi_desc.processor = info->processor; msi_desc.pci.msi_attribute.is_64 = 1; + msi_desc.pci.msi_attribute.is_msix=1; // todo: QEMU是使用msix的,因此要先在pci中实现msix io_mfence(); int retval = pci_enable_msi(&msi_desc); diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 2a2c6075..4f49cd53 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -249,7 +249,8 @@ unsigned long page_init(struct Page *page, ul flags) { ++page->ref_counts; barrier(); - ++page->zone->total_pages_link; + if (page->zone) + ++page->zone->total_pages_link; } page->anon_vma = NULL; spin_init(&(page->op_lock));