#include #include #include #include #include #include #include #include #include #include #include #include #include "reg_api.h" const char glob_uuid[] = "d0b24768-9c51-11e7-8de5-c7b0c2769e69"; #define TEST_SIZE 1024 * 1024 #define EOR (1 << 30) #define OWN (1 << 31) #define NUM_TX_DESC 64 /* * @fd: container fd * @sz: requested size * @vaddr: virtual address */ int dma_map_type1(int fd, unsigned long sz, void *vaddr) { int ret; struct vfio_iommu_type1_dma_map dma_map; /* Allocate some space and setup a DMA mapping */ vaddr = mmap(NULL, (size_t)sz, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (vaddr == MAP_FAILED) { printf("Failed to map memory\n"); return -ENOMEM; } memset(&dma_map, 0, sizeof(dma_map)); dma_map.argsz = sizeof(dma_map); dma_map.vaddr = (unsigned long)vaddr; dma_map.size = sz; /* FIXME proper offset to alloc multiple bus addresses */ dma_map.iova = 0; dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; ret = ioctl(fd, VFIO_IOMMU_MAP_DMA, &dma_map); if (ret) printf("Failed to map DMA memory (%s)\n", strerror(errno)); return ret; } int dma_unmap_type1(int fd, unsigned long sz, void *vaddr) { int ret; struct vfio_iommu_type1_dma_unmap dma_unmap; memset(&dma_unmap, 0, sizeof(dma_unmap)); dma_unmap.argsz = sizeof(dma_unmap); dma_unmap.size = sz; dma_unmap.iova = 0; ret = ioctl(fd, VFIO_IOMMU_UNMAP_DMA, &dma_unmap); if (ret) printf("Failed to unmap DMA memory (%s)\n", strerror(errno)); ret = munmap(vaddr, (size_t)sz); if (vaddr == MAP_FAILED) { printf("Failed to unmap memory\n"); return -ENOMEM; } return ret; } struct rtl_8169_descriptor { unsigned int command; /* command/status uint32_t */ unsigned int vlan; /* currently unused */ unsigned int low_buf; /* low 32-bits of physical buffer address */ unsigned int high_buf; /* high 32-bits of physical buffer address */ }; struct TxDesc { uint32_t opts1; uint32_t opts2; uint64_t addr; }; void dma_xmit(int container, uint32_t tx_desc, void *map) { //uint32_t opts[2]; //opts[0] = 0x00; /* VLAN tag */ //opts[1] = OWN; /* Descriptor is owned by NIC */ //io_write8(map + 0x38, 0x40); dma_unmap_type1(container, TEST_SIZE, &tx_desc); } int rtl_8169_remap(int container, void *map) { uint32_t tx_desc = 0; dma_map_type1(container, TEST_SIZE, &tx_desc); io_write8(map + 0x50, 0xc0); io_write32(map + 0x44, 0x0000e70f); /* RxConfig = RXFTH: unlimited, MXDMA: unlimited, AAP: set (promisc. mode set) */ /* Already enabled, do we need a reset? */ //io_write8(map + 0x37, 0x04); /* Enable Tx in the Command register, required before setting TxConfig */ io_write32(map + 0x40, 0x03000700); /* TxConfig = IFG: normal, MXDMA: unlimited */ io_write16(map + 0xDA, 0x1FFF); /* Max rx packet size */ io_write8(map + 0xEC, 0x3B); /* max tx packet size */ //io_write32(map + 0x20, (unsigned long)&tx_desc); /* Tell the NIC where the first Tx descriptor is */ //io_write32(map + 0xE4, (unsigned long)&rx_desc); /* Tell the NIC where the first Rx descriptor is */ /* Done */ //io_write8(map + 0x37, 0x0C); /* Enable Rx/Tx in the Command register */ io_write8(map + 0x50, 0x00); /* Lock config registers */ dma_xmit(container, tx_desc, map); return 0; } void rtl_8169_dump(int container, void *map) { printf("0x%02x\n", io_read8(map + 0x50)); printf("0x%08x\n", io_read32(map + 0x44)); printf("0x%02x\n", io_read8(map + 0x37)); printf("0x%08x\n", io_read32(map + 0x40)); printf("0x%04x\n", io_read16(map + 0xDA)); printf("0x%02x\n", io_read8(map + 0xEC)); } int main(int argc, char* argv[]) { int container, group, device, i; struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) }; struct vfio_region_info region_info = { .argsz = sizeof(region_info) }; void *map; /* Create a new container */ container = open("/dev/vfio/vfio", O_RDWR); if (ioctl(container, VFIO_GET_API_VERSION) != VFIO_API_VERSION) { printf("Unknown API version\n"); goto out; } if (!ioctl(container, VFIO_CHECK_EXTENSION, VFIO_TYPE1_IOMMU)) { printf("Doesn't support the IOMMU driver we want\n"); goto out; } group = open("/dev/vfio/11", O_RDWR); /* Test the group is viable and available */ ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)) { printf("Group is not viable\n"); goto out; } /* Set container */ ioctl(group, VFIO_GROUP_SET_CONTAINER, &container); ioctl(group, VFIO_GROUP_GET_STATUS, &group_status); if (!(group_status.flags & VFIO_GROUP_FLAGS_CONTAINER_SET)) { printf("Container not set\n"); goto out; } /* Enable the IOMMU model we want */ ioctl(container, VFIO_SET_IOMMU, VFIO_TYPE1_IOMMU); /* Get addition IOMMU info */ ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info); /* Get a file descriptor for the device */ device = ioctl(group, VFIO_GROUP_GET_DEVICE_FD, glob_uuid); /* Test and setup the device */ ioctl(device, VFIO_DEVICE_GET_INFO, &device_info); printf("Device supports %d regions, %d irqs\n", device_info.num_regions, device_info.num_irqs); for (i = 0; i < device_info.num_regions; i++) { region_info.index = i; if (ioctl(device, VFIO_DEVICE_GET_REGION_INFO, ®ion_info)) continue; if (!region_info.size) { //printf("Region:%d unimplemented PCI BAR\n", i); continue; } if (region_info.flags & VFIO_REGION_INFO_FLAG_MMAP) { printf("## Region:%d size %lu, offset 0x%lx, flags 0x%x ##\n", i, (unsigned long)region_info.size, (unsigned long)region_info.offset, region_info.flags); map = mmap(NULL, (size_t)region_info.size, PROT_READ | PROT_WRITE, MAP_SHARED, device, (off_t)region_info.offset); if (map == MAP_FAILED) { printf("mmap failed\n"); continue; } //rtl_8169_dump(container, map); rtl_8169_remap(container, map); #ifdef DBG int j; size_t sz; sz = region_info.size / sizeof(uint32_t *); for (j = 0; j < sz; j++) { if (!(j % 8) && j != 0) printf("\n"); printf("%08x ", ((uint32_t *)map)[j]); } printf("\n"); #endif /* fwrite(map, 1, region_info.size > 16 ? 16 : region_info.size, stdout); printf("]\n"); */ munmap(map, (size_t)region_info.size); } } return 0; out: return -1; }