WRITE and READ memory mapped device registers in Linux on ARM - c

I am trying to read and write registers on my ARM9 (SAM9X25) following those steps : http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka3750.htmlI ended with the following code :
#include "stdio.h"
#define PIO_WPMR_BANK_D 0xFFFFFAE4 // PIO Write Protection Mode Register Bank D
#define PIO_PUER_BANK_D 0xFFFFFA64 // PIO Pull-Up Enable Register Bank D
#define PIO_PUSR_BANK_D 0xFFFFFA68 // PIO Pull-Up Status Register Bank D
#define MASK_LED7 0xFFDFFFFF // LED7 Mask
#define DESABLE_WRITE_PROTECTION_BANK_D 0x50494F00 // Desable write protection Bank D
int main(void) {
printf("test");
unsigned int volatile * const register_PIO_WPMR_BANK_D = (unsigned int *) PIO_WPMR_BANK_D;
unsigned int volatile * const register_PIO_PUSR_BANK_D = (unsigned int *) PIO_PUSR_BANK_D;
unsigned int volatile * const port_D = (unsigned int *) PIO_PUER_BANK_D;
*register_PIO_WPMR_BANK_D = DESABLE_WRITE_PROTECTION_BANK_D;
*port_D = *register_PIO_PUSR_BANK_D & MASK_LED7;
return 0; }
I cross compiled my code in Ubuntu 16.04 like so arm-linux-gnueabi-gcc gpio.c -o gpio But I have a Segmentation Fault just after the printf during the execution of the program on my board. I know the addresses are right... So why do I have this error?Is it the good way ?Thank you for your help !
SOLUTION :
Thank you to #vlk I could make it work ! Here is a little example for toggling a LED :
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define _PIOD_BANK_D 0xA00
#define _PIO_OFFSET 0xFFFFF000
/* When executing this on the board :
long sz = sysconf(_SC_PAGESIZE);
printf("%ld\n\r",sz);
We have 4096.
*/
#define _MAP_SIZE 0x1000 // 4096
#define _WPMR_OFFSET 0x0E4 // PIO Write Protection Mode Register Bank D
#define _PIO_ENABLE 0x000
#define _PIO_DISABLE 0x004
#define _PIO_STATUS 0x008
#define _OUTPUT_ENABLE 0x010
#define _OUTPUT_DISABLE 0x014
#define _OUTPUT_STATUS 0x018
#define _FILTER_ENABLE 0x020
#define _FILTER_DISABLE 0x024
#define _FILTER_STATUS 0x028
#define _OUTPUT_DATA_SET 0x030
#define _OUTPUT_DATA_CLEAR 0x034
#define _OUTPUT_DATA_STATUS 0x038
#define _PIN_DATA_STATUS 0x03c
#define _MULTI_DRIVER_ENABLE 0x050
#define _MULTI_DRIVER_DISABLE 0x054
#define _MULTI_DRIVER_STATUS 0x058
#define _PULL_UP_DISABLE 0x060
#define _PULL_UP_ENABLE 0x064
#define _PULL_UP_STATUS 0x068
#define _PULL_DOWN_DISABLE 0x090
#define _PULL_DOWN_ENABLE 0x094
#define _PULL_DOWN_STATUS 0x098
#define _DISABLE_WRITE_PROTECTION 0x50494F00 // Desable write protection
#define LED_PIN 21
int main(void) {
volatile void *gpio_addr;
volatile unsigned int *gpio_enable_addr;
volatile unsigned int *gpio_output_mode_addr;
volatile unsigned int *gpio_output_set_addr;
volatile unsigned int *gpio_output_clear_addr;
volatile unsigned int *gpio_data_status_addr;
volatile unsigned int *gpio_write_protection_addr;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0){
fprintf(stderr, "Unable to open port\n\r");
exit(fd);
}
gpio_addr = mmap(NULL, _MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PIO_OFFSET);
if(gpio_addr == MAP_FAILED){
handle_error("mmap");
}
gpio_write_protection_addr = gpio_addr + _PIOD_BANK_D + _WPMR_OFFSET;
gpio_enable_addr = gpio_addr + _PIOD_BANK_D + _PIO_ENABLE;
gpio_output_mode_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_ENABLE;
gpio_output_set_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_SET;
gpio_output_clear_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_CLEAR;
gpio_data_status_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_STATUS;
*gpio_write_protection_addr = _DISABLE_WRITE_PROTECTION;
*gpio_enable_addr = 1 << LED_PIN;
*gpio_output_mode_addr = 1 << LED_PIN; // Output
// If LED
if((*gpio_data_status_addr & (1<<LED_PIN)) > 0){
*gpio_output_clear_addr = 1 << LED_PIN;
}else{
*gpio_output_set_addr = 1 << LED_PIN;
}
return 0;
}
EDIT :
Answer for the 3) in the comments. You have to change the mmap and the assignations like so if you want it to work with all the offsets (i.e : mmap example):
#define _PIO_OFFSET 0xFFFFFA00 // Instead of 0xFFFFF000
#define _MAP_SIZE 0x1000 // 4096
#define _MAP_MASK (_MAP_SIZE - 1)
#define _PA_OFFSET _PIO_OFFSET & ~_MAP_MASK
And the mmap :
gpio_addr = mmap(NULL, _MAP_SIZE + _PIO_OFFSET - _PA_OFFSET, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PA_OFFSET);
And for the assignation :
gpio_enable_addr = gpio_addr + _PIO_OFFSET - (_PA_OFFSET) + _PIO_ENABLE;

You can't access registers directly, because Linux use MMU and this create for your application virtual address space which is different than physical MCU address space and access outside this virtual address space cause segmentation fault.
Only Way to access these registers in Linux (if you don't want to write kernel drivers) is to open file /dev/mem as file and map it with mmap
For example I have small python library for access GPIO registers on Atmel SAM MCU gpiosam. You can inspire and port it to C.

busybox devmem
busybox devmem is a tiny CLI utility that mmaps /dev/mem.
You can get it in Ubuntu with: sudo apt-get install busybox
Usage: read 4 bytes from the physical address 0x12345678:
sudo busybox devmem 0x12345678
Write 0x9abcdef0 to that address:
sudo busybox devmem 0x12345678 w 0x9abcdef0
See this for a few tips on how to test it out: Accessing physical address from user space
Also mentioned at: https://unix.stackexchange.com/questions/4948/shell-command-to-read-device-registers

Related

mmap behaviour changed after OS upgrade?

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

Writing to socket fails with the addition of any single line of code to c program

I'm writing a c program designed to transfer data over a socket to python client. I've encountered a really strange issue where writing to the created socket stops working with the addition of virtually any line of code after a certain point in the program.
To expand on the issue: I'm trying to write an arbitrary number to the socket, e.g. 256. This works fine, i.e. the python client receives the number 256 from the c server, as long as there aren't any commands beyond get_status(s2mm_status,"S2MM"); If there are any such commands beyond this point, the "0" gets written to the socket instead of whatever number (e.g. 256) I've specified. Anything as simple as the declaration "int i" results in this behavior.
Needless to say, I'm confused. I'm wondering if I'm using too much memory or something? I just don't know how to diagnose the problem. Anyway, I've provided the code below. Sorry about the length, I just really don't know what is or is not relevant.
To provide a basic explanation of the code: a number of memory maps are created so that I can write specific values to registers, which initiates the recording of data from an ADC aboard the device, writing of this data to memory, and then passing this data over a socket to the python client.
#include<stdio.h>
#include<string.h> //strlen
#include<stdlib.h>
#include<sys/socket.h>
#include<arpa/inet.h> // inet_addr
#include<unistd.h> //write
#include<pthread.h> //for threading, link with lpthread
#include<sys/mman.h>
#include <fcntl.h>
#include <arpa/inet.h> //https://linux.die.net/man/3/htonl
#include <time.h>
#include<math.h>
#include "server_library.h"
/*********************************/
//Address Editor
/*********************************/
//ps_0
// Data
// axi_dma_0 S_AXI_LITE reg 0x4040_0000 64K 0x4040_FFFF
//axi_dma_0
// Data_SG
// ps_0 S_AXI_HP0 HP0_DDR_LOWOCM 0x0800_0000 128M 0x07FF_FFFF
// DATA_MM2S
// ps_0 S_AXI_HP0 HP0_DDR_LOWOCM 0x0800_0000 128M 0x07FF_FFFF
// DATA_S2MM
// ps_0 S_AXI_HP0 HP0_DDR_LOWOCM 0x0800_0000 128M 0x07FF_FFFF
/*********************************/
/*********************************/
//Addresses
/*********************************/
#define AXI_DMA_ADDRESS 0x40400000
#define HP0_ADDRESS 0x08000000 //Formerly HP0_DMA_BUFFER_MEM_ADDRESS
#define MM2S_BASE_DESC_ADDR HP0_ADDRESS //Formerly HP0_MM2S_DMA_DESCRIPTORS_ADDRESS
#define S2MM_BASE_DESC_ADDR HP0_ADDRESS+MEMBLOCK_WIDTH+1 //Formerly HP0_S2MM_DMA_DESCRIPTORS_ADDRESS
#define MM2S_SOURCE_ADDRESS HP0_ADDRESS+SG_DMA_DESCRIPTORS_WIDTH+1 //Formerly HP0_MM2S_SOURCE_MEM_ADDRESS
#define S2MM_TARGET_ADDRESS HP0_ADDRESS+(MEMBLOCK_WIDTH+1)+SG_DMA_DESCRIPTORS_WIDTH+1 //Formerly HP0_S2MM_TARGET_MEM_ADDRESS
// AXI_DMA_ADDRESS: 0x40400000
// HP0_ADDRESS: 0x08000000
// MM2S_BASE_DESC_ADDR: 0x08000000
// S2MM_BASE_DESC_ADDR: 0x0c000000
// MM2S_SOURCE_ADDRESS: 0x08010000
// S2MM_TARGET_ADDRESS: 0x0c010000
/*********************************/
//Miscellaneous
/*********************************/
#define DESCRIPTOR_REGISTERS_SIZE 0xFFFF
#define SG_DMA_DESCRIPTORS_WIDTH 0xFFFF
#define MEMBLOCK_WIDTH 0x3FFFFFF //Size of memory used by S2MM and MM2S
#define BUFFER_BLOCK_WIDTH 0x7D0000 //Size of memory block per descriptor in bytes
#define NUM_OF_DESCRIPTORS 0x02 //Number of descriptors for each direction
#define START_FRAME 0x08000000 //TXSOF = 1 TXEOF = 0
#define MID_FRAME 0x00000000 //TXSOF = TXEOF = 0
#define COMPLETE_FRAME 0x0C000000 //TXSOF = TXEOF = 1
#define END_FRAME 0x04000000 //TXSOF = 0 TXEOF = 1
#define CYCLIC_ENABLE 0x10
#define TRANSFER_BYTES 1024
#define TRANSFER_INTS 256
#define TRANSFER_BITS 8*TRANSFER_BYTES
#define N_DESC 2
/*********************************/
//Offsets
/*********************************/
// MM2S CONTROL
#define MM2S_CONTROL_REGISTER 0x00 // MM2S_DMACR
#define MM2S_STATUS_REGISTER 0x04 // MM2S_DMASR
#define MM2S_CURDESC 0x08 // must align 0x40 addresses
#define MM2S_CURDESC_MSB 0x0C // unused with 32bit addresses
#define MM2S_TAILDESC 0x10 // must align 0x40 addresses
#define MM2S_TAILDESC_MSB 0x14 // unused with 32bit addresses
#define SG_CTL 0x2C // CACHE CONTROL
// S2MM CONTROL
#define S2MM_CONTROL_REGISTER 0x30 // S2MM_DMACR
#define S2MM_STATUS_REGISTER 0x34 // S2MM_DMASR
#define S2MM_CURDESC 0x38 // must align 0x40 addresses
#define S2MM_CURDESC_MSB 0x3C // unused with 32bit addresses
#define S2MM_TAILDESC 0x40 // must align 0x40 addresses
#define S2MM_TAILDESC_MSB 0x44 // unused with 32bit addresses
//Scatter/Gather Control
#define NEXT_DESC 0x00 //Set to address of next descriptor in chain
#define BUFF_ADDR 0x08 //Set to address to read from (MM2S) or write to (S2MM)
#define CONTROL 0x18 //Set transfer length, T/RXSOF and T/RXEOF
#define STATUS 0x1C //Descriptor status
/*********************************/
//Functions
/*********************************/
unsigned int set_offset(unsigned int* virtual_address, int offset, unsigned int value){
virtual_address[offset>>2] = value;
}
unsigned int get_offset(unsigned int* virtual_address, int offset) {
return virtual_address[offset>>2];
}
void print_offset(unsigned int* virtual_address, int offset){
unsigned int value = get_offset(virtual_address,offset);
printf("0x%08x\n",value);
}
void memsend(void* virtual_address, int byte_count, int socket_desc)
{
unsigned int *p = virtual_address;
int offset;
for(offset = 0; offset<byte_count;offset++){
printf("0x%08x\n",p[offset]);
write(socket_desc,&p[offset],sizeof(p[offset]));
}
}
void memdump(void* virtual_address, int byte_count)
{
unsigned int *p = virtual_address;
int offset;
for(offset = 0; offset<byte_count;offset++){
printf("%d: 0x%08x\n",offset,p[offset]);
}
}
void get_status(uint32_t status, char *type){
if(type == "S2MM"){
printf("S2MM status 0x%08x #0x%08x\n",status,AXI_DMA_ADDRESS+S2MM_STATUS_REGISTER);
}
if(type == "MM2S"){
printf("MM2S status 0x%08x #0x%08x\n",status,AXI_DMA_ADDRESS+MM2S_STATUS_REGISTER);
}
if(status & 0x00000001) printf(" Halted");
if(status & 0x00000002) printf(" Idle");
if(status & 0x00000008) printf(" SGIncld");
if(status & 0x00000010) printf(" DMAIntErr");
if(status & 0x00000020) printf(" DMASlvErr");
if(status & 0x00000040) printf(" DMADecErr");
if(status & 0x00000100) printf(" SGIntErr");
if(status & 0x00000200) printf(" SGSlvErr");
if(status & 0x00000400) printf(" SGDecErr");
if(status & 0x00001000) printf(" IOC_Irq");
if(status & 0x00002000) printf(" Dly_Irq");
if(status & 0x00004000) printf(" Err_Irq");
else printf(" running");
printf("\n");
}
int main(){
int mm2s_status, s2mm_status;
uint32_t mm2s_base_descriptor_address; //Formerly mm2s_current_descriptor_address
uint32_t s2mm_base_descriptor_address; //Formerly s2mm_current_descriptor_address
uint32_t mm2s_tail_descriptor_address;
uint32_t s2mm_tail_descriptor_address;
int fd = open("/dev/mem",O_RDWR|O_SYNC);
//Create 64K AXI DMA Memory Map at AXI_DMA_ADDRESS (0x40400000)
unsigned int* axi_dma = mmap(NULL,DESCRIPTOR_REGISTERS_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,AXI_DMA_ADDRESS);
//Create 64k mm2s descriptors memory map at HP0_MM2S_DMA_DESCRIPTORS_ADDRESS (0x08000000)
unsigned int* mm2s_descriptors = mmap(NULL,DESCRIPTOR_REGISTERS_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,MM2S_BASE_DESC_ADDR); //Formerly mm2s_descriptor_register_mmap
//Create 64k s2mm descriptors memory map at HP0_S2MM_DMA_DESCRIPTORS_ADDRESS (0x0c000000)
unsigned int* s2mm_descriptors = mmap(NULL,DESCRIPTOR_REGISTERS_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,S2MM_BASE_DESC_ADDR); //Formerly s2mm_descriptor_register_mmap
//Create ~1Mb x Num_Descriptors source memory map at HP0_MM2S_SOURCE_MEM_ADDRESS (0x08010000)
unsigned int* source_memory = mmap(NULL,BUFFER_BLOCK_WIDTH*NUM_OF_DESCRIPTORS,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(off_t)(MM2S_SOURCE_ADDRESS)); //Formerly source_mmap
//Create ~1Mb x Num_Descriptors target memory map at HP0_S2MM_TARGET_MEM_ADDRESS (0x0c010000)
unsigned int* dest_memory = mmap(NULL,BUFFER_BLOCK_WIDTH*NUM_OF_DESCRIPTORS,PROT_READ|PROT_WRITE,MAP_SHARED,fd,(off_t)(S2MM_TARGET_ADDRESS)); //Formerly dest_mmap
//Clear mm2s descriptors
memset(mm2s_descriptors,0x00000000,DESCRIPTOR_REGISTERS_SIZE);
//Clear s2mm descriptors
memset(s2mm_descriptors,0x00000000,DESCRIPTOR_REGISTERS_SIZE);
//Clear Target Memory
memset(dest_memory,0x00000000,NUM_OF_DESCRIPTORS*BUFFER_BLOCK_WIDTH/4);
//Reset and halt all DMA operations
set_offset(axi_dma,MM2S_CONTROL_REGISTER,0x4);
set_offset(axi_dma,S2MM_CONTROL_REGISTER,0x4);
set_offset(axi_dma,MM2S_CONTROL_REGISTER,0x0);
set_offset(axi_dma,S2MM_CONTROL_REGISTER,0x0);
mm2s_status = get_offset(axi_dma,MM2S_STATUS_REGISTER);
s2mm_status = get_offset(axi_dma,S2MM_STATUS_REGISTER);
get_status(mm2s_status,"MM2S");
get_status(s2mm_status,"S2MM");
/*********************************************************************/
// Any code after this point, save for a print function,
// breaks the write function (line 223) below.
//Specifically, it goes from correctly writing TRANSFER_INTS = 256
//to writing 0 to "new_socket"
//????
/*********************************************************************/
/*********************************************************************/
// Open a server
/*********************************************************************/
//Define variables
int new_socket,c;
int socket_desc;
struct sockaddr_in created_server_addr, client_addr;
//Create Socket
socket_desc = open_socket(socket_desc);
//Create Server
created_server_addr = server_structure(created_server_addr);
//Bind Socket
bind_socket(socket_desc,created_server_addr);
//Listen
listen(socket_desc,3);
puts("Waiting for incoming connections...");
//Transfer Data over socket
while(new_socket=accept(socket_desc,(struct sockaddr *)&client_addr, (socklen_t*)&c))
{
puts("Connection Accepted.\n");
printf("Transfer Size: %d\n",TRANSFER_INTS);
uint32_t htonl_TRANSFER_INTS = htonl(TRANSFER_INTS);
write(new_socket, &htonl_TRANSFER_INTS,sizeof(htonl_TRANSFER_INTS));
printf("Write Data (Destination memory block): \n");
close(new_socket);
break;
}
close(socket_desc);
/*********************************************************************/
}
I'm also including the "server_library.h" file for reference, which I wrote ti simplify the process of starting a socket a little bit. I can provide the python client code on request, but I'm already worried this is way too much code to sift through already.
#ifndef MY_HEADER_H
#define MY_HEADER_H
//Start/open Socket
int open_socket(int socket_desc)
{
int option = 1;
socket_desc = socket(AF_INET,SOCK_STREAM,0);
if(socket_desc == -1)
{
printf("Could not create socket\n");
}
//This stupid line fixes binds failing
setsockopt(socket_desc,SOL_SOCKET,SO_REUSEADDR,(void *)&option,sizeof(option));
return socket_desc;
}
//Prepare server structure
struct sockaddr_in server_structure(struct sockaddr_in server_addr)
{
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8888);
return server_addr;
}
//Bind Socket
void bind_socket(int socket_desc, struct sockaddr_in server_addr)
{
if(bind(socket_desc,(struct sockaddr *)&server_addr, sizeof(server_addr))<0)
{
puts("Bind Failed\n");
}else{
puts("Bind Done\n");
}
}
//Send Message (uint32_t)
//Doesn't Work I dunno why
void send_int(uint32_t message, int socket)
{
message = htonl(message);
write(socket, &message, sizeof(message));
}
#endif
I'm sorry to be so vague here and throw so much code out, I just don't even really have any ideas for how to diagnose this issue, let alone start narrowing the cause down.
Anyway, thank you for the help. I'm happy to provide as much additional information as I am able.
When changing the code in unrelated ways breaks a program, here are some things to check for:
1. Stack overflow (this is more common in firmware/embedded code on microprocessors, but can happen especially with recursion or if you put large arrays and such on the stack)
2. Wild pointers, out-of-bounds writes to arrays or strings, etc. which lead to a memory overwrite (changing the code in an unrelated way may change the memory layout such that the memory overwrite is in either a harmless or a harmful location) [Do you check that all the arrays you try to allocate are successfully allocated?] [uses of set_offset are candidates for out-of-bounds access]
That is where I would start.
And, of course, turning on compiler warnings and checking return values from all function calls that have them.
If you do all of the above, you will either find the bug, or you can give us information which will help us to help you better.

Unable to read PCI BAR from kernel mode

I have written some code to probe registers of an AMD Vega from userspace using a mmap of the /sys resource. This is working and I am able to read the register values without any problems. However when I implement the kernel mode version of this, all reads return 0xffffffff. I feel like I am missing something simple as the userspace code is returning expected values, where the kernel code fails to read the registers.
Here is the working userspace code:
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdint.h>
#define RES_PATH "/sys/devices/pci0000:40/0000:40:01.3/0000:42:00.0/0000:43:00.0/0000:44:00.0/resource5"
#define RES_SIZE (512*1024)
#define MP0_BASE 0x16000
#define mmMP0_SMN_C2PMSG_33 ((MP0_BASE + 0x61) * 4)
#define mmMP0_SMN_C2PMSG_64 ((MP0_BASE + 0x80) * 4)
#define mmMP0_SMN_C2PMSG_81 ((MP0_BASE + 0x91) * 4)
void * map;
uint32_t _preadl(const char * name, uint32_t reg)
{
uint32_t val = *(uint32_t*)(map + reg);
printf("readl %20s (0x%08x / 0x%08x) = 0x%08x\n", name, reg / 4, reg, val);
return val;
}
#define preadl(x) _preadl(#x, x)
int main(int argc, char * argv[])
{
int fd = open(RES_PATH, O_RDWR | O_SYNC, 0);
map = mmap(NULL, RES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("mmap = %p\n", map);
preadl(mmMP0_SMN_C2PMSG_33);
preadl(mmMP0_SMN_C2PMSG_64);
preadl(mmMP0_SMN_C2PMSG_81);
return 0;
}
And here is the non working kernel code:
static int reset_amdgpu_vega(struct pci_dev *dev, int probe)
{
#define MP0_BASE 0x16000
#define mmMP0_SMN_C2PMSG_33 ((MP0_BASE + 0x61) * 4)
#define mmMP0_SMN_C2PMSG_64 ((MP0_BASE + 0x80) * 4)
#define mmMP0_SMN_C2PMSG_81 ((MP0_BASE + 0x91) * 4)
resource_size_t mmio_base, mmio_size;
void __iomem *mmio;
int ret;
int i;
uint32_t val;
if (probe)
return 0;
mmio_base = pci_resource_start(dev, 5);
mmio_size = pci_resource_len(dev, 5);
mmio = pci_iomap(dev, 5, 0);
if (mmio == NULL) {
printk(KERN_ERR
"[reset_amdgpu_vega] failed to map the resource\n");
ret = -ENOMEM;
goto out;
}
printk(KERN_INFO "mmio: %llx %llx %lx\n",
mmio_base,
mmio_size,
(uintptr_t)mmio);
printk(KERN_INFO "regs: %08x %08x %08x\n",
mmMP0_SMN_C2PMSG_33,
mmMP0_SMN_C2PMSG_64,
mmMP0_SMN_C2PMSG_81);
printk(KERN_INFO "vals: %08x %08x %08x\n",
ioread32(mmio + mmMP0_SMN_C2PMSG_33),
ioread32(mmio + mmMP0_SMN_C2PMSG_64),
ioread32(mmio + mmMP0_SMN_C2PMSG_81));
pci_iounmap(dev, mmio);
ret = 0;
out:
return ret;
}
The problem was that before the pci reset quirk is called, the kernel turns off the PCI_COMMAND_MEMORY flag on the COMMAND register. By doing so the device is no longer readable. The solution is to re-enable the flag first:
pci_read_config_word(dev, PCI_COMMAND, &cfg);
cfg |= PCI_COMMAND_MEMORY;
pci_write_config_word(dev, PCI_COMMAND, cfg);

Map size and mmap Invalid argument error

I am trying to access physical addresses registers on my ARM (https://4donline.ihs.com/images/VipMasterIC/IC/ATML/ATML-S-A0001248554/ATML-S-A0001248554-1.pdf) with mmap but I don't know what length to put. For example, if I have a register at address 0xFFFFFCE8 in which I have access to 32 bit.What should I put in mmap size_t ?
Thank you for you help !
EDIT :Here and here we can see they put 4096, and on the first one it is a SAM9 almost the same as mine.So, why did they put 4096 ?Maybe because if I do :
#include <unistd.h>
long sz = sysconf(_SC_PAGESIZE);
printf("%ld",sz);
Th answer is 4096...
EDIT 2 :Based on this post I could write this :
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define PIOD_START_ADDR 0xFFFFFA00
#define PIOD_STOP_ADDR 0xFFFFFC00
#define PIOD_SIZE (PIOD_STOP_ADDR-PIOD_START_ADDR)
#define PIO_WPMR_OFFSET 0xE4 // PIO Write Protection Mode Register Bank D
#define PIO_PUER_OFFSET 0x64 // PIO Pull-Up Enable Register Bank D
#define PIO_PUSR_OFFSET 0x68 // PIO Pull-Up Status Register Bank D
#define LED7_ON 0xFFDFFFFF // LED7 Mask ON
#define LED7_OFF 0xFFFFFFFF // LED7 Mask OFF
#define DESABLE_WRITE_PROTECTION_BANK_D 0x50494F00 // Desable write protection
int main(void) {
volatile void *gpio_D_addr;
volatile unsigned int *gpio_pullup_enable_addr;
volatile unsigned int *gpio_pullup_status_addr;
volatile unsigned int *gpio_enable_write_addr;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0){
fprintf(stderr, "Unable to open port\n\r");
exit(fd);
}
gpio_D_addr = mmap(0, PIOD_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PIOD_START_ADDR);
gpio_addr = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDR);
if(gpio_D_addr == MAP_FAILED){
handle_error("mmap");
}
gpio_enable_write_addr = gpio_D_addr + PIO_WPMR_OFFSET;
gpio_pullup_enable_addr = gpio_D_addr + PIO_PUER_OFFSET;
gpio_pullup_status_addr = gpio_D_addr + PIO_PUSR_OFFSET;
*gpio_enable_write_addr = DESABLE_WRITE_PROTECTION_BANK_D;
*gpio_pullup_enable_addr = *gpio_pullup_status_addr & LED7_ON;
return 0;
}
But I have a mmap: Invalid argument error.
--> But by changing the mmap like so (thank to this thread): mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, PIOD_START_ADDR & ~MAP_MASK); with :
#define MAP_SIZE 4096UL
#define MAP_MASK (MAP_SIZE - 1)
I no longer have the error but nothing happened...
Any idea ?
Read Chapter 5 of the datasheet you provided as a link. It describes the various memories and memory mapping for this device. The address you gave is 32-bit, but you need to make sure about its mapping. That's where the chart on p. 18 comes in -- along with becoming familiar with the entire 1200-page datasheet if you really want to program these SAM devices at a low level.
The address you gave also seems to be for the PMC (power management controller) memory space (according to the map), so I'd review that section, chapter 21.
Thank you to #vlk and his library in python I could make it work ! Here is a little example for toggling a LED :
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
#define _PIOD_BANK_D 0xA00
#define _PIO_OFFSET 0xFFFFF000
/* When executing this on the board :
long sz = sysconf(_SC_PAGESIZE);
printf("%ld\n\r",sz);
We have 4096.
*/
#define _MAP_SIZE 0x1000 // 4096
#define _WPMR_OFFSET 0x0E4 // PIO Write Protection Mode Register Bank D
#define _PIO_ENABLE 0x000
#define _PIO_DISABLE 0x004
#define _PIO_STATUS 0x008
#define _OUTPUT_ENABLE 0x010
#define _OUTPUT_DISABLE 0x014
#define _OUTPUT_STATUS 0x018
#define _FILTER_ENABLE 0x020
#define _FILTER_DISABLE 0x024
#define _FILTER_STATUS 0x028
#define _OUTPUT_DATA_SET 0x030
#define _OUTPUT_DATA_CLEAR 0x034
#define _OUTPUT_DATA_STATUS 0x038
#define _PIN_DATA_STATUS 0x03c
#define _MULTI_DRIVER_ENABLE 0x050
#define _MULTI_DRIVER_DISABLE 0x054
#define _MULTI_DRIVER_STATUS 0x058
#define _PULL_UP_DISABLE 0x060
#define _PULL_UP_ENABLE 0x064
#define _PULL_UP_STATUS 0x068
#define _PULL_DOWN_DISABLE 0x090
#define _PULL_DOWN_ENABLE 0x094
#define _PULL_DOWN_STATUS 0x098
#define _DISABLE_WRITE_PROTECTION 0x50494F00 // Desable write protection
#define LED_PIN 21
int main(void) {
volatile void *gpio_addr;
volatile unsigned int *gpio_enable_addr;
volatile unsigned int *gpio_output_mode_addr;
volatile unsigned int *gpio_output_set_addr;
volatile unsigned int *gpio_output_clear_addr;
volatile unsigned int *gpio_data_status_addr;
volatile unsigned int *gpio_write_protection_addr;
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd < 0){
fprintf(stderr, "Unable to open port\n\r");
exit(fd);
}
gpio_addr = mmap(NULL, _MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PIO_OFFSET);
if(gpio_addr == MAP_FAILED){
handle_error("mmap");
}
gpio_write_protection_addr = gpio_addr + _PIOD_BANK_D + _WPMR_OFFSET;
gpio_enable_addr = gpio_addr + _PIOD_BANK_D + _PIO_ENABLE;
gpio_output_mode_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_ENABLE;
gpio_output_set_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_SET;
gpio_output_clear_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_CLEAR;
gpio_data_status_addr = gpio_addr + _PIOD_BANK_D + _OUTPUT_DATA_STATUS;
*gpio_write_protection_addr = _DISABLE_WRITE_PROTECTION;
*gpio_enable_addr = 1 << LED_PIN;
*gpio_output_mode_addr = 1 << LED_PIN; // Output
// If LED
if((*gpio_data_status_addr & (1<<LED_PIN)) > 0){
*gpio_output_clear_addr = 1 << LED_PIN;
}else{
*gpio_output_set_addr = 1 << LED_PIN;
}
return 0;
}
I had to put _PIO_OFFSET to 0xFFFFF000, and add to the address the value of the D Bank (0xA00), instead of 0xFFFFFA00 because it resulted with mmap: Invalid argument. Don't know why..
EDIT :
Found the solution with the mmap example :
#define _PIO_OFFSET 0xFFFFFA00 // Instead of 0xFFFFF000
#define _MAP_SIZE 0x1000 // 4096
#define _MAP_MASK (_MAP_SIZE - 1)
#define _PA_OFFSET _PIO_OFFSET & ~_MAP_MASK
And the mmap :
gpio_addr = mmap(NULL, _MAP_SIZE + _PIO_OFFSET - _PA_OFFSET, PROT_READ | PROT_WRITE, MAP_SHARED, fd, _PA_OFFSET);
And for the assignation :
gpio_enable_addr = gpio_addr + _PIO_OFFSET - (_PA_OFFSET) + _PIO_ENABLE;

Bare-metal Loader - Send .elf binary to other processor through shared memory and execute

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.

Resources