Background:
I am currently writing a device driver for Edu device in qemu (RISC-V). From this question, I find that there is already a device driver for such device.
Outline:
I would like to write an 8-byte value to the memory mapped IO register (of Edu device) at address 0x80
, and then read the same address to see if we can get the same value.
- Edu device introduction
- The driver code I wrote (for
read()
andwrite()
) - User mode code for testing
read()
andwrite()
- Problem I met
Edu device introduction
According to this line of the documentation of Edu device, it says size == 4 or size == 8 accesses are allowed for addresses >= 0x80. Such constraint is shown here for edu_mmio_read()
and here for edu_mmio_write()
in Edu device source code.
The driver code I wrote
In the driver code, for both read()
and write()
, it seems that it only handles reading/writing values of size 4 bytes, not 8 bytes. So I added something new to those two functions to support 8-byte values read/write:
static ssize_t read(struct file *filp, char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
u32 kbuf32;
u64 kbuf64;
if (*off % 4 || len == 0) {
ret = 0;
} else {
switch (len)
{
case 4:
kbuf32 = ioread32(mmio + *off);
if (copy_to_user(buf, (void *)&kbuf32, len)) {
ret = -EFAULT;
} else {
ret = 4;
(*off)++;
}
break;
case 8:
kbuf64 = ioread64(mmio + *off);
if (copy_to_user(buf, (void *)&kbuf64, len)) {
ret = -EFAULT;
} else {
ret = 8;
(*off)++;
}
break;
default:
ret = -EFAULT;
break;
}
}
return ret;
}
static ssize_t write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{
ssize_t ret;
u32 kbuf32; /* for size == 4 */
u64 kbuf64; /* for size == 8 */
ret = len;
if (!(*off % 4)) {
switch (len) {
case 4:
/* copy buf to kbuf32 */
if (copy_from_user((void *)&kbuf32, buf, len)) {
ret = -EFAULT;
} else {
iowrite32(kbuf32, mmio + *off);
}
break;
case 8:
if (copy_from_user((void *)&kbuf64, buf, len)) {
ret = -EFAULT;
} else {
iowrite64(kbuf64, mmio + *off);
}
break;
default:
ret = -EFAULT;
break;
}
}
return ret;
}
User mode code for testing
In my user mode testing code, I did the following:
// open the device - succeed
// fd2 - the file descriptor representing the opened edu device
uint64_t val64 = 0x8b320000; // a random 64-bit value
unsigned long ret = -1; // retval
// write val64 to 0x80
lseek(fd2, 0x80, SEEK_SET); // seek to address 0x80 - dma.src in edu device source code (line 281)
ret = write(fd2, &val64, sizeof(uint64_t));
if(ret == -1) printf("write to dma src failed\n");
else printf("written %llx to dma src\n", val64);
// reset val64
val64 = 0;
// read what we have just written (sanity check)
lseek(fd2, 0x80, SEEK_SET);
ret = read(fd2, &ret64, sizeof(uint64_t));
if(ret == -1) printf("read from dma src failed\n");
else printf("sanity check: read dma src and we get - %llx\n", ret64);
Problem I met
However, when I tested the user mode code, it did write the value to address 0x80
, but failed to read it. I added some printf statements and noticed that the error occurred in read()
, corresponding to this line of code:
kbuf64 = ioread64(mmio + *off);
I found this page talking about the differences between IO access functions, and replaced ioread64()
with readq()
, but still did not solve the problem. The code stopped executing when kbuf64 = ioread64(mmio + *off);
is being executed. I also tried to add #define CONFIG_64BIT
, but still did not solve the error.
After that, I hit Ctrl+C but was unable to stop the user mode code. All I could do was to Ctrl+A then X to stop QEMU and restart again.
Note that for 4-byte values, read or write works well with ioread32()
and iowrite32()
.
May I know what I did wrong that caused the error of ioread64()
?