After a major OS upgrade this C code behaviour has changed:
...
if ((fd = open(argv[1], O_RDWR | O_SYNC)) == -1)
FATAL;
printf("character device %s opened.\n", argv[1]);
fflush(stdout);
/* map one page */
map_base = mmap(0xe0000000, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map_base == (void *)-1)
FATAL;
printf("Memory mapped at address %p.\n", map_base);
...
With a binary inherited from an old OS, "old mmap" returns a virtual address 0x7fb20d725000. If I rebuild the same C file on a new OS, it returns 0xe0000000 which seems to be a physical, and subsequent code - which uses this returned address - now fails with a segmentation fault.
How to force mmap to work as before without downgrading the OS or using old binary? Any modern flags for gcc or mmap itself?
Run a code example below with sudo ./test /dev/zero 0x01000000 : (/dev/zero instead of a real device gives the same results)
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <byteswap.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
/* ltoh: little to host */
/* htol: little to host */
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define ltohl(x) (x)
#define ltohs(x) (x)
#define htoll(x) (x)
#define htols(x) (x)
#elif __BYTE_ORDER == __BIG_ENDIAN
#define ltohl(x) __bswap_32(x)
#define ltohs(x) __bswap_16(x)
#define htoll(x) __bswap_32(x)
#define htols(x) __bswap_16(x)
#endif
#define FATAL do { fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", __LINE__, __FILE__, errno, strerror(errno)); exit(1); } while(0)
#define MAP_SIZE (16*1024*1024UL)
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv)
{
int fd;
void *map_base, *virt_addr;
uint32_t read_result, writeval;
off_t target;
char *device;
if (argc != 3) {
fprintf(stderr,
"\nUsage:\t%s <device> <address> [[type] data]\n"
"\tdevice : character device to access\n"
"\taddress : memory address to access\n\n",
argv[0]);
exit(1);
}
device = strdup(argv[1]);
target = strtoul(argv[2], 0, 0);
fprintf("argc = %d, device: %s, address: 0x%08x\n", argc, device, (unsigned int)target);
if ((fd = open(argv[1], O_RDWR | O_SYNC)) == -1)
FATAL;
fprintf(stdout, "character device %s opened.\n", argv[1]);
fflush(stdout);
/* map one page */
map_base = mmap(0xe0000000, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (map_base == (void *)-1)
FATAL;
fprintf(stdout, "Memory mapped at address %p.\n", map_base);
fflush(stdout);
/* calculate the virtual address to be accessed */
virt_addr = map_base + target;
/* read only */
read_result = *((uint32_t *) virt_addr);
/* swap 32-bit endianess if host is not little-endian */
read_result = ltohl(read_result);
printf("Read 32-bit value at address 0x%08x (%p): 0x%08x\n",
(unsigned int)target, virt_addr, (unsigned int)read_result);
if (munmap(map_base, MAP_SIZE) == -1)
FATAL;
close(fd);
return 0;
}
You seem to be confusing virtual and physical addresses. User programs usually only work with virtual addresses. The mmap syscall accepts an hint as first argument: a desired virtual address for the requested mapped area. See man 2 mmap for more information.
What was most likely happening with your previous program was that the call to mmap was probably something like:
map_area = mmap(NULL, /* same arguments here */);
This way, the operating system will choose an appropriate address and return it.
What you are doing in the new program instead, is letting the OS know that you would prefer a specific address (0xe...), and the OS will map memory at that address if possible (very likely). You really shouldn't need this, the program works regardless of the position of the mapped area, but in any case you can keep it.
The reason why you are getting a segmentation fault is because you are mapping an area of 16 * 1024 * 1024 bytes (0x01000000), but then you are accessing memory at an higher offset than the specified size (target >= 0x01000000).
The correct way to do what you are trying to do is to use the offset argument of mmap to request a map that starts at an appropriate offset in the file. Requesting a mapping of two pages starting at that offset will ensure that what you want to read or write will be correctly mapped (assuming the file is big enough, otherwise MAP_FAILED will be returned).
Here's how it should be done:
offset = target & 0xFFFFFFFFFFFFF000; // align target to page size
// Map two pages starting at 0xe... and corresponding to the calculated offset in the file.
map_base = mmap((void *)0xe0000000, 0x1000 * 2, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, offset);
// ...
virt_addr = map_base + (target & 0xfff); // cut target to get offset within the mapped pages
read_result = *((uint32_t *) virt_addr);
read_result = ltohl(read_result);
printf("Read 32-bit value at address 0x%08x (%p): 0x%08x\n",
(unsigned int)target, virt_addr, (unsigned int)read_result);
Related
I have a problem considering the usage of mmap. I am trying to map a pci device to a virtual address and read its content. In the future I am planning to write values to it as well.
The problem is that I (seemingly) successfully mapped the device to virtual memory space. However when I read the content of that virtual address all values are zero, despite the file not being empty.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include "../include/types.h"
#include "../include/pci.h"
#define PRINT_ERROR \
do { \
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
__LINE__, __FILE__, errno, strerror(errno)); exit(1);\
} while(0)
#define MAP_SIZE 4069
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char *argv[])
{
int pci_dev;
int *mmap_base;
int *content;
char file[] = {"/sys/bus/pci/devices/0000:04:00.0/resource"};
int i;
printf("File to be read from: %s\n", file);
pci_dev = open(file, O_RDONLY);
if (pci_dev < 0)
{
PRINT_ERROR;
}
mmap_base = mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE | MAP_ANON, pci_dev, 0);
if (mmap_base == (void *)-1 || mmap_base == NULL)
{
PRINT_ERROR;
}
printf("Mapped on address %p of size %d Byte\n", mmap_base, (int)MAP_SIZE);
content = (int *)mmap_base;
for(i = 0; i < 1024; i++)
{
printf("%x", content[i]);
}
return 0;
}
Here's the content of the first line from the file "/sys/bus/pci/devices/0000:04:00.0/resource" that I am trying to access:
0x00000000cd000000 0x00000000cd07ffff 0x0000000000040200
However the output I get is:
File to be read from: /sys/bus/pci/devices/0000:04:00.0/resource
Mapped on address 0xb7705000 of size 4096 Byte
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000...
Am I doing something wrong? Every help is appreciated!
Actually you've got 2 mistakes:
Don't use MAP_ANON when you create a map for a real file on the file system, it's meant for IPC and requiring extra memory from OS e.g. while malloc().
When you remove the flag, the mmap() will likely return ENODEV, because linux sysfs doesn't support mmaping; So you have to use read()'s here.
When i run the binary file of this code it throws an segmentation fault core dumped error. And the dmesg is:
segfault at 0 ip b7651747 sp bfb312d0 error 4 in libc-2.21.so[b75e9000+1b4000]
The code is for translation of virtual address to physical address.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
// ORIG_BUFFER will be placed in memory and will then be changed to NEW_BUFFER
// They must be the same length
#define ORIG_BUFFER "Hello, World!"
#define NEW_BUFFER "Hello, Linux!"
// The page frame shifted left by PAGE_SHIFT will give us the physcial address of the frame
// Note that this number is architecture dependent. For me on x86_64 with 4096 page sizes,
// it is defined as 12. If you're running something different, check the kernel source
// for what it is defined as.
#define PAGE_SHIFT 12
#define PAGEMAP_LENGTH 8
void* create_buffer(void);
unsigned long get_page_frame_number_of_address(void *addr);
int open_memory(void);
void seek_memory(int fd, unsigned long offset);
int main(void) {
// Create a buffer with some data in it
void *buffer = create_buffer();
// Get the page frame the buffer is on
unsigned int page_frame_number = get_page_frame_number_of_address(buffer);
printf("Page frame: 0x%x\n", page_frame_number);
// Find the difference from the buffer to the page boundary
unsigned int distance_from_page_boundary = (unsigned long)buffer %
getpagesize();
// Determine how far to seek into memory to find the buffer
uint64_t offset = (page_frame_number << PAGE_SHIFT) + distance_from_page_boundary;
// Open /dev/mem, seek the calculated offset, and
// map it into memory so we can manipulate it
// CONFIG_STRICT_DEVMEM must be disabled for this
int mem_fd = open_memory();
seek_memory(mem_fd, offset);
printf("Buffer: %s\n", buffer);
puts("Changing buffer through /dev/mem...");
// Change the contents of the buffer by writing into /dev/mem
// Note that since the strings are the same length, there's no purpose in
// copying the NUL terminator again
if(write(mem_fd, NEW_BUFFER, strlen(NEW_BUFFER)) == -1) {
fprintf(stderr, "Write failed: %s\n", strerror(errno));
}
printf("Buffer: %s\n", buffer);
// Clean up
free(buffer);
close(mem_fd);
return 0;
}
void* create_buffer(void) {
size_t buf_size = strlen(ORIG_BUFFER) + 1;
// Allocate some memory to manipulate
void *buffer = malloc(buf_size);
if(buffer == NULL) {
fprintf(stderr, "Failed to allocate memory for buffer\n");
exit(1);
}
// Lock the page in memory
// Do this before writing data to the buffer so that any copy-on-write
// mechanisms will give us our own page locked in memory
if(mlock(buffer, buf_size) == -1) {
fprintf(stderr, "Failed to lock page in memory: %s\n", strerror(errno));
exit(1);
}
// Add some data to the memory
strncpy(buffer, ORIG_BUFFER, strlen(ORIG_BUFFER));
return buffer;
}
unsigned long get_page_frame_number_of_address(void *addr) {
// Open the pagemap file for the current process
FILE *pagemap = fopen("/proc/self/pagemap", "rb");
// Seek to the page that the buffer is on
unsigned long offset = (unsigned long)addr / getpagesize() * PAGEMAP_LENGTH;
if(fseek(pagemap, (unsigned long)offset, SEEK_SET) != 0) {
fprintf(stderr, "Failed to seek pagemap to proper location\n");
exit(1);
}
// The page frame number is in bits 0-54 so read the first 7 bytes and clear the 55th bit
unsigned long page_frame_number = 0;
fread(&page_frame_number, 1, PAGEMAP_LENGTH-1, pagemap);
page_frame_number &= 0x7FFFFFFFFFFFFF;
fclose(pagemap);
return page_frame_number;
}
int open_memory(void) {
// Open the memory (must be root for this)
int fd = open("/dev/mem", O_RDWR);
if(fd == -1) {
fprintf(stderr, "Error opening /dev/mem: %s\n", strerror(errno));
exit(1);
}
return fd;
}
void seek_memory(int fd, unsigned long offset) {
unsigned pos = lseek(fd, offset, SEEK_SET);
if(pos == -1) {
fprintf(stderr, "Failed to seek /dev/mem: %s\n", strerror(errno));
exit(1);
}
}
In function get_page_frame_number_of_address.
Please confirm open file success.
FILE *pagemap = fopen("/proc/self/pagemap", "rb");
Check the pagemap is NULL or not.
I am using Shared memory (POSIX), to share a FILE pointer(FILE * rt_file), but it's not getting file pointer at client end. Any suggestions please.
/*
* shm_msgserver.c
*
* Illustrates memory mapping and persistency, with POSIX objects.
* This process produces a message leaving it in a shared segment.
* The segment is mapped in a persistent object meant to be subsequently
* open by a shared memory "client".
*
*
* Created by Mij <mij#bitchx.it> on 27/08/05.
* Original source file available at http://mij.oltrelinux.com/devel/unixprg/
*
*/
#include <stdio.h>
/* shm_* stuff, and mmap() */
#include <sys/mman.h>
#include <sys/types.h>
/* exit() etc */
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
/* for random() stuff */
#include <stdlib.h>
#include <time.h>
/* Posix IPC object name [system dependant] - see
http://mij.oltrelinux.com/devel/unixprg/index2.html#ipc__posix_objects */
#define SHMOBJ_PATH "/foo1423"
/* maximum length of the content of the message */
#define MAX_MSG_LENGTH 50
/* how many types of messages we recognize (fantasy) */
#define TYPES 8
/* message structure for messages in the shared segment */
struct msg_s {
int type;
char content[MAX_MSG_LENGTH];
FILE *pt;
};
int main(int argc, char *argv[]) {
int shmfd;
int shared_seg_size = (1 * sizeof(struct msg_s)); /* want shared segment capable of storing 1 message */
struct msg_s *shared_msg; /* the shared segment, and head of the messages list */
FILE *rt_file;
rt_file = fopen ("res","a+");
printf("rt_file : %s\n",rt_file);
/* creating the shared memory object -- shm_open() */
shmfd = shm_open(SHMOBJ_PATH, O_CREAT | O_EXCL | O_RDWR, S_IRWXU | S_IRWXG);
if (shmfd < 0) {
perror("In shm_open()");
exit(1);
}
fprintf(stderr, "Created shared memory object %s\n", SHMOBJ_PATH);
/* adjusting mapped file size (make room for the whole segment to map) -- ftruncate() */
ftruncate(shmfd, shared_seg_size);
/* requesting the shared segment -- mmap() */
shared_msg = (struct msg_s *)mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
if (shared_msg == NULL) {
perror("In mmap()");
exit(1);
}
fprintf(stderr, "Shared memory segment allocated correctly (%d bytes).\n", shared_seg_size);
srandom(time(NULL));
/* producing a message on the shared segment */
shared_msg->type = random() % TYPES;
snprintf(shared_msg->content, MAX_MSG_LENGTH, "My message, type %d, num %ld", shared_msg->type, random());
shared_msg->pt = rt_file;
/* [uncomment if you wish] requesting the removal of the shm object -- shm_unlink() */
/*
if (shm_unlink(SHMOBJ_PATH) != 0) {
perror("In shm_unlink()");
exit(1);
}
*/
return 0;
}
Client Code
/*
* shm_msgclient.c
* http://mij.oltrelinux.com/devel/unixprg/#ipc__posix_shm
* Illustrates memory mapping and persistency, with POSIX objects.
* This process reads and displays a message left it in "memory segment
* image", a file been mapped from a memory segment.
*
*
* Created by Mij <mij#bitchx.it> on 27/08/05.
* Original source file available at http://mij.oltrelinux.com/devel/unixprg/
*
*/
#include <stdio.h>
/* exit() etc */
#include <unistd.h>
/* shm_* stuff, and mmap() */
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/stat.h>
/* for random() stuff */
#include <stdlib.h>
#include <time.h>
/* Posix IPC object name [system dependant] - see
http://mij.oltrelinux.com/devel/unixprg/index2.html#ipc__posix_objects */
#define SHMOBJ_PATH "/foo1423"
/* maximum length of the content of the message */
#define MAX_MSG_LENGTH 50
/* how many types of messages we recognize (fantasy) */
#define TYPES 8
/* message structure for messages in the shared segment */
struct msg_s {
int type;
char content[MAX_MSG_LENGTH];
FILE *pt;
};
int main(int argc, char *argv[]) {
int shmfd;
int shared_seg_size = (1 * sizeof(struct msg_s)); /* want shared segment capable of storing 1 message */
struct msg_s *shared_msg; /* the shared segment, and head of the messages list */
FILE *rt_file;
/* creating the shared memory object -- shm_open() */
shmfd = shm_open(SHMOBJ_PATH, O_RDWR, S_IRWXU | S_IRWXG);
if (shmfd < 0) {
perror("In shm_open()");
exit(1);
}
printf("Created shared memory object %s\n", SHMOBJ_PATH);
/* requesting the shared segment -- mmap() */
shared_msg = (struct msg_s *)mmap(NULL, shared_seg_size, PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
if (shared_msg == NULL) {
perror("In mmap()");
exit(1);
}
printf("Shared memory segment allocated correctly (%d bytes).\n", shared_seg_size);
rt_file = shared_msg->pt;
printf("rt_file : %s\n",rt_file);
rt_file = fopen ("res","a+");
printf("rt_file : %s\n", rt_file);
char x[10]="ABCDEFGHIJ";
fwrite(x, sizeof(x[0]), sizeof(x)/sizeof(x[0]), rt_file);
printf("Message type is %d, content is: %s\n", shared_msg->type, shared_msg->content);
if(shm_unlink(SHMOBJ_PATH) == -1){
printf("%s is used by another Process\n",SHMOBJ_PATH);
}
else{
printf("Memory is freed\n");
}
return 0;
}
FILE* pointers are valid only in the process where they were created.
While you can transfer the pointer value, or even the structure it points to, to another process, using it in the other process will invoke undefined behaviour.
If you want another process on the same machine to access a certain file I suggest that you send the file name, mode and offset to the other process and let it create it's own FILE* structure by calling fopen.
Setup:
One ARM-CPU (A9) running busybox-Linux. This one talks to the network and gets a precompiled statically linked elf.
Second CPU runs bare-metal application. I have newlib on that one and the whole "OS" sits in memory just executing that one basic program.
Both share OCM.
I have succeeded in making the two processors "talk". I can write hex-values into memory from linux, the other processor reads it and vice-versa.
Now I'd like to parse the aforementioned elf, send it to OCM, make the bare-metal read it into it's memory, set the program counter via asm and execute said elf (could use a .o file as well).
I got stuck at parsing the elf already...
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <byteswap.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <errno.h>
#include <sys/mman.h>
#define PAGE_SIZE ((size_t)getpagesize())
#define PAGE_MASK ((uint64_t)(long)~(PAGE_SIZE - 1))
const unsigned int COMM_RX_DATA = 0xFFFF900c;
int main(int argc, char **argv){
int fd;
int cached = 0;
unsigned char* c;
//uint32_t value;
unsigned char value;
uint64_t offset = COMM_RX_DATA;
uint64_t base;
volatile uint8_t *mm;
fprintf(stderr, "Nr. 0\n");
FILE* f_read;
if ((argc != 1) && (argc != 2)) {
fprintf(stderr, "usage: %s ELF_NAME\n", argv[0]);
return 1;
}
fd = open("/dev/mem", O_RDWR);//|(!cached ? O_SYNC : 0));
if (fd < 0) {
fprintf(stderr, "open(/dev/mem) failed (%d)\n", errno);
return 1;
}
f_read = fopen(argv[1], "rb");
if(!f_read){
fprintf(stderr, "read failed");
return 1;
}
else {
printf("Nr. 1\n");
base = offset & PAGE_MASK;
offset &= ~PAGE_MASK;
mm = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base);
fseek(f_read, 0, SEEK_END);
int size = ftell(f_read);
fseek(f_read, 0, SEEK_SET); //Reset stream
c = malloc(size);
//malloc error checking!
while (fgets(c, size, f_read) != NULL ){ //tried fgetc but segfaults
//tmp-output to stdout
puts(c);
value = c;//strtoull((char*)c, NULL, 0);
printf("Writing %d to %d", (int)value, (int)(mm + offset));
*(volatile uint32_t *)(mm + offset) = value;
printf("size: %d , value = %s\n", size, value);
}
}
munmap((void *)mm, PAGE_SIZE);
fclose(f_read);
return 0;
}
_asm-idea.S:
ExecuteR0:
mov lr, r0 /* move the destination address into link register */
mcr 15,0,r0,cr7,cr5,0 /* Invalidate Instruction cache */
mcr 15,0,r0,cr7,cr5,6 /* Invalidate branch predictor array */
dsb
isb /* make sure it completes */
ldr r4, =0
mcr 15,0,r4,cr1,cr0,0 /* disable the ICache and MMU */
isb /* make sure it completes */
bx lr /* force the switch, destination should have been in r0 */
Help me mighty SO you're my only hope.
Updated my post...
I got below program. It operates on /dev/kmem and /dev/mem.
I think I can learn something from the code. But when I run it on my Beagle Board, below result is given:
case 1: ( if(1) )
root#omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3
found jiffies at (0xc0870080) c0870080
/dev/kmem read buf = 319317
jiffies=319317 (read from virtual memory)
/dev/mem: the offset is 870080
the page size = 4096
mmap: Invalid argument
case 2: ( if(0) )
root#omap:/home/ubuntu/tom# ./kmem_mem /boot/System.map-3.0.4-x3
found jiffies at (0xc0870080) c0870080
/dev/kmem read buf = 333631
jiffies=333631 (read from virtual memory)
/dev/mem: the offset is 870080
/dev/mem read failed: Bad address
jiffies=0 (read from physical memory)
And I used below command so that mmap can use NULL as its first parameter.
root#omap:/home/ubuntu/tom# echo 0 > /proc/sys/vm/mmap_min_addr
root#omap:/home/ubuntu/tom# cat /proc/sys/vm/mmap_min_addr
0
As you can see, read_kmem() works fine but read_mem() doesn't work, and it seems that the 'offset' transferred to it is wrong. But kernel address - PAGE_OFFSET(0xC0000000) = physical address, is it wrong?
My questions are:
(1) Why "mmap: Invalid argument" in case 1?
(2) Why the mmap only maps PAGE_SIZE length space?
(3) What's wrong with read_mem?
Can anyone help?
Thanks!
/*
* getjiff.c
*
* this toolkit shows how to get jiffies value from user space:
* 1. find jiffies's address from kernel image.
* 2. access virtual address space to get jiffies value.
* 3. access physical address sapce to get jiffies value.
*
* demostrate following techniques:
* o get ELF object symbol address by calling nlist()
* o access virtual memory space from /dev/kmem
* o access virtual memory space from /dev/mem
*/
#include <stdio.h>
#include <stdlib.h> //exit
#include <linux/a.out.h> //nlist
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <memory.h>
#define LONG *(volatile unsigned long*)
/* read from virtual memory */
int read_kmem(off_t offset, void* buf, size_t count)
{
int fd;
int n;
fd = open("/dev/kmem", O_RDONLY);
if (fd < 0)
{
perror("open /dev/kmem failed");
return -1;
}
lseek(fd, offset, SEEK_SET);
n = read(fd, buf, count);
if (n != count)
perror("/dev/kmem read failed");
else
printf("/dev/kmem read buf = %ld\n", *(unsigned long *)buf);
close(fd);
return n;
}
/* read from physical memory */
int read_mem(off_t offset, void* buf, size_t count)
{
int fd;
int n;
int page_size;
void *map_base;
unsigned long value;
printf("/dev/mem: the offset is %lx\n", offset);
fd = open("/dev/mem", O_RDONLY);
if (fd < 0)
{
perror("open /dev/mem failed");
return -1;
}
if(1){
page_size = getpagesize();
printf("the page size = %d\n", page_size);
map_base = mmap(0,page_size,PROT_READ,MAP_SHARED,fd,offset);
if (map_base == MAP_FAILED){
perror("mmap");
exit(1);
}
value = LONG(map_base);
printf("/dev/mem: the value is %ld\n", value);
buf = (unsigned long *)map_base;
}
if(0){
lseek(fd, offset, SEEK_SET);
n = read(fd, buf, count);
if (n != count)
perror("/dev/mem read failed");
else
printf("/dev/mem read buf = %ld\n", *(unsigned long *)buf);
}
close(fd);
return n;
}
int main(int argc, char **argv)
{
FILE *fp;
char addr_str[11]="0x";
char var[51];
unsigned long addr;
unsigned long jiffies;
char ch;
int r;
if (argc != 2) {
fprintf(stderr,"usage: %s System.map\n",argv[0]);
exit(-1);
}
if ((fp = fopen(argv[1],"r")) == NULL) {
perror("fopen");
exit(-1);
}
do {
r = fscanf(fp,"%8s %c %50s\n",&addr_str[2],&ch,var); // format of System.map
if (strcmp(var,"jiffies")==0)
break;
} while(r > 0);
if (r < 0) {
printf("could not find jiffies\n");
exit(-1);
}
addr = strtoul(addr_str,NULL,16); //Convert string to unsigned long integer
printf("found jiffies at (%s) %08lx\n",addr_str,addr);
read_kmem(addr, &jiffies, sizeof(jiffies));
printf("jiffies=%ld (read from virtual memory)\n\n", jiffies);
jiffies = 0; //reinit for checking read_mem() below
read_mem(addr-0xC0000000, &jiffies, sizeof(jiffies));
printf("jiffies=%ld (read from physical memory)\n", jiffies);
return 0;
}
I've tried combinations or offset and bs for dd and found this solution:
On PC, in build directory I've found location of jiffies.
grep -w jiffies System.map
c04660c0 D jiffies
On PandaBoard:
In /proc/iomem you can see:
80000000-9c7fffff : System RAM
80008000-80435263 : Kernel code
80464000-804d0d97 : Kernel data
a0000000-bfefffff : System RAM
RAM starts from physical 80000000, and Kernel data start on 80464000. Looks similar to address of jiffies.
Then convert from virtual address to phys: virt - 0xC000000 + 0x8000000.
dd if=/dev/mem skip=$((0x804660c)) bs=$((0x10)) count=1 2> /dev/null | hexdump
0000000 02b9 0002 0001 0000 0000 0000 0000 0000
0000010
Try several times and see how the value is incrementing.
Summary: /dev/mem uses phys address, RAM starts at phys address 0x8000000
For the invalid argument in case 1, the problem is offset being non-page aligned. mmap(2) works by manipulating page tables, and such works only on multiplies of page-size for both size and offset
As for the second case, I'm not sure if you're guaranteed to have kernel space begin at 3G boundary. Also, I'm pretty sure that's the boundary of kernel's virtual space, not location in physical memory - so on beagle board, quite possibly you ended up with a wrapped-around offset pointing who-knows-where.
I think what you might need is PHYS_OFFSET, not PAGE_OFFSET.