Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed last month.
Improve this question
I'm trying to reading PCI CSR (Configuration Space Register) on my system via open,mmap /dev/mem.
I met some problems when using 8 byte length reading
Here is the minimal working example of my code
#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define FATAL \
do { \
fprintf(stderr, \
"Error at line %d, file %s (%d) [%s]\n", \
__LINE__, \
__FILE__, \
errno, \
strerror(errno)); \
exit(1); \
} while(0)
#define PAGE_SIZE 4096UL
#define PAGE_MASK (PAGE_SIZE - 1)
typedef struct rw_config rw_config;
struct rw_config {
uint64_t address;
uint64_t data;
};
static uint64_t _mmio_read_worker(uint64_t address) {
int fd;
void *map_base = NULL;
void *map_address = NULL;
uint64_t result = 0UL;
if((fd = open("/dev/mem", O_RDONLY | O_SYNC)) < 0) FATAL;
// PAGE_SIZE = 4096UL
// PAGE_MASK = (PAGE_SIZE - 1) = 4095UL
if((map_base = mmap(NULL,
PAGE_SIZE,
PROT_READ,
MAP_SHARED,
fd,
(address & ~PAGE_MASK)))
== MAP_FAILED)
FATAL;
map_address = map_base + (address & PAGE_MASK);
result = *(uint64_t *)map_address;
printf("uint32_t 0x%016x, uint64_t 0x%016lx\n",
(*(uint32_t *)map_address),
(*(uint64_t *)map_address));
close(fd);
return result;
}
void rw_worker(rw_config *cfg) {
cfg->data = _mmio_read_worker(cfg->address);
return;
}
int main(int argc, char *argv[]) {
rw_config *cfg = malloc(sizeof(rw_config));
cfg->address = 0x80000000;
cfg->data = 0x0;
rw_worker(cfg);
return 0;
}
Reading the address = 0x80000000 which is pci mmio base address.
The output of my code is as follows:
uint32_t 0x0000000009a28086, uint64_t 0xffffffffffffffff
And I try to using gdb to get some information.
(gdb) printf "0x%llx\n",(*(uint64_t *)map_address)
0x10000009a28086
# before assigning 'result'
(gdb) printf "0x%llx\n",result
0x0
(gdb) next
# after assigning 'result'
(gdb) printf "0x%llx\n",result
0xffffffffffffffff
(gdb) print map_address
$2 = (void *) 0x7ffff7ffb000
(gdb) x/1xg 0x7ffff7ffb000
0x7ffff7ffb000: 0x0010000009a28086
I guess I fail to casting (void*) to *(uint64_t *), but why?
The value storage in map_address is correct, am I using the wrong way to get the value?
After reading the replies from other members, I read some documents that may be related to this bug, and the following are some of my insights:
I tried testing with another address which NOT in PCI CSR(Configuration Space Register), and got the correct value. So I think this bug is related to hardware registers rather than software implementation
In EDK II UEFI Writer Guide link, using 64bits read on PCI BAR(Base Address Register, which is a part of PCI CSR) may cause an alignment fault, you should use 2x of 32bits read to achieve 64bits read. Although in the example it is not enforced that the whole CSR has this limitation, but I think there is already a good reason for this bug.
PCIe config space must be read using 1, 2, or 4-byte accesses.
8-byte accesses are not permitted.
This is specified in the PCIe spec, section 2.2.7.1: "For Configuration Requests, Length[9:0] must be 00 0000 01b." (Length is specified in DW.)
My experience is that 8-byte accesses always return FF in all bytes.
Related
am writing a hook to a function using the basic x86 method of inserting a jump worth 5 bytes. My code is rusty but I think i have the logic. I get a segmentation fault error when I run against LD_PRELOAD env var. Am basically using a replacement function, hook func, trampoline function to modify and return the original address. Below is the code link.
foo.h
#ifndef foo_h__
#define foo_h__
extern void foo(const char*);
#endif // foo_h_
foo.c
#include <stdio.h>
void foo(const char*str)
{
puts(str);
}
main.c
#include <stdio.h>
#include "foo.h"
int main(void)
{const char*str="I am a shared lib!\n";
int count=1;
puts("This is a shared library test...");
while(count!=200){
printf("%d time!\n",count);
foo(str);
count++;
}
return 0;
}
hook.c
# include <stdio.h>
# include <unistd.h>
# define __USE_GNU
# include <dlfcn.h>
# include <stdint.h>
# include <sys/mman.h>
const char*str = "Hooked! ma fucker!\n";
struct hookdata
{
int64_t*origFunc;
int64_t*newFunc;
const char*s;
void (*foo_trampoline)(const char*str);
}*hkd;
void fooHooked(const char*str)
{
puts(str);
hkd->foo_trampoline(hkd->s);
}
void hook(void)
{
//Get pointers to the original and new functions and calculate the jump offset
hkd->origFunc = dlsym(RTLD_NOW, "foo");
hkd->newFunc = (int64_t*) &fooHooked;
int64_t offset = hkd->newFunc - (hkd->origFunc + 5);
//Make the memory containing the original funcion writable
//Code from http://stackoverflow.com/questions/20381812/mprotect-always-returns-invalid-arguments
size_t pageSize = sysconf(_SC_PAGESIZE);
uintptr_t start = (uintptr_t) hkd->origFunc;
uintptr_t end = start + 1;
uintptr_t pageStart = start & -pageSize;
mprotect((void *) pageStart, end - pageStart,
PROT_READ | PROT_WRITE | PROT_EXEC);
//Insert the jump instruction at the beginning of the original function
int32_t instruction = 0xe9 | offset << 8;
*hkd->origFunc = instruction;
}
void foo(const char*str)
{
if (*hkd->origFunc == 0xe9)
{
printf("hook detected!");
}
else
hook();
}
The combination of page access flags PROT_READ | PROT_WRITE | PROT_EXEC violates W^X protection, so that may be the first problem at hand. In a first step first setting PROT_READ | PROT_WRITE for replacing the function preamble and then restoring it to PROT_READ | PROT_EXEC will probably solve that problem.
You stocking you're 5 bytes instruction in a 4 bytes type, you need something like this:
unsigned char instr[5];
instr[0] = 0xe9;
*(int32_t*)(&instr[1]) = offset;
memcpy(hkd->origFunc, instr, 5);
I have this program that is supposed to mmap a file in read-write mode and be able to edit its contents. Also the file this is written for is about 40-50 GB, so I need mmap64. The problem is, while mmap64 does not return an error, the address it returns is not accessible.
#include <assert.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <unistd.h>
typedef unsigned long long u64;
void access_test(u64 p, u64 sz)
{
u64 i;
char tmp;
for (i=0; i<sz; i++) {
tmp = *(char*)(p+i);
}
}
int main(int argc, char *argv[])
{
int fd;
long long int sz, p;
struct stat buf;
fd = open(argv[1], O_RDWR, 0x0666);
if (fd == -1) {
perror("open");
return 1;
}
fstat64(fd, &buf);
sz = buf.st_size;
printf("File size: 0x%016llx\n", sz);
p = mmap64 (0, buf.st_size, PROT_READ | PROT_WRITE , MAP_SHARED, fd, 0);
if (p == -1) {
perror ("mmap");
return 1;
}
access_test(p,sz);
if (close (fd) == -1) {
perror ("close");
return 1;
}
if (munmap ((void*)p, buf.st_size) == -1) {
perror ("munmap");
return 1;
}
return 0;
}
The result of this is on a small file:
$ ./testmmap minicom.log
File size: 0x0000000000000023
[1] 8282 segmentation fault (core dumped) ./testmmap minicom.log
The same goes for the big one.
Always enable warnings when you compile
Here is the result with warnings enabled:
$ gcc mmp.c -Wall -g
mmp.c: In function ‘access_test’:
mmp.c:18:10: warning: variable ‘tmp’ set but not used [-Wunused-but-set-variable]
char tmp;
^
mmp.c: In function ‘main’:
mmp.c:36:5: warning: implicit declaration of function ‘fstat64’ [-Wimplicit-function-declaration]
fstat64(fd, &buf);
^
mmp.c:40:5: warning: implicit declaration of function ‘mmap64’ [-Wimplicit-function-declaration]
p = mmap64 (0, buf.st_size, PROT_READ | PROT_WRITE , MAP_SHARED, fd, 0);
The last two warnings here are extremely important. They say there is no prototype for mmap64. C therefore gives you a default prototype, and it is wrong, at least for the mmap64() call (since the prototype will return an int, which cannot represent a pointer on a 64-bit Linux host)
The argument to fstat64() is a struct stat64 too BTW, which is another issue.
Make the specific 64-bit functions available
If you want to make the fstat64()/mmap64() function available, you need to compile the code with the _LARGEFILE and LARGEFILE64_SOURCE #define, see information here, so you should compile this as e.g:
gcc -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE mmp.c -Wall -g
Or use #define _FILE_OFFSET_BITS=64
There is however no need to do this. Just call the normal fstat() and mmap() and #define _FILE_OFFSET_BITS=64 when compiling. e.g.:
gcc -D_FILE_OFFSET_BITS=64 mmp.c -Wall -g
This will enable support for large files, and e.g. translate the mmap() call to mmap64() if it is needed (e.g. if you're on a 32-bit host).
If you are trying to mmap() an 50 GB file, you anyway need to be on a 64-bit host, and on a 64-bit Linux host there's no need for any of this - mmap() and fstat() handles large files without any need to do anything.
Use pointers
The next issue is you're assigning the return value of mmap() to an integer. This might happen to work, but the code does look odd because of it. If you want to treat the thing as a char *, assign it to a char *. Don't play tricks with casting pointers around to a 64-bit integer type.
E.g. your access function should be:
void access_test(char *p, u64 sz)
{
u64 i;
char tmp;
for (i=0; i<sz; i++) {
tmp = p[i];
}
}
And p should be declared as char *p; in main(), or use uint8_t *p; if you intend to treat the data as binary data.
I use the the code below to output data from SPI port of an embedded board (olimex imx233-micro -- it is not a board specific question). When I run the code ioctl return "bad address". I am modifying the code on http://twilight.ponies.cz/spi-test.c which works fine. Could anyone tell me what am I doing wrong?
root#ubuntu:/home# gcc test.c -o test
test.c:20: warning: conflicting types for ‘msg_send’
test.c:16: note: previous implicit declaration of ‘msg_send’ was here
root#ubuntu:/home# ./test
errno:Bad address - cannot send SPI message
root#ubuntu:/home# uname -a
Linux ubuntu 3.7.1 #2 Sun Mar 17 03:49:39 CET 2013 armv5tejl GNU/Linux
Code:
//test.c
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <errno.h>
static uint16_t delay;
int main(int argc,char *argv[]){
msg_send(254); //the message that I want to send decimal "254"
return 0;
}
void msg_send(int msg){
int fd;
int ret = 0;
fd = open("/dev/spidev32766.1", O_RDWR); //ls /dev outputs spidev32766.1
if(fd < 0){
fprintf(stderr, "errno:%s - FD could be not opened\n ", strerror(errno));
exit(1);
}
struct spi_ioc_transfer tr = {
.len = 1,
.delay_usecs = delay,
.speed_hz = 500000, //500 kHz
.bits_per_word = 8,
.tx_buf = msg,
.rx_buf = 0, //half duplex
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret <1 ){
fprintf(stderr, "errno:%s - cannot send SPI message\n ", strerror(errno));
}
close(fd);
}
Thank you!
The error message "Bad address" comes from the error code EFAULT, which happens when you pass an address to the kernel which is not a valid virtual address in your process's virtual address space. The address to your tr structure is clearly valid, so the problem must be with one of its members.
According to the definition of struct spi_ioc_transfer, the .tx_buf and .rx_buf members must be pointers to userspace buffers, or null. You're setting .tx_buf to the integer 254, which is not a valid userspace pointer, so that's where the bad address is coming from.
I'm not familiar with this IOCTL, so my best guess is that you need to bass the data in binary. One way to do that would be this:
struct spi_ioc_transfer tr = {
.len = sizeof(msg), // Length of rx and tx buffers
...
.tx_buf = (u64)&msg, // Pointer to tx buffer
...
};
If you need to send it as ASCII instead, then you should use a function such as snprintf(3) to convert the integer to an ASCII string, and then point the TX buffer at that string and set the length accordingly.
Please tell me, why my simple application cannot mmap a small size of memory?
And, why such a specific boundary - 257UL?
// #define MAP_SIZE 256UL or below - fail
// #define MAP_SIZE 257UL - ok
#include <stdio.h>
#include <stdlib.h>
#include <unistd.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>
#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 4096UL
#define MAP_MASK (MAP_SIZE - 1)
int main(int argc, char **argv) {
int fd;
void *map_base, *virt_addr;
unsigned long read_result, writeval;
off_t target = strtoul("0x00002000", 0, 0);
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) FATAL;
printf("/dev/mem opened.\n");
fflush(stdout);
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target & ~MAP_MASK);
if(map_base == (void *) -1) FATAL;
printf("Memory mapped at address %p.\n", map_base);
fflush(stdout);
...
}
mmap works in multiples of the page size on your system. If you're doing this on i386/amd64 or actually most modern CPUs, this will be 4096.
In the man page of mmap on my system it says: "offset must be a multiple of the page size as returned by sysconf(_SC_PAGE_SIZE).". On some systems for historical reasons the length argument may be not a multiple of page size, but mmap will round up to a full page in that case anyway.
Probably you just don't have the rights to write to /dev/mem. This is probably not what you want, mapping all the low end physical memory into your address space.
Have a look into shm_open to open memory segments or MAP_ANONYMOUS to map anonymously.
Edit:
Do a man mem to know what the /dev/mem device node is about:
Byte addresses in mem are interpreted as physical memory
addresses.
References to nonexistent locations cause errors to be returned.
If you want to map to a device node to have a memory segment you should use /dev/zero, but nowadays the tools I describe above should be sufficient.
Then don't, really don't, run such a code with root privileges unless you really know what you are doing. Writing into the physical memory and thus overwriting kernel and userspace data and programs can only lead to catastrophes.
I want my program to do the following:
Open a new file.
Copy a (page-aligned) portion of the stack that includes the current frame pointer address to the file.
Map the contents of the file back into the process's address space in the same range as that of the original portion of the stack, so that the process will use the file for that part of its stack rather than the region of memory the system had originally allocated to it for the stack.
Below is my code. I am getting a segmentation fault on the call to mmap, specifically where mmap makes the system call with vsyscall. (I am working with gcc 4.4.3, glibc 2.11.1, under Ubuntu Server (x86-64). I have compiled and run both with 64-bit and 32-bit configurations, with the same results.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <assert.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#define PAGE_SIZE 0x1000
#define FILENAME_LENGTH 0x10
#if defined ARCH && ARCH == 32
#define PAGE_SIZE_COMPLEMENT 0xfffff000
#define UINT uint32_t
#define INT int32_t
#define BP "ebp"
#define SP "esp"
#define X_FORMAT "%x"
#else
#define PAGE_SIZE_COMPLEMENT 0xfffffffffffff000
#define UINT uint64_t
#define INT int64_t
#define BP "rbp"
#define SP "rsp"
#define X_FORMAT "%lx"
#endif
#define PAGE_ROUND_UP(v) (((v) + PAGE_SIZE - 1) & PAGE_SIZE_COMPLEMENT)
#define PAGE_ROUND_DOWN(v) ((v) & PAGE_SIZE_COMPLEMENT)
UINT stack_low, stack_high, stack_length;
void find_stack_high(void) {
UINT bp = 0;
UINT raw_stack_high = 0;
/* Set the global stack high to the best
* approximation.
*/
asm volatile ("mov %%"BP", %0" : "=m"(bp));
while (bp) {
raw_stack_high = bp;
bp = *(UINT *)bp;
}
stack_high = PAGE_ROUND_UP(raw_stack_high);
}
int file_create(void) {
int fd;
char filename[FILENAME_LENGTH];
strcpy(filename, "tmp.XXXXXX");
fd = mkstemp(filename);
if (fd == -1) {
perror("file_create:mkstemp");
exit(EXIT_FAILURE);
}
unlink(filename);
return fd;
}
int main(void) {
int fd, bytes_written;
UINT bp;
off_t offset;
printf("In main\n");
fd = file_create();
printf("fd %d\n", fd);
find_stack_high();
// Get the current frame pointer.
asm volatile ("mov %%"BP", %0" : "=m" (bp));
// Store page boundary below
// frame pointer as end of potentially shared stack.
stack_low = PAGE_ROUND_DOWN(bp);
stack_length = stack_high - stack_low;
printf("start "X_FORMAT" end "X_FORMAT" length "X_FORMAT"\n",
stack_low, stack_high, stack_length);
bytes_written =
write(fd, (const void *)stack_low, PAGE_SIZE);
if (bytes_written != PAGE_SIZE) {
perror("main: write");
fprintf(stderr, "Num bytes: %x\n", bytes_written);
exit(EXIT_FAILURE);
}
offset = 0;
if (mmap((void *)stack_low, PAGE_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_FIXED | MAP_GROWSDOWN, fd, offset) ==
MAP_FAILED) {
perror("file_copy: mmap");
exit(EXIT_FAILURE);
}
close(fd);
return EXIT_SUCCESS;
}
Thanks!
The stack changes (e.g. the return address for the mmap call) after you copied it. I can think of 2 possible ways around this:
Write asm that doesn't need the stack to perform the new mapping.
Call into a function with some huge local data so that the working stack is on a different page from the pages you're mapping over. Then, you could map over the lower addresses with a second call to mmap once this function returns.
Whatever you do, this is a horrible hack and probably a bad idea..
Tried turning on execute permission? In any case, the symptom suggests that you've managed to map in over the top of the stack, destroying the return pointer.