-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdevmem.c
250 lines (230 loc) · 9.22 KB
/
devmem.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
//=============================================================================
// devmem
// read/write to hardware registers on Onion Omega2
//=============================================================================
#include <stdio.h>
#include <sys/mman.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h> //close
#include <stdlib.h>
#include <stdbool.h>
#include <libgen.h> //basename
//=============================================================================
// defines
//=============================================================================
#define MAX_ARGS 255 //limit # of args to program
#define BASE_ADDR 0x10000000 //Omega2 base register address
//=============================================================================
// command buffer struct and global buffer
//=============================================================================
typedef struct {
char cmd;
uint32_t addr;
uint32_t val;
} cmd_buffer_t;
cmd_buffer_t g_cmdbuffer[MAX_ARGS+1]; //store all validated commands/values
//=============================================================================
// show usage
//=============================================================================
static void usage(const char* n){
printf(
"\n"
" %s r <addr> :read address, return decimal\n"
" %s h <addr> :read address, return hex\n"
" %s b <addr> :read address, return binary\n"
" %s v <addr> :read address, verbose output\n"
" %s w <addr> <val> :write address value\n"
" %s s <addr> <bitmask> :set bitmask bit(s) at address\n"
" %s c <addr> <bitmask> :clear bitmask bit(s) at address\n"
"\n"
" <addr> absolute address or offset address from 0x10000000\n"
" address will be automatically word aligned\n"
" <val> 32bit value\n"
" <bitmask> 32bit bitmask value (1=set or clr specified bit)\n"
"\n"
" numbers can be in any format-\n"
" decimal, hex (0x_), binary (0b_), octal (0_)\n"
" can provide more than one command at a time, up to a limit\n"
" of 255 arguments\n"
"\n",
n,n,n,n,n,n,n
);
exit(EXIT_FAILURE);
}
//=============================================================================
// print error message, then exit
//=============================================================================
static void error_exit(const char* msg)
{
if(msg) fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
//=============================================================================
// value conversion argument error
//=============================================================================
static void arg_error_exit(int argn, const char* str)
{
fprintf(stderr, "argument# %d -> %s\n", argn, str);
error_exit("invalid command or value");
}
//=============================================================================
// map physical address to virtual memory, 1 page will be mapped
// error/exit on any failures
// return pointer to virtual address where word is located
//=============================================================================
static uint32_t* regmap(uint32_t addr)
{
static uint32_t m_page; //current physical page mapped
static void* m_vmem; //current mmap pointer
static uint32_t m_pagesize; //system page size
//get system page size (most likely 4096), get/store once
if(m_pagesize == 0) m_pagesize = sysconf(_SC_PAGE_SIZE);
//if same page already mapped, no need to map again
if(m_vmem && (addr / m_pagesize * m_pagesize) == m_page) goto ret;
//if previously mapped and unmap fails (!0), error
if(m_vmem && munmap(m_vmem, m_pagesize)){
error_exit("failed to unmap register memory");
}
int fd = open("/dev/mem", O_RDWR);
if(fd < 0) error_exit("failed to open /dev/mem");
m_page = addr / m_pagesize * m_pagesize;
m_vmem = mmap(
NULL, //mmap determines virtual address
m_pagesize, //length (just get a whole page)
PROT_READ | PROT_WRITE, //match file open
MAP_FILE | MAP_SHARED,
fd, //file
m_page //offset into fd (page aligned)
);
close(fd); //fd no longer needed
if(m_vmem == MAP_FAILED) error_exit("failed to mmap register memory");
ret:
return &((uint32_t*)m_vmem)[(addr-m_page)/4];
}
//=============================================================================
// convert command line value argument to unsigned integer
// exit on any failure
//=============================================================================
int strto_u32(const char* str, int argn)
{
if(*str == '-') arg_error_exit(argn, str); //disallow negative numbers
char *endptr = NULL;
const char *s = str;
int base = 0; //auto for decimal, hex, octal
if(s[0] == '0' && s[1] == 'b'){ //check for binary
base = 2; //is binary
s += 2; //skip over "0b"
}
uint32_t val = strtoul(s, &endptr, base);
if(*endptr) arg_error_exit(argn, str); //failed if *endptr not 0
return val; //conversion ok
}
//=============================================================================
// validate everything on command line before doing anything
// (either all good, or all fails, no partital writes)
// store everything in g_cmdbuffer so main loop only has to go through already
// validated/converted buffer
// g_cmdbuffer[] = { cmd, address, value, ..., 0, 0, 0 }
//=============================================================================
bool validate_command_line(int argc, char** argv)
{
cmd_buffer_t* cb = &g_cmdbuffer[0];
for(int i = 0; i < argc; cb++){
cb->cmd = argv[i][0];
//only single letter command allowed
if(argv[i++][1]) arg_error_exit(i, argv[i-1]);
if(i >= argc) error_exit("incomplete command");
cb->addr = strto_u32(argv[i], i+1);
//get addr into 0x1xxxxxxx and word aligned
cb->addr &= (BASE_ADDR-1); // 0x0.......
cb->addr |= BASE_ADDR; // 0x1.......
cb->addr &= ~3; // 0x.......n (n = 0b..00)
i++;
switch(cb->cmd){
case 'r': case 'h': case 'b': case 'v':
break; //already got address, nothing to do
case 'w': case 's': case 'c':
if(i >= argc) error_exit("incomplete command");
cb->val = strto_u32(argv[i], i+1);
i++;
break;
default:
arg_error_exit(i, argv[i-1]);
break;
}
}
}
//=============================================================================
// read register 'v' verbose, 'h' hex, 'r' decimal, 'b' binary
//=============================================================================
void regread(char opt, uint32_t addr, uint32_t val)
{
uint8_t b0 = val;
uint8_t b1 = val>>8;
uint8_t b2 = val>>16;
uint8_t b3 = val>>24;
if(opt == 'v'){
printf(
"\n"
"addr: [0x%08X] val: [0x%08X]\n"
"\n"
"bit 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1\n"
"pos 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0\n"
" ............... ............... ............... ...............\n"
"bin ",
addr, val);
for(int i = 0; i < 32; i++, val<<=1){
printf("%s", val & (1<<31) ? "1 " : "0 " );
if((i & 7) == 7) printf(" ");
}
printf(
"\n"
"hex %11s0x%02X %11s0x%02X %11s0x%02X %11s0x%02X\n"
"dec %15d %15d %15d %15d\n\n",
" ", b3, " ", b2, " ", b1, " ", b0, b3, b2, b1, b0);
} else if(opt == 'h'){
printf("0x%08X\n", val);
} else if(opt == 'b'){
printf("0b ");
for(int i = 0; i < 32; i++, val<<=1){
printf("%s", val & (1<<31) ? "1" : "0");
if((i & 7) == 7) printf(" ");
}
printf("\n");
} else {
printf("%d\n", val);
}
}
//=============================================================================
// MAIN
//=============================================================================
int main(int argc, char** argv)
{
//limit args to a reasonable number
if(argc > MAX_ARGS) error_exit("argument count exceeded");
//check argc, if no args show usage
//let basename modify argv[0]- is ok
if(argc < 2) usage(basename(argv[0]));
//validate (starting at first arg)
validate_command_line(argc-1, &argv[1]);
//iterate over validated command line buffer
for(cmd_buffer_t* cb = &g_cmdbuffer[0]; cb->cmd; cb++){
uint32_t* p32 = regmap(cb->addr);
switch(cb->cmd){
case 'r':
case 'h':
case 'v':
case 'b':
regread( cb->cmd, cb->addr, *p32 );
break;
case 'w': *p32 = cb->val;
break;
case 's': *p32 |= cb->val;
break;
case 'c': *p32 &= ~cb->val;
break;
}
}
exit(EXIT_SUCCESS);
}