I am trying to change a variable's content of a program with devmem2. Simply:
static inline __attribute__ ((always_inline))
void clflush(volatile void *p)
{
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
}
uint64_t get_pfn(uint64_t entry) {
return ((entry) & 0x3fffffffffffff);
}
uint64_t get_phys_addr(uint64_t v_addr)
{
uint64_t entry;
uint64_t offset = (v_addr/4096) * sizeof(entry);
uint64_t pfn;
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd >= 0);
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
close(fd);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
pfn = get_pfn(entry);
assert(pfn != 0);
return (pfn*4096) | (v_addr & 4095);
}
int main(){
int a = 44;
uint64_t v_addr = (uint64_t) &a;
printf("%lx\n",v_addr);
uint64_t p_addr = get_phys_addr(v_addr);
printf("%lx\n",p_addr);
clflush(&a);
sleep(20);
int b = a;
printf("value of b: %d\n",b);
printf("value of b: %d\n",a);
uint64_t v_addr2 = (uint64_t) &b;
printf("%lx\n",v_addr2);
uint64_t p_addr2 = get_phys_addr(v_addr2);
printf("%lx\n",p_addr2);
}
I am trying to change the content of variable "a" while sleeping with devmem2. Consider that my program prints "17580fab8" as the physical address of "a". I am executing devmem2 in another tab like this:
sudo devmem2 -x17580fab8 w 0x1235
So, after sleeping 20 seconds, I would expect the value of a and b to 0x1235. However, they do not change. The output when I run devmem2 while my program sleeps is this:
/dev/mem opened.
Memory mapped at address 0x7f464d50c000.
Value at address 0x0 (0x7f464d50c000): 0x1235
Written 0x1235; readback 0x1235
devmem2 claims to "read/write from/to any location in memory". Is it wrong? Because how can it write to my program's pages?
Related
For the next system: VAR-DART-MX8M (https://variwiki.com/index.php?title=DART-MX8M)(It is a ARM A-53 cortex based system)
Kernel version: Linux Debian Stretch version (kernel 4.14.78)
I use Eclipse to write the code and then cross compile to the specified system.
I want to use an SPI bus in my embedded system to communicate with an FPGA. I have read that casting pointers can lead to undefined behaviour: Passing pointer of unsigned int to pointer of long int
The problem is that I have to cast some parameters in order to meet the spi_ioc_transfer struct requirements:
int transfer16(int fd, uint16_t *tx, uint16_t *rx, uint32_t len){
int ret;
errno=0;
tr.tx_buf = (unsigned long)tx;
tr.rx_buf = (unsigned long)rx;
tr.len = len;
tr.delay_usecs = 1;
tr.speed_hz = spi_speed;
tr.bits_per_word = 16;
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (errno != 0){
printf("SPI IOCTL ret(%d), error(%d) %s\n", ret, errno,
strerror(errno));
}
return ret;
}
Where tr is the struct spi_ioc_transfer provided by spidev.h (declared as global variable).
The function transfer16 is called by the following function:
int send_command_readadc(int fd, int16_t *rx, uint16_t ndata_adc, uint8_t membank) {
int ret;
uint16_t tx[2];
uint16_t crc16_o, crc16_i;
uint8_t rcommand;
uint8_t ack;
uint32_t len = 2*(ndata_adc+NUMELS(tx)+2); // 2*sizeof(rx)
int i = 0;
tx[0] = (THE_READADC_COMMAND << 8) + membank;
tx[1] = crc16_uint16_false(tx,NUMELS(tx)-1);
ret = transfer16(fd, tx, rx, len);
if (ret==-1) {
return -1;
}
And I call this function using the following parameters:
ret = send_command_readadc(fd_spi, (int16_t *)(data_adc+(ndata_adc+4)*i), ndata_adc, membank);
Where data_adc is a pointer to a allocated part of memory where I want to save the ADC readings:
data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t));
So, for example, if M is 3 I save the quantity of (ndata_adc+4) starting from the register data_adc. Then (ndata_adc+4) starting from the register data_adc+(ndata_adc+4) etc.
The function transfer16 sometimes doesn't work properly, as same exact executions can have different results:
Any tip on how to proceed with this issue?
If you need more information about the problem, ask without hesitation.
Thanks!
I have solutioned this problem this way:
Instead of using data_adc = (int16_t *) calloc((ndata_adc+4)*M,sizeof(int16_t)); , I declared an array int16_t data_adc_static[(ndata_adc+4)*M]; and I give the next address int16_t *address = &data_adc_static[(ndata_adc+4)*i]; to the send_command_readadc command.
This way the problem that I had desappeared.
Thanks everybody for your answers
Here, I explain my problem, I am a beginner on the ptrace function and I would like to succeed in recovering the hard information of a structure.
For example with this command, I will have strace -e trace = fstat ls
a line: fstat (3, {st_mode = ..., st_size = ...}
and I would like to successfully retrieve the contents of the structure (st_mode) and (st_size).
I try this but to no avail:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
while (byte < size) {
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if ((size - byte) / sizeof(tmp))
data = sizeof(tmp);
else
data = size % sizeof(tmp);
memcpy((void *)(buffer + byte), &tmp, data);
byte += data;
}
}
and in params :
struct stat stat_i;
buffer(addr, pid, sizeof(stat_i), &stat_i);
printf("%lu", stat_i.st_size); -> fake value :/
Thank'ks !
From the man page,
PTRACE_PEEKTEXT, PTRACE_PEEKDATA
Read a word at the address addr in the tracee's memory,
returning the word as the result of the ptrace() call. Linux
does not have separate text and data address spaces, so these
two requests are currently equivalent. (data is ignored; but
see NOTES.)
Thus you must understand that tmp would hold the actually value that was read.
Your checks are wrong - you should set errno = 0 before the call and then check if it has changed. If it has - you've got an error. If it hasn't - you can be assured that tmp has the word from the remote process.
Try something like this:
int buffer(unsigned long long addr, pid_t child, size_t size, void *buffer)
{
size_t byte = 0;
size_t data;
unsigned long tmp;
// support for word aligned sizes only
if (size % sizeof(long) != 0)
return -1;
long * buffer_int = (long*) buffer;
while (byte < size) {
errno = 0;
tmp = ptrace(PTRACE_PEEKDATA, child, addr + byte);
if (errno)
return -1;
buffer_int[byte / sizeof(long)] = tmp;
byte += sizeof(long);
}
}
I am interfacing the Raspberry Pi with an accelerometer using the code:
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include "LSM9DS0.h"
void readBlock(uint8_t command, uint8_t size, uint8_t *data);
void selectDevice(int file, int addr);
void readACC(int *a);
void writeAccReg(uint8_t reg, uint8_t value);
void enableIMU();
int file;
void readBlock(uint8_t command, uint8_t size, uint8_t *data)
{
int result = i2c_smbus_read_i2c_block_data(file, command, size, data);
if (result != size)
{
printf("Failed to read block from I2C.");
exit(1);
}
}
void selectDevice(int file, int addr)
{
if (ioctl(file, I2C_SLAVE, addr) < 0) {
printf("Failed to select I2C device.");
}
}
void readACC(int *a)
{
printf("entered readACC");
uint8_t block[6];
selectDevice(file,ACC_ADDRESS);
readBlock(0x80 | OUT_X_L_A, sizeof(block), block);
*a = (int16_t)(block[0] | block[1] << 8);
*(a+1) = (int16_t)(block[2] | block[3] << 8);
*(a+2) = (int16_t)(block[4] | block[5] << 8);
printf("X axis: %4.2f, Y axis: %4.2f, Z axis: %4.2f", a, a+1, a+2);
}
void writeAccReg(uint8_t reg, uint8_t value)
{
selectDevice(file,ACC_ADDRESS);
int result = i2c_smbus_write_byte_data(file, reg, value);
if (result == -1)
{
printf ("Failed to write byte to I2C Acc.");
exit(1);
}
}
void enableIMU()
{
__u16 block[I2C_SMBUS_BLOCK_MAX];
int res, bus, size;
char filename[20];
sprintf(filename, "/dev/i2c-%d", 1);
file = open(filename, O_RDWR);
if (file<0) {
printf("Unable to open I2C bus!");
exit(1);
}
// Enable accelerometer.
writeAccReg(CTRL_REG1_XM, 0b10010111); // z,y,x axis enabled, continuos update, 100Hz data rate
writeAccReg(CTRL_REG2_XM, 0b00100000); // +/- 16G full scale
printf("Accelerometer is enabled\n");
}
int main(){
enableIMU();
printf("fine\n");
int *a;
readAcc(a);
return 0;
}
My output looks like this:
Accelerometer is enabled
fine
Segmentation fault
Based on the output, the function enableIMU is working fine, the output also shows "fine" which is right before entering readACC, but I never enter readACC because "entered readACC" doesn't get printed. Instead, I get a segmentation fault.
Do you know what I'm doing wrong here? I would really appreciate your help!
The problem in your code is:
int *a;
readAcc(a);
…
*a = (int16_t)(block[0] | block[1] << 8);
In the first line, you declare a pointer to an int. This pointer points somewhere, since it is not initialized. Then, in the last line, you write to this somewhere in memory.
Instead, you should write this:
int16_t a[3];
readAcc(a);
That way, you define the storage where the function readAcc can write to. The parameter to the readAcc function should not be a pointer to int, but a pointer to int16_t, since you use it as such.
Inside readAcc, you can then write:
a[0] = …;
a[1] = …;
a[2] = …;
Instead of the array, you could also define a struct xyz { int16_t x, y, z; }, which describes more accurately what the code does.
I would write it like this:
struct xyz {
int16_t x, y, z;
};
void readACC(struct xyz *coord)
{
printf("entered readACC\n");
selectDevice(file, ACC_ADDRESS);
uint8_t block[6];
readBlock(0x80 | OUT_X_L_A, sizeof(block), block);
coord->x = (int16_t)(block[0] | block[1] << 8);
coord->y = (int16_t)(block[2] | block[3] << 8);
coord->z = (int16_t)(block[4] | block[5] << 8);
printf("X axis: %4.2f, Y axis: %4.2f, Z axis: %4.2f\n",
coord->x / 256.0, coord->y / 256.0, coord->z / 256.0);
}
I defined the struct xyz to make the code self-explaining.
I moved the selectDevice call above the block declaration, since the block is not needed for that.
I replaced the a parameter with a better name, coord, which has an appropriate data type.
I corrected the arguments to the printf call to match the %f conversion specifier. (I hope the factor 256.0 is correct.) When you try to print an int using %f, the behavior is undefined (which is the worst thing that can happen in a C program).
I added newlines to the end of the printf format strings.
I am trying to get physical address from /proc/[pid]/pagemap using virtual address and I thought it was working fine until I tried with a simple test program.
This is my code which gets physical address using virtual address:
#include "addresstranslation.h"
#include <stdio.h>
#define PAGEMAP_ENTRY 8
#define GET_BIT(X,Y) (X & ((uint64_t)1<<Y)) >> Y
#define GET_PFN(X) X & 0x7FFFFFFFFFFFFF
#define page_mapping_file "/proc/self/pagemap"
const int __endian_bit = 1;
#define is_bigendian() ( (*(char*)&__endian_bit) == 0 )
uintptr_t virtual_to_physical_address(uintptr_t virt_addr)
{
uintptr_t file_offset = 0;
uintptr_t read_val = 0;
uintptr_t page_number = 0;
int i = 0;
int c = 0;
int pid = 0;
int status = 0;
unsigned char c_buf[PAGEMAP_ENTRY];
FILE *f = fopen(page_mapping_file, "rb");
if(!f)
{
// if this happens run as root
printf("Error! Cannot open %s. Please, run as root.\n", page_mapping_file);
return 0;
}
file_offset = virt_addr / getpagesize() * PAGEMAP_ENTRY;
status = fseek(f, file_offset, SEEK_SET);
if(status)
{
printf("Error! Cannot seek in %s.\n", page_mapping_file);
perror("Failed to do fseek!");
fclose(f);
return 0;
}
for(i = 0; i < PAGEMAP_ENTRY; i++)
{
c = getc(f);
if(c == EOF)
{
fclose(f);
return 0;
}
if(is_bigendian())
{
c_buf[i] = c;
}
else
{
c_buf[PAGEMAP_ENTRY - i - 1] = c;
}
}
for(i=0; i < PAGEMAP_ENTRY; i++)
{
read_val = (read_val << 8) + c_buf[i];
}
/*
if(GET_BIT(read_val, 63))
{
page_number = GET_PFN(read_val);
printf("%d \n", page_number);
}
else
{
printf("Page not present\n");
}
if(GET_BIT(read_val, 62))
{
printf("Page swapped\n");
}
*/
fclose(f);
return read_val;
}
addresstranslation.h:
/*
* addresstranslation.h
*
* Translates virtual to physical address.
*/
#ifndef __ADDRESS_TRANSLATION_H
#define __ADDRESS_TRANSLATION_H
#include <inttypes.h>
#include <stdint.h>
uintptr_t virtual_to_physical_address(uintptr_t virt_addr);
#endif
And this is the simple test that I tried.
#include <stdlib.h>
#include <stdio.h>
#include "addresstranslation.h"
int main(int argc, char* argv[])
{
int *a1, *a2, *b1,*c1, *b2 = NULL;
int N = 4096;
uintptr_t ap1, ap2, bp1, bp2 = 0;
printf("Test virtual to physical address translation.\n");
a1 = (int*)malloc(sizeof(int) * N);
if (!a1)
{
printf("Error: cannot allocate memory for a\n");
return 1;
}
b1 = (int*)malloc(sizeof(int) * N);
if (!b1)
{
printf("Error: cannot allocate memory for b\n");
return 1;
}
ap1 = virtual_to_physical_address((uintptr_t)a1);
bp1 = virtual_to_physical_address((uintptr_t)b1);
printf("a1_virt= %p: a1_phys= %" PRIxPTR "\n", a1, ap1);
printf("b1_virt= %p b1_phys= %" PRIxPTR "\n", b1, bp1);
a2 = a1 + 1000;
b2 = b1 + 1;
ap2 = virtual_to_physical_address((uintptr_t)a2);
bp2 = virtual_to_physical_address((uintptr_t)b2);
printf("a2_virt= %p a2_phys= %" PRIxPTR "\n", a2, ap2);
printf("b2_virt= %p b2_phys= %" PRIxPTR "\n", b2, bp2);
printf("Done\n");
}
Which prints something like this:
Test virtual to physical address translation.
a1_virt= 0x958d008: a1_phys= 4f8ce
b1_virt= 0x9591010 b1_phys= 4d40b
a2_virt= 0x958dfa8 a2_phys= 4f8ce
b2_virt= 0x9591014 b2_phys= 4d40b
Done
As you can see a1 and a2 have different virtual addresses but have same physical address, my question is; Is my virtual-physical address conversion faulty or is it about Linux memory management and it is possible one physical address can be mapped to two different virtual addresses ?
the C language is case sensitive.
so this macro:
#DEFINE PAGE_MAPPING_FILE "/PROC/self/pagemap"
is not invoked when using:
FILE *f = fopen(page_mapping_file, "rb");
and #DEFINE means nothing to the C compiler.
The correct spelling is: #define I.E. all lower case
I have a hex file of 327680 characters which I'm writing to physical address 0x30000000 - 0x3004FFFF on the memory on my ARM linux system.
While reading back from the memory I'm getting a segfault after reading 64170 characters from the start address, ie at 0x3000FAAA.
If I change my starting address to 0x3000FA64, then also I get a segfault after 64170 characters.
How do I ensure data is accessed correctly if Data > 4kB (page size) ?
I'm unable to understand the exact problem, so I'm adding the snippet of my code below:
#define MAX_RANGE 327679
int fd;
FILE* fd_table=NULL;
unsigned long int count = 0 ;
void * mem;
void * aligned_vaddr;
unsigned long aligned_paddr;
uint32_t aligned_size;
unsigned long int addr_phys;
uint8_t *addr;
int g_size = 1;
unsigned long int g_paddr = 0x30000000; //Starting physical address
while((count<MAX_RANGE)){
g_paddr = addr_phys;
g_paddr &= ~(g_size - 1);
aligned_paddr = g_paddr & ~(4096 - 1);
aligned_size = g_paddr - aligned_paddr + (g_count * g_size);
aligned_size = (aligned_size + 4096 - 1) & ~(4096 - 1);
/* Align address to access size */
aligned_vaddr = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, aligned_paddr);
if (aligned_vaddr == NULL) {
printf("Error mapping address\n");
close(fd);
return 1;
}
mem = (void *)((uint32_t)aligned_vaddr + (g_paddr - aligned_paddr));
addr = mem;
fprintf(fd_table, "%02X\n",addr[0]);
addr_phys +=1; //Increment byte address
count++;
}
Note:
1. There is no error in the write process, I have verified by viewing the segfault address with memtool.
2. The address 0x30000000 onwards is not used by the system (I have ensured that in the u-boot).