ioctl giving error in different executions - c

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

Related

MPU6050 motion driver library function call cause hardfault exception

Hi I'm try to use mpu6050 on my stm32 project.
I copy the motion driver library from SparkFun_MPU-9250-DMP_Arduino_Library and replace arduino function like arduino_i2c_write to stm32 write function. The replace part work fine. The stm32 board did write bytes into the imu and read from it.
However, when I try to setup the dmp funtion and use the mpu_load_firmware function, I encouter a weird situation.
int mpu_load_firmware(unsigned short length, const unsigned char *firmware,
unsigned short start_addr, unsigned short sample_rate)
{
unsigned short ii;
unsigned short this_write;
/* Must divide evenly into st.hw->bank_size to avoid bank crossings. */
#define LOAD_CHUNK (16)
unsigned char cur[LOAD_CHUNK], tmp[2];
if (st.chip_cfg.dmp_loaded)
/* DMP should only be loaded once. */
return -1;
if (!firmware)
return -1;
for (ii = 0; ii < length; ii += this_write) {
this_write = min(LOAD_CHUNK, length - ii);
if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii])))
return -1;
if (mpu_read_mem(ii, this_write, cur))
return -1;
if (memcmp(firmware+ii, cur, this_write))
return -2;
}
/* Set program start address. */
tmp[0] = start_addr >> 8;
tmp[1] = start_addr & 0xFF;
if (i2c_write(st.hw->addr, st.reg->prgm_start_h, 2, tmp))
return -1;
st.chip_cfg.dmp_loaded = 1;
st.chip_cfg.dmp_sample_rate = sample_rate;
return 0;
}
When the code run to the line if (mpu_write_mem(ii, this_write, (unsigned char*)&(firmware[ii]))), it cause a hardfault. And I found out that the code cannnot call the mpu_write_mem function porperly. When I start to step in the function, the hardfault exception immediately occurred. I cannot figure it out what cause the exception and how to fix it.
I've checked the pointer, &(firmware[ii]), address, it looks just fine. But the weired thing is that in the mpu_write_mem function, the argument value all differ from the mpu_load_firmware function.(mem_addr != ii, length != this_write, ...) I'm not sure if this cause the exception or the other way around.
Can anyone give me some idea? Thank you very much ~~
Here is the mpu_write_mem function, both function are in the inv_mpu.c file
int mpu_write_mem(unsigned short mem_addr, unsigned short length,
unsigned char *data)
{
unsigned char tmp[2];
if (!data)
return -1;
if (!st.chip_cfg.sensors)
return -1;
tmp[0] = (unsigned char)(mem_addr >> 8);
tmp[1] = (unsigned char)(mem_addr & 0xFF);
/* Check bank boundaries. */
if (tmp[1] + length > st.hw->bank_size)
return -1;
if (i2c_write(st.hw->addr, st.reg->bank_sel, 2, tmp))
return -1;
if (i2c_write(st.hw->addr, st.reg->mem_r_w, length, data))
return -1;
return 0;
}

retrieve information from a structure with ptrace

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);
}
}

Function sscanf must be assigned to variable otherwise strange behavior

Consider this code:
#define TRANSLATOR_requestElectricityMeterWrite() do{addr = word_getAddress(); value = word_getValue(); }while(0)
uint16_t value;
uint8_t addr;
bool dispatcher(void)
{
TRANSLATOR_requestElectricityMeterWrite();
return true;
} // AFTER this point (during debug) program goes to default handler
int main(void)
{
if(dispatcher())
continue;
. . . .
. . . .
}
uint16_t word_getValue(void)
{
uint16_t value;
sscanf("ABCD", "%4x", (unsigned int *)&value);
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
sscanf("00", "%2x", (unsigned int *)&address);
;
return address;
}
When the code above is run, the statement inside if causes program to crash(goes to some default handler).
But when I change the two(word_getValue and word_getAddres) functions to this:
uint16_t word_getValue(void)
{
uint16_t value;
int i = 0;i++;
i = sscanf(WORD_getValueString(), "%4x", (unsigned int *)(&value));
return value;
}
uint8_t word_getAddress(void)
{
uint8_t address;
int i = 0;i++;
i = sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
return address;
}
It works. The addition if the dummy i seems to solve that problem. But why doesn't it work the other way?
GNU ARM v4.8.3 toolchain
Both functions invoke undefined behavior, hence anything can happen. Adding an extra local variable changes the location of the destination variable, hiding the effect of its incorrect size.
sscanf("ABCD", "%4x", (unsigned int *)&value);
sscanf will store sizeof(unsigned int) bytes (probably 4) into variable value, which has only 2 bytes.
sscanf(WORD_getNameString(), "%2x", (unsigned int *)(&address));
Will store sizeof(unsigned int) bytes into variable address, which has only 1 byte.
The easiest way to fix this problem is to parse into an unsigned int and store the parsed value to the destination separately, or simply return the value:
uint16_t word_getValue(void) {
unsigned int value;
if (sscanf(WORD_getValueString(), "%4x", &value) == 1)
return value;
// could not parse a value, return some default value or error code
return 0;
}
uint8_t word_getAddress(void) {
unsigned int address;
if (sscanf(WORD_getNameString(), "%2x", &address) == 1)
return address;
// could not parse a value, return some default value or error code
return 0;
}
You might also want to verify if the parsed value is within range for the destination type, but since you limit the parse to respectively 4 and 2 hex digits, overflow cannot happen.
%x format requires unsigned argument (suppose it's uint32_t on your platform). If you pass uint16_t or uint8_t it can corrupt memory. In your case it corrupt stack and overwrites return address. Try use %4hx for uint16_t and %2hhx for uint8_t.

Suggestions to handle `Wframe-larger-than`-warning on kernel module

Hello and a happy new year,
I'm working on a kernel-module. It is necessary to do a numeric calculation of some parameter to set up the device correctly.
The function works perfectly but the gcc compiler (I'm using kbuild) gives me the warning:
warning: the frame size of 1232 bytes is larger than 1024 bytes [-Wframe-larger-than=]
If I'm right this means that space local variables exceed a limitation given by the machine the module compiled on.
There are some questions now:
Does this warning refer to the whole memory space needed for the module, this explicit function or this function and its sub-functions?
How critical is this?
I don't see a way to reduce the needed memory. Are there some suggestions to handle this? Any how-to?
Maybe it is helpful: The calculation uses a 64bit fixed-point-arithmetic. All the functions of this library are inline functions.
Thanks in advance
Alex
Following the advice from #Tsyvarev the problem could reduce to the allocation in a function as this example shows (I know that the code doesn't make sense - it's only for showing how I declare the variables inside the functions):
uint8_t getVal ( uint8_t )
{
uint64_t ar1[128] = {0};
uint64_t ar2[128] = {0};
uint8_t val;
// a much of stuff
return val;
}
void fun ( void )
{
uint64_t ar1[128] = {0};
uint64_t ar2[128] = {0};
uint8_t cnt;
for(cnt=0; cnt<128; cnt++)
{
ar1[cnt] = getVal(cnt);
ar1[cnt] = getVal(cnt);
}
}
to point 3:
As suggested the solution is to store the data to the heap with kmalloc instead to the stack.
uint8_t getVal ( uint8_t )
{
uint64_t *ar1;
uint64_t *ar2;
uint8_t val, cnt;
// allocate memory on the heap
ar1 = kmalloc(sizeof(uint64_t), 128);
ar2 = kmalloc(sizeof(uint64_t), 128);
// initialize the arrays
for(cnt=0; cnt<128; cnt++)
{
ar1[cnt] = 0;
ar2[cnt] = 0;
}
// a much of stuff
return val;
}
void fun ( void )
{
uint64_t *ar1;
uint64_t *ar2;
uint8_t cnt;
// allocate memory on the heap
ar1 = kmalloc(sizeof(uint64_t), 128);
ar2 = kmalloc(sizeof(uint64_t), 128);
// initialize the arrays
for(cnt=0; cnt<128; cnt++)
{
ar1[cnt] = 0;
ar2[cnt] = 0;
}
for(cnt=0; cnt<128; cnt++)
{
ar1[cnt] = getVal(cnt);
ar1[cnt] = getVal(cnt);
}
}

copying struct 8 bytes at at time

I'm working in the arduino environment. I have a struct defined as follows. The struct will ultimately be encrypted and sent wirelessly over a radio link layer. It's 32bytes long.
struct SENSORTYPE{
int sensor1:8;
int sensor2:8;
int sensor3:8;
int sensor4:8;
};
struct SENSOR{
float sensor1;
float sensor2;
float sensor3;
float sensor4;
};
struct HEADER{
byte type;
short id;
short to;
short from;
byte version;
long _buff;
SENSORTYPE sensortype;
SENSOR sensor;
};
HEADER header;
I have an XTEA encryption/decryption routine that's defined as follows and is verified to work. It operates on two 32bit blocks at at time.
void xteaEncrypt( unsigned long v[2])
void xteaDecrypt(unsigned long v[2])
What I'm trying to do is run header through xteaEncrypt. I'm getting tripped up on converting my struct to the two 32bit numbers. The following is what I have so far.
#define BLOCK_SIZE 8
header.type = 1; //test value
header._buff = 1; //test value
byte data[BLOCK_SIZE]; //8 byte buffer to encrypt/decrypt
byte buff[32]; //32 byte buffer to put encrypted/decrypted data into
for (uint32_t i = 0; i < 4; i++){ //4 times (4 * 8 = 32)
memcpy(data, &header+(i*BLOCK_SIZE), BLOCK_SIZE); //copy 8 bytes from header struct into data
xteaEncrypt((uint32_t*)data); //encrypt data
memcpy(&buff+(i*8), data, BLOCK_SIZE); //put encrypted data into the new buffer
}
memcpy(&header, &buff, sizeof(header)); //copy into original header for convenience
//now decrypt it back
for (uint32_t i = 0; i < 4; i++){
memcpy(data, &header+(i*BLOCK_SIZE), BLOCK_SIZE);
xteaDecrypt((uint32_t*)data);
memcpy(&buff+(i*8), data, BLOCK_SIZE);
}
memcpy(&header, &buff, sizeof(header));
After encryption header.type = 0xee and header._buff = C0010000. After decryption, header.type = 1 and _buff still = C0010000 so it would seem there is an error in my memcpy'ing but I can't find it. Any help would be greatly appreciated. This one has been particularly hard to debug for me. If I'm going about this completely wrong let me know and point me in the right direction.
You're getting tripped up by pointer arithmetic. Consider the snippet &header+(i*BLOCK_SIZE). What you evidently expect to happen is that you'll get some address, let's call it addr, and then add some small number to it to calculate a new address, i.e.
finalAddress = addr + (i * 8);
But because &header is a pointer to a struct HEADER, the actual calculation the compiler does is
finalAddress = addr + (sizeof(struct HEADER) * i * 8);
The result is an address that's well beyond the end of the header for any i greater than 0. The same thing is happening with &buff+(i*8), since &buff is a pointer to 32 bytes.
To solve the problem, I recommend using intermediate variables that are char *, e.g.
char *headAddress = (char *)&header;
char *buffAddress = (char *)&buff;
for (uint32_t i = 0; i < 4; i++)
{
memcpy(data, headAddress+(i*BLOCK_SIZE), BLOCK_SIZE);
xteaEncrypt((uint32_t*)data);
memcpy(&buffAddress+(i*BLOCK_SIZE), data, BLOCK_SIZE);
}

Resources