Skip to content

Commit 44ba826

Browse files
committed
[pcimem]: Add pcimem Utility
The pcimem application provides a simple method of reading and writing to memory registers on a PCI device. The code used is from https://github.com/billfarrow/pcimem Signed-off-by: Ashwin H [email protected]
1 parent 63c14d2 commit 44ba826

File tree

11 files changed

+737
-0
lines changed

11 files changed

+737
-0
lines changed

rules/pcimem.dep

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
SPATH := $($(SONIC_PCIMEM)_SRC_PATH)
2+
DEP_FILES := $(SONIC_COMMON_FILES_LIST) rules/pcimem.mk rules/pcimem.dep
3+
DEP_FILES += $(SONIC_COMMON_BASE_FILES_LIST)
4+
DEP_FILES += $(shell git ls-files $(SPATH))
5+
6+
$(SONIC_PCIMEM)_CACHE_MODE := GIT_CONTENT_SHA
7+
$(SONIC_PCIMEM)_DEP_FLAGS := $(SONIC_COMMON_FLAGS_LIST)
8+
$(SONIC_PCIMEM)_DEP_FILES := $(DEP_FILES)
9+

rules/pcimem.mk

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# pcimem package
2+
3+
SONIC_PCIMEM_VERSION = 1.0.0
4+
SONIC_PCIMEM_PKG_NAME = pcimem
5+
6+
SONIC_PCIMEM = $(SONIC_PCIMEM_PKG_NAME)_$(SONIC_PCIMEM_VERSION)_$(CONFIGURED_ARCH).deb
7+
$(SONIC_PCIMEM)_SRC_PATH = $(SRC_PATH)/$(SONIC_PCIMEM_PKG_NAME)
8+
SONIC_DPKG_DEBS += $(SONIC_PCIMEM)

src/pcimem/Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
RM := rm -rf
2+
PCIMEM_TARGET := pcimem
3+
CP := cp
4+
MKDIR := mkdir
5+
CC := gcc
6+
MV := mv
7+
8+
-include src/subdir.mk
9+
10+
ifneq ($(MAKECMDGOALS),clean)
11+
ifneq ($(strip $(C_DEPS)),)
12+
-include $(C_DEPS)
13+
endif
14+
endif
15+
16+
all: pcimem-build
17+
18+
pcimem-build: $(OBJS)
19+
$(CC) -o "$(PCIMEM_TARGET)" $(OBJS)
20+
21+
install:
22+
$(MKDIR) -p $(DESTDIR)/usr/sbin
23+
$(MV) $(PCIMEM_TARGET) $(DESTDIR)/usr/sbin
24+
25+
deinstall:
26+
$(RM) $(DESTDIR)/usr/sbin/$(PCIMEM_TARGET)
27+
$(RM) -rf $(DESTDIR)/usr/sbin
28+
29+
clean:
30+
-$(RM) $(EXECUTABLES)$(OBJS)$(C_DEPS) $(PCIMEM_TARGET)
31+
32+
.PHONY: all clean dependents

src/pcimem/debian/changelog

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pcimem (1.0.0) stable; urgency=medium
2+
3+
* Initial release.
4+
5+
-- Ashwin H <[email protected]> Tue, 20 Sep 2022 13:55:00 +0530
6+

src/pcimem/debian/compat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
10

src/pcimem/debian/control

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Source: pcimem
2+
Section: devel
3+
Priority: optional
4+
Maintainer: Ashwin H <[email protected]>
5+
Build-Depends: debhelper (>= 8.0.0),
6+
Standards-Version: 3.9.3
7+
Homepage: https://github.com/sonic-net/sonic-buildimage
8+
XS-Go-Import-Path: github.com/sonic-net/sonic-buildimage
9+
10+
Package: pcimem
11+
Architecture: any
12+
Built-Using: ${misc:Built-Using}
13+
description: program to read/write from/to a pci device.

src/pcimem/debian/rules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/make -f
2+
%:
3+
dh $@

src/pcimem/src/LICENSE.txt

Lines changed: 339 additions & 0 deletions
Large diffs are not rendered by default.

src/pcimem/src/README

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
== Overview ==
2+
3+
The pcimem application provides a simple method of reading and writing
4+
to memory registers on a PCI card.
5+
6+
Usage: ./pcimem { sys file } { offset } [ type [ data ] ]
7+
sys file: sysfs file for the pci resource to act on
8+
offset : offset into pci memory region to act upon
9+
type : access operation type : [b]yte, [h]alfword, [w]ord, [d]ouble-word
10+
data : data to be written
11+
12+
== Platform Support ==
13+
14+
WARNING !! This method is platform dependent and may not work on your
15+
particular target architecture. Refer to the PowerPC section below.
16+
17+
== Example ==
18+
19+
bash# ./pcimem /sys/devices/pci0001\:00/0001\:00\:07.0/resource0 0 w
20+
/sys/devices/pci0001:00/0001:00:07.0/resource0 opened.
21+
Target offset is 0x0, page size is 4096
22+
mmap(0, 4096, 0x3, 0x1, 3, 0x0)
23+
PCI Memory mapped to address 0x4801f000.
24+
Value at offset 0x0 (0x4801f000): 0xC0BE0100
25+
26+
27+
== Why do this at all ? ==
28+
29+
When I start working on a new PCI device driver I generally go through a
30+
discovery phase of reading and writing to certain registers on the PCI card.
31+
Over the years I have written lots of small kernel modules to probe addresses
32+
within the PCI memory space, constantly iterating: modify code, recompile, scp
33+
to target, load module, unload module, dmesg.
34+
35+
Urk! There has to be a better way - sysfs and mmap() to the rescue.
36+
37+
38+
== Sysfs ==
39+
40+
Let's start at with the PCI files under sysfs:
41+
42+
bash# ls -l /sys/devices/pci0001\:00/0001\:00\:07.0/
43+
total 0
44+
-rw-r--r-- 1 root root     4096 Jul  2 20:13 broken_parity_status
45+
lrwxrwxrwx 1 root root        0 Jul  2 20:13 bus -> ../../../bus/pci
46+
-r--r--r-- 1 root root     4096 Jul  2 20:13 class
47+
-rw-r--r-- 1 root root      256 Jul  2 20:13 config
48+
-r--r--r-- 1 root root     4096 Jul  2 20:13 device
49+
-r--r--r-- 1 root root     4096 Jul  2 20:13 devspec
50+
-rw------- 1 root root     4096 Jul  2 20:13 enable
51+
-r--r--r-- 1 root root     4096 Jul  2 20:13 irq
52+
-r--r--r-- 1 root root     4096 Jul  2 20:13 local_cpus
53+
-r--r--r-- 1 root root     4096 Jul  2 20:13 modalias
54+
-rw-r--r-- 1 root root     4096 Jul  2 20:13 msi_bus
55+
-r--r--r-- 1 root root     4096 Jul  2 20:13 resource
56+
-rw------- 1 root root     4096 Jul  2 20:13 resource0
57+
-rw------- 1 root root    65536 Jul  2 20:13 resource1
58+
-rw------- 1 root root 16777216 Jul  2 20:13 resource2
59+
lrwxrwxrwx 1 root root        0 Jul  2 20:13 subsystem -> ../../../bus/pci
60+
-r--r--r-- 1 root root     4096 Jul  2 20:13 subsystem_device
61+
-r--r--r-- 1 root root     4096 Jul  2 20:13 subsystem_vendor
62+
-rw-r--r-- 1 root root     4096 Jul  2 20:13 uevent
63+
-r--r--r-- 1 root root     4096 Jul  2 20:13 vendor
64+
65+
The vendor and device files report the PCI vendor ID and device ID:
66+
67+
bash# cat device
68+
0x0001
69+
70+
This info is also available from lspci
71+
72+
bash# lspci -v
73+
0001:00:07.0 Class 0680: Unknown device bec0:0001 (rev 01)
74+
    Flags: bus master, 66MHz, medium devsel, latency 128, IRQ 31
75+
    Memory at 8d010000 (32-bit, non-prefetchable) [size=4K]
76+
    Memory at 8d000000 (32-bit, non-prefetchable) [size=64K]
77+
    Memory at 8c000000 (32-bit, non-prefetchable) [size=16M]
78+
79+
This PCI card makes 3 seperate regions of memory available to the host
80+
computer. The sysfs resource0 file corresponds to the first memory region. The
81+
PCI card lets the host computer know about these memory regions using the BAR
82+
registers in the PCI config.
83+
84+
== mmap() ==
85+
86+
These sysfs resource can be used with mmap() to map the PCI memory into a
87+
userspace applications memory space.  The application then has a pointer to the
88+
start of the PCI memory region and can read and write values directly. (There
89+
is a bit more going on here with respect to memory pointers, but that is all
90+
taken care of by the kernel).
91+
92+
fd = open("/sys/devices/pci0001\:00/0001\:00\:07.0/resource0", O_RDWR | O_SYNC);
93+
ptr = mmap(0, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
94+
printf("PCI BAR0 0x0000 = 0x%4x\n",  *((unsigned short *) ptr);
95+
96+
== PowerPC ==
97+
98+
To make this work on a PowerPC architecture you also need to make a small
99+
change to the pci core. My example is from kernel 2.6.34, and hopefully this
100+
will be fixed for us in a later kernel version.
101+
102+
bash# vi arch/powerpc/kernel/pci-common.c
103+
    /* If memory, add on the PCI bridge address offset */
104+
     if (mmap_state == pci_mmap_mem) {
105+
-#if 0 /* See comment in pci_resource_to_user() for why this is disabled */
106+
+#if 1 /* See comment in pci_resource_to_user() for why this is disabled */
107+
         *offset += hose->pci_mem_offset;
108+
 #endif
109+
         res_bit = IORESOURCE_MEM;
110+
 
111+
         /* We pass a fully fixed up address to userland for MMIO instead of
112+
         * a BAR value because X is lame and expects to be able to use that
113+
         * to pass to /dev/mem !
114+
         *
115+
         * That means that we'll have potentially 64 bits values where some
116+
         * userland apps only expect 32 (like X itself since it thinks only
117+
         * Sparc has 64 bits MMIO) but if we don't do that, we break it on
118+
         * 32 bits CHRPs :-(
119+
         *
120+
         * Hopefully, the sysfs insterface is immune to that gunk. Once X
121+
         * has been fixed (and the fix spread enough), we can re-enable the
122+
         * 2 lines below and pass down a BAR value to userland. In that case
123+
         * we'll also have to re-enable the matching code in
124+
         * __pci_mmap_make_offset().
125+
         *
126+
         * BenH.
127+
         */
128+
-#if 0
129+
+#if 1
130+
        else if (rsrc->flags &amp; IORESOURCE_MEM)
131+
                offset = hose->pci_mem_offset;
132+
#endif
133+

src/pcimem/src/pcimem.c

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* pcimem.c: Simple program to read/write from/to a pci device from userspace.
3+
*
4+
* Copyright (C) 2010, Bill Farrow ([email protected])
5+
*
6+
* Based on the devmem2.c code
7+
* Copyright (C) 2000, Jan-Derk Bakker ([email protected])
8+
*
9+
* This program is free software; you can redistribute it and/or modify
10+
* it under the terms of the GNU General Public License as published by
11+
* the Free Software Foundation; either version 2 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU General Public License
20+
* along with this program; if not, write to the Free Software
21+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22+
*
23+
*/
24+
25+
#include <stdio.h>
26+
#include <stdlib.h>
27+
#include <stdint.h>
28+
#include <unistd.h>
29+
#include <string.h>
30+
#include <errno.h>
31+
#include <signal.h>
32+
#include <fcntl.h>
33+
#include <ctype.h>
34+
#include <termios.h>
35+
#include <sys/types.h>
36+
#include <sys/mman.h>
37+
38+
#define PRINT_ERROR \
39+
do { \
40+
fprintf(stderr, "Error at line %d, file %s (%d) [%s]\n", \
41+
__LINE__, __FILE__, errno, strerror(errno)); exit(1); \
42+
} while(0)
43+
44+
45+
int main(int argc, char **argv) {
46+
int fd;
47+
void *map_base, *virt_addr;
48+
uint64_t read_result, writeval, prev_read_result = 0;
49+
char *filename;
50+
off_t target, target_base;
51+
int access_type = 'w';
52+
int items_count = 1;
53+
int verbose = 0;
54+
int read_result_dupped = 0;
55+
int type_width;
56+
int i;
57+
int map_size = 4096UL;
58+
59+
if(argc < 3) {
60+
// pcimem /sys/bus/pci/devices/0001\:00\:07.0/resource0 0x100 w 0x00
61+
// argv[0] [1] [2] [3] [4]
62+
fprintf(stderr, "\nUsage:\t%s { sysfile } { offset } [ type*count [ data ] ]\n"
63+
"\tsys file: sysfs file for the pci resource to act on\n"
64+
"\toffset : offset into pci memory region to act upon\n"
65+
"\ttype : access operation type : [b]yte, [h]alfword, [w]ord, [d]ouble-word\n"
66+
"\t*count : number of items to read: w*100 will dump 100 words\n"
67+
"\tdata : data to be written\n\n",
68+
argv[0]);
69+
exit(1);
70+
}
71+
filename = argv[1];
72+
target = strtoul(argv[2], 0, 0);
73+
74+
if(argc > 3) {
75+
access_type = tolower(argv[3][0]);
76+
if (argv[3][1] == '*')
77+
items_count = strtoul(argv[3]+2, 0, 0);
78+
}
79+
80+
switch(access_type) {
81+
case 'b':
82+
type_width = 1;
83+
break;
84+
case 'h':
85+
type_width = 2;
86+
break;
87+
case 'w':
88+
type_width = 4;
89+
break;
90+
case 'd':
91+
type_width = 8;
92+
break;
93+
default:
94+
fprintf(stderr, "Illegal data type '%c'.\n", access_type);
95+
exit(2);
96+
}
97+
98+
if((fd = open(filename, O_RDWR | O_SYNC)) == -1) PRINT_ERROR;
99+
printf("%s opened.\n", filename);
100+
printf("Target offset is 0x%x, page size is %ld\n", (int) target, sysconf(_SC_PAGE_SIZE));
101+
fflush(stdout);
102+
103+
target_base = target & ~(sysconf(_SC_PAGE_SIZE)-1);
104+
if (target + items_count*type_width - target_base > map_size)
105+
map_size = target + items_count*type_width - target_base;
106+
107+
/* Map one page */
108+
printf("mmap(%d, %d, 0x%x, 0x%x, %d, 0x%x)\n", 0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (int) target);
109+
110+
map_base = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, target_base);
111+
if(map_base == (void *) -1) PRINT_ERROR;
112+
printf("PCI Memory mapped to address 0x%08lx.\n", (unsigned long) map_base);
113+
fflush(stdout);
114+
115+
for (i = 0; i < items_count; i++) {
116+
117+
virt_addr = map_base + target + i*type_width - target_base;
118+
switch(access_type) {
119+
case 'b':
120+
read_result = *((uint8_t *) virt_addr);
121+
break;
122+
case 'h':
123+
read_result = *((uint16_t *) virt_addr);
124+
break;
125+
case 'w':
126+
read_result = *((uint32_t *) virt_addr);
127+
break;
128+
case 'd':
129+
read_result = *((uint64_t *) virt_addr);
130+
break;
131+
}
132+
133+
if (verbose)
134+
printf("Value at offset 0x%X (%p): 0x%0*lX\n", (int) target + i*type_width, virt_addr, type_width*2, read_result);
135+
else {
136+
if (read_result != prev_read_result || i == 0) {
137+
printf("0x%04X: 0x%0*lX\n", (int)(target + i*type_width), type_width*2, read_result);
138+
read_result_dupped = 0;
139+
} else {
140+
if (!read_result_dupped)
141+
printf("...\n");
142+
read_result_dupped = 1;
143+
}
144+
}
145+
146+
prev_read_result = read_result;
147+
148+
}
149+
150+
fflush(stdout);
151+
152+
if(argc > 4) {
153+
writeval = strtoull(argv[4], NULL, 0);
154+
switch(access_type) {
155+
case 'b':
156+
*((uint8_t *) virt_addr) = writeval;
157+
read_result = *((uint8_t *) virt_addr);
158+
break;
159+
case 'h':
160+
*((uint16_t *) virt_addr) = writeval;
161+
read_result = *((uint16_t *) virt_addr);
162+
break;
163+
case 'w':
164+
*((uint32_t *) virt_addr) = writeval;
165+
read_result = *((uint32_t *) virt_addr);
166+
break;
167+
case 'd':
168+
*((uint64_t *) virt_addr) = writeval;
169+
read_result = *((uint64_t *) virt_addr);
170+
break;
171+
}
172+
printf("Written 0x%0*lX; readback 0x%*lX\n", type_width,
173+
writeval, type_width, read_result);
174+
fflush(stdout);
175+
}
176+
177+
if(munmap(map_base, map_size) == -1) PRINT_ERROR;
178+
close(fd);
179+
return 0;
180+
}

0 commit comments

Comments
 (0)