0

I need to access and write to some physical addresses in my RAM. I was looking at this answer and the definition of mmap.

If addr is NULL, then the kernel chooses the (page-aligned) address at which to create the mapping; this is the most portable method of creating a new mapping. If addr is not NULL, then the kernel takes it as a hint about where to place the mapping; on Linux, the kernel will pick a nearby page boundary (but always above or equal to the value specified by /proc/sys/vm/mmap_min_addr) and attempt to create the mapping there.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
        return 0;
    }

    off_t offset = strtoul(argv[1], NULL, 0);
    size_t len = strtoul(argv[2], NULL, 0);

    // Truncate offset to a multiple of the page size, or mmap will fail.
    size_t pagesize = sysconf(_SC_PAGE_SIZE);
    off_t page_base = (offset / pagesize) * pagesize;
    off_t page_offset = offset - page_base;

    int fd = open("/dev/mem", O_SYNC);
    unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
    if (mem == MAP_FAILED) {
        perror("Can't map memory");
        return -1;
    }

    size_t i;
    for (i = 0; i < len; ++i)
        printf("%02x ", (int)mem[page_offset + i]);

    return 0;
}

Why is the first argument to mmap function NULL ? Shouldn't it be page_base ? We want the mapping to start at page base and extend till offset. I have to do something similar where I have to copy an array of values into the RAM starting from exactly the same locations. Shouldn't this be the call to mmap:

unsigned char *mem = mmap(page_base, page_offset + len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page_base);

acevans
  • 13
  • 4

1 Answers1

1

mmap does not take in physical addresses - all these addresses are virtual. The first argument is a hint telling the virtual address where the mapping should be placed.

You do not need the virtual and physical addresses to be identical in order to be able to write at some physical address. Instead you'd just need to translate physical addresses to virtual addresses. What you need to do, however, is to use MAP_SHARED so that any changes you make into memory will be conveyed to /dev/mem and you won't have a private copy.


If you really want to do an identity mapping, then there is a special flag (or two) to mmap that you have to use for this to really work, which you should | with either MAP_PRIVATE or MAP_SHARED.

MAP_FIXED

Don't interpret addr as a hint: place the mapping at exactly that address. addr must be suitably aligned: for most architectures a multiple of the page size is sufficient; however, some architectures may impose additional restrictions. If the memory region specified by addr and len overlaps pages of any existing mapping(s), then the overlapped part of the existing mapping(s) will be discarded. If the specified address cannot be used, mmap() will fail.

Software that aspires to be portable should use the MAP_FIXED flag with care, keeping in mind that the exact layout of a process's memory mappings is allowed to change significantly between kernel versions, C library versions, and operating system releases. Carefully read the discussion of this flag in NOTES!

This is for example what ld.so uses to load the program at the predefined address

There is a safer version of the flag: MAP_FIXED_NOREPLACE in later Linux kernels, which does not evict the previous mapping if it exists, but returns an error.

  • To further emphasize: OP does not need an identity mapping, and there's no reason to believe it's even possible to make an identity mapping. That virtual address range might already be in use, or might be reserved or unmappable for some reason. The only time it's legitimate to use `MAP_FIXED` is to replace an existing mapping you already created, not because you think something new you're mapping is "supposed to be" at that address. – R.. GitHub STOP HELPING ICE Oct 12 '20 at 16:30
  • @R..GitHubSTOPHELPINGICE haha good point about MAP_PRIVATE, fixed. Well, `MAP_FIXED` should probably be used if you're making a dynamic linker loading exes into static addresses. – Antti Haapala -- Слава Україні Oct 12 '20 at 17:33
  • `MAP_FIXED` is inappropriate even for that. You want a reportable error if the mapping can't be made, not mapping over something random followed by runaway wrong code execution. – R.. GitHub STOP HELPING ICE Oct 12 '20 at 17:46