1 /* devmem.c - Access physical addresses
2 *
3 * Copyright 2019 The Android Open Source Project
4
5 USE_DEVMEM(NEWTOY(devmem, "<1(no-sync)(no-mmap)f:", TOYFLAG_USR|TOYFLAG_SBIN))
6
7 config DEVMEM
8 bool "devmem"
9 default y
10 help
11 usage: devmem [-f FILE] ADDR [WIDTH [DATA...]]
12
13 Read/write physical addresses. WIDTH is 1, 2, 4, or 8 bytes (default 4).
14 Prefix ADDR with 0x for hexadecimal, output is in same base as address.
15
16 -f FILE File to operate on (default /dev/mem)
17 --no-sync Don't open the file with O_SYNC (for cached access)
18 --no-mmap Don't mmap the file
19 */
20
21 #define FOR_devmem
22 #include "toys.h"
23
GLOBALS(char * f;)24 GLOBALS(
25 char *f;
26 )
27
28 unsigned long xatolu(char *str, int bytes)
29 {
30 char *end = str;
31 unsigned long lu;
32
33 errno = 0;
34 lu = strtoul(str, &end, 0);
35 // Report out of range values as errors rather than truncating.
36 if (errno == ERANGE || lu > (~0UL)>>(sizeof(long)-bytes)*8)
37 error_exit("%s>%d bytes", str, bytes);
38 if (*end || errno) perror_exit("bad %s", str);
39
40 return lu;
41 }
42
devmem_main(void)43 void devmem_main(void)
44 {
45 int ii, writing = toys.optc > 2, bytes = 4, fd;
46 unsigned long data QUIET, map_len QUIET,
47 addr = xatolu(*toys.optargs, sizeof(long));
48 void *map QUIET, *p QUIET;
49
50 // WIDTH?
51 if (toys.optc>1) {
52 char *sizes = sizeof(long)==8 ? "1248" : "124";
53
54 if ((ii = stridx(sizes, *toys.optargs[1]))==-1 || toys.optargs[1][1])
55 error_exit("bad width: %s", toys.optargs[1]);
56 bytes = 1<<ii;
57 }
58
59 // Map in just enough.
60 if (CFG_TOYBOX_FORK) {
61 fd = xopen(TT.f ? : "/dev/mem", O_RDWR*writing+O_SYNC*!FLAG(no_sync));
62 if (FLAG(no_mmap)) xlseek(fd, addr, SEEK_SET);
63 else {
64 unsigned long long page_size = sysconf(_SC_PAGESIZE)-1, map_off;
65
66 map_off = addr & ~page_size;
67 map_len = addr + (writing ? (toys.optc - 2) * bytes : bytes) - map_off;
68 map = xmmap(0, map_len, writing ? PROT_WRITE : PROT_READ, MAP_SHARED, fd,
69 map_off);
70 p = map+(addr&page_size);
71 close(fd);
72 }
73 } else p = (void *)addr;
74
75 // Not using peek()/poke() because registers care about size of read/write.
76 if (writing) {
77 for (ii = 2; ii<toys.optc; ii++) {
78 data = xatolu(toys.optargs[ii], bytes);
79 if (FLAG(no_mmap)) xwrite(fd, &data, bytes);
80 else {
81 if (bytes==1) *(char *)p = data;
82 else if (bytes==2) *(unsigned short *)p = data;
83 else if (bytes==4) *(unsigned int *)p = data;
84 else if (sizeof(long)==8 && bytes==8) *(unsigned long *)p = data;
85 p += bytes;
86 }
87 }
88 } else {
89 if (FLAG(no_mmap)) xread(fd, &data, bytes);
90 else {
91 if (bytes==1) data = *(char *)p;
92 else if (bytes==2) data = *(unsigned short *)p;
93 else if (bytes==4) data = *(unsigned int *)p;
94 else if (sizeof(long)==8 && bytes==8) data = *(unsigned long *)p;
95 }
96 printf((!strchr(*toys.optargs, 'x')) ? "%0*ld\n" : "0x%0*lx\n",
97 bytes*2, data);
98 }
99
100 if (CFG_TOYBOX_FORK) {
101 if (FLAG(no_mmap)) close(fd);
102 else munmap(map, map_len);
103 }
104 }
105