Difference between C mmap and Java MappedByteBuffer in Linux

0

I am having trouble using a Linux UIO device from Java. The idea behind this device is that you have a peripheral with a set of registers that are memory mapped. In order to access these registers for reading and writing, the kernel module exposes a device file (e.g. /dev/ui0) which can be mmapped and the pointer used to interact with the registers. The following code in C++ demonstrates the behavior:

#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
#include <cstdio>

int main() {
    size_t filesize = 256;
    //Open file
    int fd = open("/dev/uio0", O_RDWR);
    assert(fd != -1);
    //Execute mmap
    void* mmappedData = mmap(NULL, filesize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

    assert(mmappedData != MAP_FAILED);
    //Write the mmapped data to stdout (= FD #1)
    volatile int* regs = (int*) mmappedData;
    for(int i =0; i < 7; i++) printf("REG[%d]=0x%08x\n", i, regs[i]);
    //Cleanup
    int rc = munmap(mmappedData, filesize);
    assert(rc == 0);
    close(fd);
    return 0;
}

The output of the code above is:

REG[0]=0x00000001
REG[1]=0x00000001
REG[2]=0x00000004
REG[3]=0x00000024
REG[4]=0xa0109146
REG[5]=0x00000024
REG[6]=0x000000b9

Which is correct. However, I am attempting to achieve the same with the following Java code:

public class RegisterFile {
    private final RandomAccessFile mRegisterSetFile;
    private final MappedByteBuffer mByteBuffer;

    public RegisterFile(String deviceName) throws IOException {
        LOGGER.log(Level.INFO, "Mapping device: {0}", deviceName);
        mRegisterSetFile = new RandomAccessFile(deviceName, "rws");
        LOGGER.log(Level.INFO, "File length is: {0}", mRegisterSetFile.length());
        mByteBuffer = mRegisterSetFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 256);
        dump();
    }


    public final void dump() throws IOException {
        StringBuilder builder = new StringBuilder("\n");
        for (Address address : Address.values()) {
            builder.append(String.format("REGFILE[%02d] = 0x%08x\n", address.get(), mByteBuffer.getInt(address.get())));
        }
        LOGGER.log(Level.INFO, "{0}", builder.toString());
    }
}

However the result is:

[junit] Jan 13, 2018 3:50:26 PM org.apache.hadoop.io.compress.gzipFpga.RegisterFile <init>
[junit] INFO: Mapping device: /dev/uio0
[junit] Jan 13, 2018 3:50:26 PM org.apache.hadoop.io.compress.gzipFpga.RegisterFile <init>
[junit] INFO: File length is: 0
[junit] Jan 13, 2018 3:50:26 PM org.apache.hadoop.io.compress.gzipFpga.GzipFpgaCompressor gzipCoreAvailable
[junit] SEVERE: Unable to read from GZIP core devices.
[junit] java.io.IOException: Invalid argument
[junit]     at sun.nio.ch.FileDispatcherImpl.truncate0(Native Method)
[junit]     at sun.nio.ch.FileDispatcherImpl.truncate(FileDispatcherImpl.java:80)
[junit]     at sun.nio.ch.FileChannelImpl.map(FileChannelImpl.java:906)
[junit]     at org.apache.hadoop.io.compress.gzipFpga.RegisterFile.<init>(RegisterFile.java:85)

I believe the reason is that File.length() returns 0, because this is not a real file. Is there a way to force the mapping of the file?

java
c++
linux
mmap
asked on Stack Overflow Jan 13, 2018 by rhobincu

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0