diff --git a/sw/test/Makefile b/sw/test/Makefile new file mode 100644 index 0000000..d2f22ca --- /dev/null +++ b/sw/test/Makefile @@ -0,0 +1,18 @@ + +BIN=pcie_dma_test + +SRCS=$(wildcard *.c) +OBJS=$(SRCS:.c=.o) + +all: $(BIN) + +$(BIN): $(OBJS) + gcc -o $@ $(CFLAGS) $(LDFLAGS) $^ + +%.o: %.c + gcc -c -o $@ $(CFLAGS) $< + +.PHONY: clean +clean: + rm -rf $(OBJS) + rm -rf $(BIN) \ No newline at end of file diff --git a/sw/test/enable_bus_mastering.sh b/sw/test/enable_bus_mastering.sh new file mode 100644 index 0000000..c98384c --- /dev/null +++ b/sw/test/enable_bus_mastering.sh @@ -0,0 +1,2 @@ +# Enable bus mastering +sudo setpci -d 10EE:9038 COMMAND=7 \ No newline at end of file diff --git a/sw/test/pcie_dma_test.c b/sw/test/pcie_dma_test.c new file mode 100644 index 0000000..c26a24e --- /dev/null +++ b/sw/test/pcie_dma_test.c @@ -0,0 +1,102 @@ +#define _XOPEN_SOURCE 700 +#include /* open */ +#include /* uint64_t */ +#include /* printf */ +#include /* size_t */ +#include /* pread, sysconf */ +#include + +typedef struct { + uint64_t pfn : 55; + unsigned int soft_dirty : 1; + unsigned int file_page : 1; + unsigned int swapped : 1; + unsigned int present : 1; +} PagemapEntry; + +/* Parse the pagemap entry for the given virtual address. + * + * @param[out] entry the parsed entry + * @param[in] pagemap_fd file descriptor to an open /proc/pid/pagemap file + * @param[in] vaddr virtual address to get entry for + * @return 0 for success, 1 for failure + */ +int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr) +{ + size_t nread; + ssize_t ret; + uint64_t data; + uintptr_t vpn; + + vpn = vaddr / sysconf(_SC_PAGE_SIZE); + nread = 0; + while (nread < sizeof(data)) { + ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread, + vpn * sizeof(data) + nread); + nread += ret; + if (ret <= 0) { + return 1; + } + } + entry->pfn = data & (((uint64_t)1 << 55) - 1); + entry->soft_dirty = (data >> 55) & 1; + entry->file_page = (data >> 61) & 1; + entry->swapped = (data >> 62) & 1; + entry->present = (data >> 63) & 1; + return 0; +} + +/* Convert the given virtual address to physical using /proc/PID/pagemap. + * + * @param[out] paddr physical address + * @param[in] pid process to convert for + * @param[in] vaddr virtual address to get entry for + * @return 0 for success, 1 for failure + */ +int virt_to_phys_user(uintptr_t *paddr, uintptr_t vaddr) +{ + char pagemap_file[BUFSIZ]; + int pagemap_fd; + + snprintf(pagemap_file, sizeof(pagemap_file), "/proc/self/pagemap"); + pagemap_fd = open(pagemap_file, O_RDONLY); + if (pagemap_fd < 0) { + return 1; + } + PagemapEntry entry; + if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) { + return 1; + } + close(pagemap_fd); + *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE)); + return 0; +} + +int main(void) +{ + printf("Allocating 1024 bytes as source\n"); + uintptr_t src = (uintptr_t)malloc(1024); + printf("Virtual address: %lx\n", src); + uintptr_t src_phys; + virt_to_phys_user(&src_phys, src); + printf("Physical address: %lx\n", src_phys); + printf("\n\n"); + + printf("Allocating 1024 bytes as destination\n"); + uintptr_t dst = (uintptr_t)malloc(1024); + printf("Virtual address: %lx\n", dst); + uintptr_t dst_phys; + virt_to_phys_user(&dst_phys, dst); + printf("Physical address: %lx\n", dst_phys); + printf("\n\n"); + + printf("mmaping PCIe space\n"); + // this is hardcoded, seems to be deterministic. + uint32_t pcie_physical_base_offset = 0xfe800000; + int fd = open("/dev/mem", O_RDWR|O_SYNC); + uint32_t* pcie_base = (uint32_t*)mmap(0, 64, PROT_READ|PROT_WRITE, MAP_SHARED, fd, pcie_physical_base_offset); + uintptr_t pcie_physical_sanity_check; + virt_to_phys_user(&pcie_physical_sanity_check, (uintptr_t)pcie_base); + printf("Virtual PCIe Base: %p\n", pcie_base); + printf("Physical PCIe Base: %lx\n", pcie_physical_sanity_check); +} \ No newline at end of file