2

For some reason my mmap failed with an Invalid argument message even though my offset is page aligned. Page size is 4096 bytes. Also CONFIG_STRICT_DEVMEM is disabled, i.e. I can access memory above 1MB.

Here is my code:

void *mmap64;
off_t offset = 0x000000d9fcc000;
int memFd = open("/dev/mem", O_RDWR);
if (-1 == memFd)
  perror("Error ");

mmap64 = mmap(0, getpagesize(), PROT_WRITE | PROT_READ, MAP_SHARED, memFd, offset);
if (MAP_FAILED == mmap64) {
  perror("Error ");
  return -1;
}

Can someone explain why this is happening?

EDIT

Here is the strace of my code

execve("./to_phys_test", ["./to_phys_test", "-r"], [/* 18 vars */]) = 0
brk(0)                                  = 0x2012000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe240a2c000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=162063, ...}) = 0
mmap(NULL, 162063, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe240a04000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P \2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1840928, ...}) = 0
mmap(NULL, 3949248, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe240447000
mprotect(0x7fe240601000, 2097152, PROT_NONE) = 0
mmap(0x7fe240801000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1ba000) = 0x7fe240801000
mmap(0x7fe240807000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe240807000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe240a03000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe240a01000
arch_prctl(ARCH_SET_FS, 0x7fe240a01740) = 0
mprotect(0x7fe240801000, 16384, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ)     = 0
mprotect(0x7fe240a2e000, 4096, PROT_READ) = 0
munmap(0x7fe240a04000, 162063)          = 0
open("/dev/mem", O_RDWR)                = 3
open("/dev/my_kmodule", O_RDWR)    = 4
ioctl(4, 0x40086e00, 0x7ffc72b334b0)    = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe240a2b000
write(1, "sa.size = 44\n", 13)          = 13
write(1, "sa.addr_uint64_t = d9047000\n", 28) = 28
write(1, "sa.addr_void_ptr = 0xd9047000\n", 30) = 30
write(1, "PAGE_SIZE = 4096\n", 17)      = 17
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0xd9047000) = -1 EINVAL (Invalid argument)
dup(2)                                  = 5
fcntl(5, F_GETFL)                       = 0x8002 (flags O_RDWR|O_LARGEFILE)
brk(0)                                  = 0x2012000
brk(0x2033000)                          = 0x2033000
fstat(5, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 0), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe240a2a000
lseek(5, 0, SEEK_CUR)                   = -1 ESPIPE (Illegal seek)
write(5, "Error : Invalid argument\n", 25) = 25
close(5)                                = 0
munmap(0x7fe240a2a000, 4096)            = 0
exit_group(-1)                          = ?
+++ exited with 255 +++
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
flashburn
  • 4,180
  • 7
  • 54
  • 109
  • YOu use `mmap` and `perror` in a device driver? I have doubts ... And Joda conditions not using you should. Compilers checking will this for you. – too honest for this site Aug 25 '16 at 00:40
  • @Olaf No. Is this a wrong use of `mmap` and `perror`? – flashburn Aug 25 '16 at 00:42
  • Any reason you added the driver-tag then? Don't spam tags! – too honest for this site Aug 25 '16 at 00:42
  • 3
    Where does `d9fcc000` come from? What are you expecting to find there? If it's not a valid physcal address, `mmap` will return `EINVAL`. Also, have you looked to see if the kernel is telling you anything in `dmesg`? – Jonathon Reinhart Aug 25 '16 at 00:53
  • 2
    Note that `perror` doesn't cause the program to exit. So, if your `open("/dev/mem")` fails (because you don't have `CAP_SYS_RAWIO`), you'll `perror` the message, but then continue to try and pass `-1` for the `fd` argument to `mmap`. Also, this *is* x86, right? As this is an architecture-specific feature of the kernel, it would be best to indicate the architecture in your tags. – Jonathon Reinhart Aug 25 '16 at 01:04
  • Also, what is `getpagesize()` returning? There are so many possibilities you're not checking for here. – Jonathon Reinhart Aug 25 '16 at 01:16
  • Can you include the `strace` output for the `open` and `mmap` system calls from running this program? That will show exactly what the return values are, and also the args. (You used the same string for both `perror` calls, which is weird. I assume you actually used a debugger to tell which call was failing, since your question claims that you know it's mmap that's erroring.) – Peter Cordes Aug 25 '16 at 05:30
  • @PeterCordes Just added `strace` of my code – flashburn Aug 25 '16 at 17:18
  • @JonathonReinhart I think I mentioned this in the original post, but just in case this is not clear, page size is 4096 bytes and this is what `getpagesize()` returns. Also I included an `stace` of my call in the original post. – flashburn Aug 25 '16 at 17:19
  • Your mmap call looks valid. Unfortunately I don't think this question can be answered without more information. You say your kernel allows mmap of physical addresses that are RAM but we have no way to confirm that. Also, I dont know what `nsg_api_kmodule` is, what that ioctl does, and what the physical address returned by it that you're trying to mmap is. – Jonathon Reinhart Aug 26 '16 at 11:59
  • @JonathonReinhart The value returned through an `ioctl` call is a return value from `(uint64_t)virt_to_phys(__get_free_page(GFP_KERNEL))`, i.e the offset that was returned, `0x000000d9fcc000` is from this call. As far as the module name this is just a name, it really should be `my_kmodule`. I'm just trying out a few things in it. – flashburn Aug 26 '16 at 16:22
  • @flashburn Output of `uname -a`? – Jonathon Reinhart Aug 26 '16 at 18:56
  • 1
    @JonathonReinhart I recompiled my kernel to disable the `CONFIG_STRICT_DEVMEM`. The original kernel is `4.4.0-34-generic` on `Ubuntu 14.04 LTS` – flashburn Aug 26 '16 at 20:28
  • Also see [How to access mmaped /dev/mem without crashing the Linux kernel?](https://stackoverflow.com/q/11891979/608639), [mmap of /dev/mem fails with invalid argument, but address is page aligned](https://stackoverflow.com/q/39134990/608639) and [How to access kernel space from user space?](https://stackoverflow.com/q/9662193/608639) – jww Jul 11 '17 at 03:24

1 Answers1

0

nopat kernel command line argument

Just add that and it works, as mentioned at: https://stackoverflow.com/a/36634422/895245

Here is my test setup:

kernel module:

#include <asm/io.h> /* virt_to_phys */
#include <linux/debugfs.h>
#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/seq_file.h> /* single_open, single_release */
#include <linux/slab.h> /* kmalloc, kfree */

static volatile u32 *i;

static struct dentry *debugfs_file;

static int show(struct seq_file *m, void *v)
{
    seq_printf(m,
        "*i 0x%llx\n"
        "i %p\n"
        "virt_to_phys 0x%llx\n",
        (unsigned long long)*i,
        i,
        (unsigned long long)virt_to_phys((void *)i)
    );
    return 0;
}

static int open(struct inode *inode, struct  file *file)
{
    return single_open(file, show, NULL);
}

static const struct file_operations fops = {
    .llseek = seq_lseek,
    .open = open,
    .owner = THIS_MODULE,
    .read = seq_read,
    .release = single_release,
};

static int myinit(void)
{
    i = kmalloc(sizeof(i), GFP_KERNEL);
    *i = 0x12345678;
    debugfs_file = debugfs_create_file(
        "lkmc_virt_to_phys", S_IRUSR, NULL, NULL, &fops);
    return 0;
}

static void myexit(void)
{
    debugfs_remove(debugfs_file);
    kfree((void *)i);
}

module_init(myinit)
module_exit(myexit)
MODULE_LICENSE("GPL");

/dev/mem userland:

#!/bin/sh
set -ex
insmod /virt_to_phys.ko
cd /sys/kernel/debug
cat lkmc_virt_to_phys
# *i = 0x12345678
addr=$(grep virt_to_phys lkmc_virt_to_phys | cut -d ' ' -f 2)
devmem2 "$addr"
devmem2 "$addr" w 0x9ABCDEF0
cat lkmc_virt_to_phys
# *i = 0x9ABCDEF0
rmmod virt_to_phys

nopat being passed at: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/2eca9280e12dbab79ccb67d0640b2a0edc2c9ffc/runqemu#L65

Also try xp on QEMU monitor.

And devmem2 is upstreamed by Buildroot itself: http://free-electrons.com/pub/mirror/devmem2.c See also: Accessing physical address from user space

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985