This commit is contained in:
Ciro Santilli
2017-08-05 21:18:57 +01:00
parent 83411a0597
commit d871c008fb
5 changed files with 161 additions and 8 deletions

View File

@@ -70,3 +70,32 @@ BR2_PACKAGE_HOST_DTC=y
# TODO: have a look at: https://github.com/kaiwan/device-memory-readwrite
#
BR2_PACKAGE_DEVMEM2=y
# Provides setpci and a lspci more advanced than Busybox's
#
# setpci can read and write to PCI configuration registers.
#
# Read is possible from Linux with:
#
# hexdump /sys/bus/pci/devices/0000:00:05.0/config
#
# but setpci provies nice human readable register names, e.g.:
#
# setpci --dumpregs
#
# then and then get the values with either bus or device id:
#
# setpci -s 0000:00:05.0 BASE_ADDRESS_0
# setpci -d 1234:11e9 BASE_ADDRESS_0
#
# Note however that BASE_ADDRESS_0 also appears when you do:
#
# lspci -v
#
# Then you can try messing with that address with:
#
# devmem2 0xfeb52000 w 0x12345678
#
# which for our pci_min device fires interrupts.
#
BR2_PACKAGE_PCIUTILS=y

View File

@@ -138,11 +138,12 @@ CONFIG_ARM64_PTDUMP=y
## UIO
# Userspace drivers.
# Userspace drivers: allow you to handle IRQs and do memory IO from userland through a /dev file.
#
# Superseded by the more featureful VFIO.
#
# Documentation/DocBook/uio-howto.tmpl
# Documentation/DocBook/uio-howto.tmpl contains actual userland examples
# for the generic examples under drivers/uio
#
# UIO interface in a nutshell:
#

View File

@@ -0,0 +1,103 @@
/*
Adapted from: http://free-electrons.com/pub/mirror/devmem2.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <stdint.h>
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
uintmax_t read_result, writeval;
off_t target;
int access_type = 'w';
if(argc < 2) {
fprintf(stderr, "\nUsage:\t%s { address } [ type [ data ] ]\n"
"\taddress : memory address to act upon\n"
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord\n"
"\tdata : data to be written\n\n",
argv[0]);
exit(1);
}
target = strtoul(argv[1], 0, 0);
if(argc > 2)
access_type = tolower(argv[2][0]);
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
printf("/dev/mem opened.\n");
fflush(stdout);
/* Map one page */
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
virt_addr = map_base + (target & MAP_MASK);
switch(access_type) {
case 'b':
read_result = *((uint8_t *) virt_addr);
break;
case 'h':
read_result = *((uint16_t *) virt_addr);
break;
case 'w':
read_result = *((uint32_t *) virt_addr);
break;
case 'q':
read_result = *((uint64_t *) virt_addr);
break;
default:
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
exit(2);
}
printf("Value at address 0x%jX (%p): 0x%jX\n", (uintmax_t)target, virt_addr, (uintmax_t)read_result);
fflush(stdout);
if(argc > 3) {
writeval = strtoul(argv[3], 0, 0);
switch(access_type) {
case 'b':
*((uint8_t *) virt_addr) = writeval;
read_result = *((uint8_t *) virt_addr);
break;
case 'h':
*((uint16_t *) virt_addr) = writeval;
read_result = *((uint16_t *) virt_addr);
break;
case 'w':
*((uint32_t *) virt_addr) = writeval;
read_result = *((uint32_t *) virt_addr);
break;
case 'q':
*((uint64_t *) virt_addr) = writeval;
read_result = *((uint64_t *) virt_addr);
break;
}
printf("Written 0x%X; readback 0x%jX\n", (unsigned int)writeval, (unsigned int)read_result);
fflush(stdout);
}
if(munmap(map_base, MAP_SIZE) == -1) FATAL;
close(fd);
return 0;
}

View File

@@ -1,9 +1,15 @@
/*
Adapted from: https://yurovsky.github.io/2014/10/10/linux-uio-gpio-interrupt/
modprobe uio_pdrv_genirq
modprobe uio_pci_generic
echo '1234 11e9' > /sys/bus/pci/drivers/uio_pci_generic/new_id
/uio_read.out &
TODO get working.
TODO get working. The problem now is how to generate IRQs to test our IRQ handling:
- a synchronous IRQ needs regwrites to be kicked off, but mmap does not seem to work with this driver
- a custom PCI periodic timer device would also need to be initiazed by some regwrite, otherwise
early interrupts before the OS is setup would crash everything?
Handle interrupts from userland and print a message to stdout.
*/
@@ -17,17 +23,31 @@ Handle interrupts from userland and print a message to stdout.
#include <sys/types.h>
#include <unistd.h> /* write */
#include <assert.h>
#include <sys/mman.h>
int main(int argc, char **argv)
{
char *dev;
if (argc < 2) {
char *dev = "/dev/uio0";
if (argc > 1) {
dev = argv[1];
exit(EXIT_FAILURE);
}
int fd = open("/dev/uio", O_RDWR);
int fd = open(dev, O_RDWR);
if (fd < 0) {
perror("open");
exit(EXIT_FAILURE);
}
/* TODO not supported by this kernel module? */
/*int *addr = mmap(NULL, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);*/
/*if (addr == MAP_FAILED) {*/
/*perror("mmap");*/
/*assert(0);*/
/*}*/
/**addr = 0x12345678;*/
while (1) {
uint32_t info = 1;
size_t nb = write(fd, &info, sizeof(info));

2
qemu

Submodule qemu updated: 6ba2bd0c90...019bbe59d6