Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write kernel space memory (physical address) to a file using O_DIRECT?

Tags:

People also ask

How do I transfer data from kernel space to user space?

The function copy_to_user is used to copy data from the kernel address space to the address space of the user program. For example, to copy a buffer which has been allocated with kmalloc to the buffer provided by the user.

How do I find physical address in Linux?

Linux. Open a terminal and use the ifconfig command. The MAC address will be listed next to HWaddr. If your Linux OS does not have the ifconfig command, you may also use the ip addr command.

Can kernel access physical memory?

Yes at OS level you have access to the physical memory.

Which function is used to copy data from kernel space to user space?

The copy_to_user function copies a block of data from the kernel into user space.


I want to write a physical memory to a file. The memory itself will not be touched again, thus I want to use O_DIRECT to gain the best write performance.

My first idea was to open /dev/mem and mmap the memory and write everything to a file, which is opened with O_DIRECT. The write call fails (EFAULT) on the memory-address returned by mmap. If I do not use O_DIRECT, it results in a memcpy.

#include <cstdint>
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <sys/mman.h>

#define PRINT_ERRNO_REASON(reason)  \
        case reason: { std::cout << #reason << std::endl; } break;

void write_page_aligned_buffer(int out_fd)
{
    const ssize_t PAGE_SIZE = getpagesize();

    void* page_aligned_buffer = memalign(PAGE_SIZE, PAGE_SIZE);
    if(!page_aligned_buffer)
    {
        std::cout << "Could not allocate page aligned buffer." << std::endl;
        return;
    }

    std::cout << "Allocated a buffer at address " << page_aligned_buffer << "." << std::endl;

    if(write(out_fd, page_aligned_buffer, PAGE_SIZE) < 0)
    {
        std::cout << "Could not write page-aligned user buffer to tmp-file. Quitting..." << std::endl;
        std::cout << "Reason of fail is ";

        switch(errno)
        {
            PRINT_ERRNO_REASON(EAGAIN);
            PRINT_ERRNO_REASON(EBADF);
            PRINT_ERRNO_REASON(EFAULT);
            PRINT_ERRNO_REASON(EFBIG);
            PRINT_ERRNO_REASON(EINTR);
            PRINT_ERRNO_REASON(EINVAL);
            PRINT_ERRNO_REASON(EIO);
            PRINT_ERRNO_REASON(ENOSPC);
            PRINT_ERRNO_REASON(EPIPE);
        default:
            std::cout << "Unknown" << std::endl;
        }
    }
    else
    {
        std::cout << "Successfully written user-page-aligned buffer." << std::endl;
    }

    free(page_aligned_buffer);
}

int main()
{
    const ssize_t PAGE_SIZE = getpagesize();

    // number of pages to copy
    const uint32_t PAGES_TO_COPY = 1;

    char* tmp_file_name = 0;
    int tmp_file_fd = -1;
    ssize_t bytes_copied = 0;

    std::cout << "Copying " << PAGES_TO_COPY << " pages with PAGE_SIZE = " << PAGE_SIZE << std::endl;
    std::cout << "Copying " << PAGES_TO_COPY * PAGE_SIZE / 1024 << " kBytes." << std::endl << std::endl;

    uid_t user_id = geteuid();
    if(user_id)
    {
        std::cout << "We need to be root. I am euid == " << user_id << ". Quitting..." << std::endl;
        return 0;
    }
    else
    {
        seteuid(0);
        setuid(0);
    }

    // get the file descriptor
    int mem_fd = open("/dev/mem", O_RDONLY);
    if(mem_fd < 0)
    {
        std::cout << "Could not open /dev/mem. Quitting..." << std::endl;
        std::cout << "Reason of fail is ";

        switch(errno)
        {
            PRINT_ERRNO_REASON(EACCES);
            PRINT_ERRNO_REASON(EEXIST);
            PRINT_ERRNO_REASON(EINTR);
            PRINT_ERRNO_REASON(EINVAL);
            PRINT_ERRNO_REASON(EIO);
            PRINT_ERRNO_REASON(EISDIR);
            PRINT_ERRNO_REASON(ELOOP);
            PRINT_ERRNO_REASON(EMFILE);
            PRINT_ERRNO_REASON(ENFILE);
            PRINT_ERRNO_REASON(ENOENT);
            PRINT_ERRNO_REASON(ENOSR);
            PRINT_ERRNO_REASON(ENOSPC);
            PRINT_ERRNO_REASON(ENOTDIR);
            PRINT_ERRNO_REASON(ENXIO);
            PRINT_ERRNO_REASON(EOVERFLOW);
            PRINT_ERRNO_REASON(EROFS);
            PRINT_ERRNO_REASON(EAGAIN);
            PRINT_ERRNO_REASON(ENAMETOOLONG);
            PRINT_ERRNO_REASON(ENOMEM);
            PRINT_ERRNO_REASON(ETXTBSY);
        default:
            std::cout << "Unknown" << std::endl;
        }
        return 0;
    }

    // get read pointer
    uint8_t* mem_ptr = static_cast<uint8_t*>(mmap(0,
            PAGE_SIZE,
            PROT_READ,
            MAP_SHARED,
            mem_fd,
            PAGE_SIZE));
    if(mem_ptr == MAP_FAILED)
    {
        std::cout << "Could not mmap /dev/mem. Quitting..." << std::endl;
        std::cout << "Reason of fail is ";

        switch(errno)
        {
            PRINT_ERRNO_REASON(EACCES);
            PRINT_ERRNO_REASON(EAGAIN);
            PRINT_ERRNO_REASON(EBADF);
            PRINT_ERRNO_REASON(EINVAL);
            PRINT_ERRNO_REASON(ENFILE);
            PRINT_ERRNO_REASON(ENODEV);
            PRINT_ERRNO_REASON(ENOMEM);
            PRINT_ERRNO_REASON(EPERM);
            PRINT_ERRNO_REASON(ETXTBSY);
        default:
            std::cout << "Unknown" << std::endl;
        }
        goto CLEANUP_FD_DEV_MEM;
    }

    tmp_file_name = tempnam("/tmp", "prefix");
    if(!tmp_file_name)
    {
        std::cout << "Could not get a free tmp-filename";
        goto CLEANUP_MMAP_DEV_MEM;
    }

    // if O_DIRECT is omitted the example will work
    tmp_file_fd = open(tmp_file_name,
            O_WRONLY | O_CREAT | O_DIRECT | O_TRUNC,
            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if(tmp_file_fd < 0)
    {
        std::cout << "Could not create tmp file with O_DIRECT." << std::endl;
        goto CLEANUP_MMAP_DEV_MEM;
    }

    write_page_aligned_buffer(tmp_file_fd);

    // everything worked so lets start the copying
    for(uint i = 0; i < PAGES_TO_COPY; i++)
    {
        // check memory
        // snip
        for(int i = 0; i < PAGE_SIZE; i += 32)
        {
            printf("%02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X\n",
                    mem_ptr[i + 0], mem_ptr[i + 1], mem_ptr[i + 2], mem_ptr[i + 3],
                    mem_ptr[i + 4], mem_ptr[i + 5], mem_ptr[i + 6], mem_ptr[i + 7],
                    mem_ptr[i + 8], mem_ptr[i + 9], mem_ptr[i + 10], mem_ptr[i + 11],
                    mem_ptr[i + 12], mem_ptr[i + 13], mem_ptr[i + 14], mem_ptr[i + 15],
                    mem_ptr[i + 16], mem_ptr[i + 17], mem_ptr[i + 18], mem_ptr[i + 19],
                    mem_ptr[i + 20], mem_ptr[i + 21], mem_ptr[i + 22], mem_ptr[i + 23],
                    mem_ptr[i + 24], mem_ptr[i + 25], mem_ptr[i + 26], mem_ptr[i + 27],
                    mem_ptr[i + 28], mem_ptr[i + 29], mem_ptr[i + 30], mem_ptr[i + 31]);
        }
        std::cout << "\n";
        // endsnip

        bytes_copied = write(tmp_file_fd, mem_ptr, PAGE_SIZE);
        if(bytes_copied < 0)
        {
            std::cout << "Could not write to tmp-file. Quitting..." << std::endl;
            std::cout << "Reason of fail is ";

            switch(errno)
            {
                PRINT_ERRNO_REASON(EAGAIN);
                PRINT_ERRNO_REASON(EBADF);
                PRINT_ERRNO_REASON(EFAULT);
                PRINT_ERRNO_REASON(EFBIG);
                PRINT_ERRNO_REASON(EINTR);
                PRINT_ERRNO_REASON(EINVAL);
                PRINT_ERRNO_REASON(EIO);
                PRINT_ERRNO_REASON(ENOSPC);
                PRINT_ERRNO_REASON(EPIPE);
            default:
                std::cout << "Unknown" << std::endl;
            }
            goto CLEANUP_FD_TMP_FILE;
        }
    }

CLEANUP_FD_TMP_FILE:
    if(tmp_file_name)
    {
        if(close(tmp_file_fd))
        {
            std::cout << "Could close tmp-file " << tmp_file_name << "." << std::endl;
        }

        if(remove(tmp_file_name))
        {
            std::cout << "Could remove tmp-file " << tmp_file_name << "." << std::endl;
        }

        free(tmp_file_name);
    }

CLEANUP_MMAP_DEV_MEM:
    if(munmap(mem_ptr, PAGE_SIZE))
    {
        std::cout << "munmap failed." << std::endl;
    }

CLEANUP_FD_DEV_MEM:
    if(close(mem_fd))
    {
        std::cout << "Could not close /dev/mem filedescriptor." << std::endl;
    }

    return 0;
}

The next step would be to write a char-device or a block-device, which handles the memory-transfer. But how to bypass the copy_to_user? The target system is an embedded PowerPC architecture, with the drawback, that writing user-memory to the harddrive (using the DMA-controller) is faster than memcpy from RAM to RAM. Thus I must bypass the page-cache.

Best regards

Friedrich