1 | /* |
2 | Copyright (c) 2013, Broadcom Europe Ltd |
3 | All rights reserved. |
4 | |
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the following conditions are met: |
7 | * Redistributions of source code must retain the above copyright |
8 | notice, this list of conditions and the following disclaimer. |
9 | * Redistributions in binary form must reproduce the above copyright |
10 | notice, this list of conditions and the following disclaimer in the |
11 | documentation and/or other materials provided with the distribution. |
12 | * Neither the name of the copyright holder nor the |
13 | names of its contributors may be used to endorse or promote products |
14 | derived from this software without specific prior written permission. |
15 | |
16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY |
20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | /** |
29 | * |
30 | * @file debug_sym.c |
31 | * |
32 | * @brief The usermode process which implements displays the messages. |
33 | * |
34 | ****************************************************************************/ |
35 | |
36 | // ---- Include Files ------------------------------------------------------- |
37 | |
38 | #include "interface/vcos/vcos.h" |
39 | |
40 | #include <errno.h> |
41 | #include <stdlib.h> |
42 | #include <stdio.h> |
43 | #include <fcntl.h> |
44 | #include <string.h> |
45 | #if defined(WIN32) |
46 | # include <io.h> |
47 | #elif defined(__CYGWIN__) |
48 | # include <sys/mman.h> |
49 | #else |
50 | # include <sys/mman.h> |
51 | # include <sys/ioctl.h> |
52 | # include <vc_mem.h> |
53 | #endif |
54 | |
55 | #include "debug_sym.h" |
56 | #include "vc_debug_sym.h" |
57 | |
58 | |
59 | // ---- Public Variables ---------------------------------------------------- |
60 | // ---- Private Constants and Types ----------------------------------------- |
61 | |
62 | #define MAX_VC_SIZE 8 /* a sanity upper bound on memory size */ |
63 | |
64 | #ifndef PAGE_SIZE |
65 | # if defined(__CYGWIN__) |
66 | # define PAGE_SIZE 65536 /* Cygwin mmap requires rounding to SYSTEM_INFO.dwAllocationGranularity */ |
67 | # else |
68 | # define PAGE_SIZE 4096 |
69 | # endif |
70 | #endif |
71 | #ifndef PAGE_MASK |
72 | # define PAGE_MASK (~(PAGE_SIZE - 1)) |
73 | #endif |
74 | |
75 | // Offset within the videocore memory map to get the address of the symbol |
76 | // table. |
77 | #define VC_SYMBOL_BASE_OFFSET VC_DEBUG_HEADER_OFFSET |
78 | |
79 | struct opaque_vc_mem_access_handle_t |
80 | { |
81 | int memFd; |
82 | int memFdBase; /* The VideoCore address mapped to offset 0 of memFd */ |
83 | VC_MEM_ADDR_T vcMemBase; /* The VideoCore address of the start of the loaded image */ |
84 | VC_MEM_ADDR_T vcMemLoad; /* The VideoCore address of the start of the code image */ |
85 | VC_MEM_ADDR_T vcMemEnd; /* The VideoCore address of the end of the loaded image */ |
86 | VC_MEM_SIZE_T vcMemSize; /* The amount of memory used by the loaded image */ |
87 | VC_MEM_ADDR_T vcMemPhys; /* The VideoCore memory physical address */ |
88 | |
89 | VC_MEM_ADDR_T vcSymbolTableOffset; |
90 | unsigned numSymbols; |
91 | VC_DEBUG_SYMBOL_T *symbol; |
92 | int use_vc_mem; /* using mmap-ed memory rather than real file */ |
93 | }; |
94 | |
95 | #if 1 |
96 | #define DBG( fmt, ... ) vcos_log_trace( "%s: " fmt, __FUNCTION__, ##__VA_ARGS__ ) |
97 | #define ERR( fmt, ... ) vcos_log_error( "%s: " fmt, __FUNCTION__, ##__VA_ARGS__ ) |
98 | #else |
99 | #define DBG( fmt, ... ) fprintf( stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__ ) |
100 | #define ERR( fmt, ... ) fprintf( stderr, "%s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__ ) |
101 | #endif |
102 | |
103 | typedef enum |
104 | { |
105 | READ_MEM, |
106 | WRITE_MEM, |
107 | } MEM_OP_T; |
108 | |
109 | #if defined( WIN32 ) |
110 | #define open _open |
111 | #define close _close |
112 | #define O_SYNC 0 |
113 | #endif |
114 | |
115 | // ---- Private Variables --------------------------------------------------- |
116 | |
117 | #define VCOS_LOG_CATEGORY (&debug_sym_log_category) |
118 | static VCOS_LOG_CAT_T debug_sym_log_category; |
119 | |
120 | // ---- Private Function Prototypes ----------------------------------------- |
121 | |
122 | // ---- Functions ----------------------------------------------------------- |
123 | |
124 | /**************************************************************************** |
125 | * |
126 | * Get access to the videocore memory space. Returns zero if the memory was |
127 | * opened successfully. |
128 | * |
129 | ***************************************************************************/ |
130 | |
131 | int OpenVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T *vcHandlePtr ) |
132 | { |
133 | return OpenVideoCoreMemoryFile( NULL, vcHandlePtr ); |
134 | } |
135 | |
136 | /**************************************************************************** |
137 | * |
138 | * Get access to the videocore memory space. Returns zero if the memory was |
139 | * opened successfully. |
140 | * |
141 | ***************************************************************************/ |
142 | |
143 | struct fb_dmacopy { |
144 | void *dst; |
145 | uint32_t src; |
146 | uint32_t length; |
147 | }; |
148 | #define FBIODMACOPY _IOW('z', 0x22, struct fb_dmacopy) |
149 | |
150 | static int vc_mem_copy(void *dst, uint32_t src, uint32_t length) |
151 | { |
152 | const char *filename = "/dev/fb0" ; |
153 | int memFd; |
154 | struct fb_dmacopy ioparam; |
155 | |
156 | ioparam.dst = dst; |
157 | ioparam.src = src; |
158 | ioparam.length = length; |
159 | |
160 | if (( memFd = open( filename, O_RDWR | O_SYNC )) < 0 ) |
161 | { |
162 | ERR( "Unable to open '%s': %s(%d)\n" , filename, strerror( errno ), errno ); |
163 | return -errno; |
164 | } |
165 | |
166 | if ( ioctl( memFd, FBIODMACOPY, &ioparam ) != 0 ) |
167 | { |
168 | close( memFd ); |
169 | return -errno; |
170 | } |
171 | close( memFd ); |
172 | return 0; |
173 | } |
174 | |
175 | int OpenVideoCoreMemoryFile( const char *filename, VC_MEM_ACCESS_HANDLE_T *vcHandlePtr ) |
176 | { |
177 | return OpenVideoCoreMemoryFileWithOffset( filename, vcHandlePtr, 0 ); |
178 | } |
179 | |
180 | int OpenVideoCoreMemoryFileWithOffset( const char *filename, VC_MEM_ACCESS_HANDLE_T *vcHandlePtr, size_t loadOffset ) |
181 | { |
182 | int rc = 0; |
183 | VC_MEM_ACCESS_HANDLE_T newHandle; |
184 | VC_DEBUG_SYMBOL_T debug_sym; |
185 | VC_MEM_ADDR_T symAddr; |
186 | size_t symTableSize; |
187 | unsigned symIdx; |
188 | |
189 | struct |
190 | { |
191 | VC_DEBUG_HEADER_T ; |
192 | VC_DEBUG_PARAMS_T params; |
193 | |
194 | } vc_dbg; |
195 | |
196 | vcos_log_register( "debug_sym" , &debug_sym_log_category ); |
197 | |
198 | if (( newHandle = calloc( 1, sizeof( *newHandle ))) == NULL ) |
199 | { |
200 | return -ENOMEM; |
201 | } |
202 | |
203 | if ( filename == NULL ) |
204 | { |
205 | newHandle->use_vc_mem = 1; |
206 | filename = "/dev/vc-mem" ; |
207 | } |
208 | else |
209 | { |
210 | newHandle->use_vc_mem = 0; |
211 | } |
212 | |
213 | if (( newHandle->memFd = open( filename, ( newHandle->use_vc_mem ? O_RDWR : O_RDONLY ) | O_SYNC )) < 0 ) |
214 | { |
215 | ERR( "Unable to open '%s': %s(%d)\n" , filename, strerror( errno ), errno ); |
216 | free(newHandle); |
217 | return -errno; |
218 | } |
219 | DBG( "Opened %s memFd = %d" , filename, newHandle->memFd ); |
220 | |
221 | if ( newHandle->use_vc_mem ) |
222 | { |
223 | newHandle->memFdBase = 0; |
224 | |
225 | #if defined(WIN32) || defined(__CYGWIN__) |
226 | #define VC_MEM_SIZE (128 * 1024 * 1024) |
227 | newHandle->vcMemSize = VC_MEM_SIZE; |
228 | newHandle->vcMemBase = 0; |
229 | newHandle->vcMemLoad = 0; |
230 | newHandle->vcMemPhys = 0; |
231 | #else |
232 | if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_SIZE, &newHandle->vcMemSize ) != 0 ) |
233 | { |
234 | ERR( "Failed to get memory size via ioctl: %s(%d)\n" , |
235 | strerror( errno ), errno ); |
236 | free(newHandle); |
237 | return -errno; |
238 | } |
239 | if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_BASE, &newHandle->vcMemBase ) != 0 ) |
240 | { |
241 | ERR( "Failed to get memory base via ioctl: %s(%d)\n" , |
242 | strerror( errno ), errno ); |
243 | free(newHandle); |
244 | return -errno; |
245 | } |
246 | if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_LOAD, &newHandle->vcMemLoad ) != 0 ) |
247 | { |
248 | ERR( "Failed to get memory load via ioctl: %s(%d)\n" , |
249 | strerror( errno ), errno ); |
250 | /* Backward compatibility. */ |
251 | newHandle->vcMemLoad = newHandle->vcMemBase; |
252 | } |
253 | if ( ioctl( newHandle->memFd, VC_MEM_IOC_MEM_PHYS_ADDR, &newHandle->vcMemPhys ) != 0 ) |
254 | { |
255 | ERR( "Failed to get memory physical address via ioctl: %s(%d)\n" , |
256 | strerror( errno ), errno ); |
257 | free(newHandle); |
258 | return -errno; |
259 | } |
260 | #endif |
261 | } |
262 | else |
263 | { |
264 | off_t len = lseek( newHandle->memFd, 0, SEEK_END ); |
265 | if ( len < 0 ) |
266 | { |
267 | ERR( "Failed to seek to end of file: %s(%d)\n" , strerror( errno ), errno ); |
268 | free(newHandle); |
269 | return -errno; |
270 | } |
271 | newHandle->vcMemPhys = 0; |
272 | newHandle->vcMemSize = len; |
273 | newHandle->vcMemBase = 0; |
274 | newHandle->vcMemLoad = loadOffset; |
275 | newHandle->memFdBase = 0; |
276 | } |
277 | |
278 | DBG( "vcMemSize = %08x" , newHandle->vcMemSize ); |
279 | DBG( "vcMemBase = %08x" , newHandle->vcMemBase ); |
280 | DBG( "vcMemLoad = %08x" , newHandle->vcMemLoad ); |
281 | DBG( "vcMemPhys = %08x" , newHandle->vcMemPhys ); |
282 | |
283 | newHandle->vcMemEnd = newHandle->vcMemBase + newHandle->vcMemSize - 1; |
284 | |
285 | // See if we can detect the symbol table |
286 | if ( !ReadVideoCoreMemory( newHandle, |
287 | &newHandle->vcSymbolTableOffset, |
288 | newHandle->vcMemLoad + VC_SYMBOL_BASE_OFFSET, |
289 | sizeof( newHandle->vcSymbolTableOffset ))) |
290 | { |
291 | ERR( "ReadVideoCoreMemory @VC_SYMBOL_BASE_OFFSET (0x%08x) failed\n" , VC_SYMBOL_BASE_OFFSET ); |
292 | rc = -EIO; |
293 | goto err_exit; |
294 | } |
295 | |
296 | // When reading from a file, the VC binary is read into a buffer and the effective base is 0. |
297 | // But that may not be the actual base address the binary is intended to be loaded to. The |
298 | // following reads the debug header to find out what the actual base address is. |
299 | if( !newHandle->use_vc_mem ) |
300 | { |
301 | // Read the complete debug header |
302 | if ( !ReadVideoCoreMemory( newHandle, |
303 | &vc_dbg, |
304 | newHandle->vcMemLoad + VC_SYMBOL_BASE_OFFSET, |
305 | sizeof( vc_dbg ))) |
306 | { |
307 | ERR( "ReadVideoCoreMemory @VC_SYMBOL_BASE_OFFSET (0x%08x) failed\n" , VC_SYMBOL_BASE_OFFSET ); |
308 | rc = -EIO; |
309 | goto err_exit; |
310 | } |
311 | // The vc_dbg header gives the "base" address of the VC binary, |
312 | // which debug_sym calls the "load" address, so we need to adjust |
313 | // it by loadOffset to find the base of the whole memory dump file |
314 | newHandle->memFdBase = vc_dbg.params.vcMemBase - loadOffset; |
315 | newHandle->vcMemBase = vc_dbg.params.vcMemBase - loadOffset; |
316 | newHandle->vcMemLoad = vc_dbg.params.vcMemBase; |
317 | newHandle->vcMemEnd = newHandle->memFdBase + newHandle->vcMemSize - 1; |
318 | |
319 | DBG( "Updated from debug header:" ); |
320 | DBG( "vcMemSize = %08x" , newHandle->vcMemSize ); |
321 | DBG( "vcMemBase = %08x" , newHandle->vcMemBase ); |
322 | DBG( "vcMemLoad = %08x" , newHandle->vcMemLoad ); |
323 | DBG( "vcMemPhys = %08x" , newHandle->vcMemPhys ); |
324 | } |
325 | |
326 | DBG( "vcSymbolTableOffset = 0x%08x" , newHandle->vcSymbolTableOffset ); |
327 | |
328 | // Make sure that the pointer points into the first few megabytes of |
329 | // the memory space. |
330 | if ( (newHandle->vcSymbolTableOffset - newHandle->vcMemLoad) > ( MAX_VC_SIZE * 1024 * 1024 )) |
331 | { |
332 | ERR( "newHandle->vcSymbolTableOffset (0x%x - 0x%x) > %dMB\n" , newHandle->vcSymbolTableOffset, newHandle->vcMemLoad, MAX_VC_SIZE ); |
333 | rc = -EIO; |
334 | goto err_exit; |
335 | } |
336 | |
337 | // Make a pass to count how many symbols there are. |
338 | |
339 | symAddr = newHandle->vcSymbolTableOffset; |
340 | newHandle->numSymbols = 0; |
341 | do |
342 | { |
343 | if ( !ReadVideoCoreMemory( newHandle, |
344 | &debug_sym, |
345 | symAddr, |
346 | sizeof( debug_sym ))) |
347 | { |
348 | ERR( "ReadVideoCoreMemory @ symAddr(0x%08x) failed\n" , symAddr ); |
349 | rc = -EIO; |
350 | goto err_exit; |
351 | } |
352 | |
353 | newHandle->numSymbols++; |
354 | |
355 | DBG( "Symbol %d: label: 0x%p addr: 0x%08x size: %zu" , |
356 | newHandle->numSymbols, |
357 | debug_sym.label, |
358 | debug_sym.addr, |
359 | debug_sym.size ); |
360 | |
361 | if ( newHandle->numSymbols > 1024 ) |
362 | { |
363 | // Something isn't sane. |
364 | |
365 | ERR( "numSymbols (%d) > 1024 - looks wrong\n" , newHandle->numSymbols ); |
366 | rc = -EIO; |
367 | goto err_exit; |
368 | } |
369 | symAddr += sizeof( debug_sym ); |
370 | |
371 | } while ( debug_sym.label != 0 ); |
372 | newHandle->numSymbols--; |
373 | |
374 | DBG( "Detected %d symbols" , newHandle->numSymbols ); |
375 | |
376 | // Allocate some memory to hold the symbols, and read them in. |
377 | |
378 | symTableSize = newHandle->numSymbols * sizeof( debug_sym ); |
379 | if (( newHandle->symbol = malloc( symTableSize )) == NULL ) |
380 | { |
381 | rc = -ENOMEM; |
382 | goto err_exit; |
383 | } |
384 | if ( !ReadVideoCoreMemory( newHandle, |
385 | newHandle->symbol, |
386 | newHandle->vcSymbolTableOffset, |
387 | symTableSize )) |
388 | { |
389 | ERR( "ReadVideoCoreMemory @ newHandle->vcSymbolTableOffset(0x%08x) failed\n" , newHandle->vcSymbolTableOffset ); |
390 | rc = -EIO; |
391 | goto err_exit; |
392 | } |
393 | |
394 | // The names of the symbols are pointers in videocore space. We want |
395 | // to have them available locally, so we make copies and fixup |
396 | // the pointer. |
397 | |
398 | for ( symIdx = 0; symIdx < newHandle->numSymbols; symIdx++ ) |
399 | { |
400 | VC_DEBUG_SYMBOL_T *sym; |
401 | char symName[ 256 ]; |
402 | |
403 | sym = &newHandle->symbol[ symIdx ]; |
404 | |
405 | DBG( "Symbol %d: label: 0x%p addr: 0x%08x size: %zu" , |
406 | symIdx, |
407 | sym->label, |
408 | sym->addr, |
409 | sym->size ); |
410 | |
411 | if ( !ReadVideoCoreMemory( newHandle, |
412 | symName, |
413 | TO_VC_MEM_ADDR(sym->label), |
414 | sizeof( symName ))) |
415 | { |
416 | ERR( "ReadVideoCoreMemory @ sym->label(0x%08x) failed\n" , TO_VC_MEM_ADDR(sym->label) ); |
417 | rc = -EIO; |
418 | goto err_exit; |
419 | } |
420 | symName[ sizeof( symName ) - 1 ] = '\0'; |
421 | sym->label = vcos_strdup( symName ); |
422 | |
423 | DBG( "Symbol %d (@0x%p): label: '%s' addr: 0x%08x size: %zu" , |
424 | symIdx, |
425 | sym, |
426 | sym->label, |
427 | sym->addr, |
428 | sym->size ); |
429 | } |
430 | |
431 | *vcHandlePtr = newHandle; |
432 | return 0; |
433 | |
434 | err_exit: |
435 | close( newHandle->memFd ); |
436 | free( newHandle ); |
437 | |
438 | return rc; |
439 | } |
440 | |
441 | /**************************************************************************** |
442 | * |
443 | * Returns the number of symbols which were detected. |
444 | * |
445 | ***************************************************************************/ |
446 | |
447 | unsigned NumVideoCoreSymbols( VC_MEM_ACCESS_HANDLE_T vcHandle ) |
448 | { |
449 | return vcHandle->numSymbols; |
450 | } |
451 | |
452 | /**************************************************************************** |
453 | * |
454 | * Returns the name, address and size of the i'th symbol. |
455 | * |
456 | ***************************************************************************/ |
457 | |
458 | int GetVideoCoreSymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, unsigned idx, char *labelBuf, size_t labelBufSize, VC_MEM_ADDR_T *vcMemAddr, size_t *vcMemSize ) |
459 | { |
460 | VC_DEBUG_SYMBOL_T *sym; |
461 | |
462 | if ( idx >= vcHandle->numSymbols ) |
463 | { |
464 | return -EINVAL; |
465 | } |
466 | sym = &vcHandle->symbol[ idx ]; |
467 | |
468 | strncpy( labelBuf, sym->label, labelBufSize ); |
469 | labelBuf[labelBufSize - 1] = '\0'; |
470 | |
471 | if ( vcMemAddr != NULL ) |
472 | { |
473 | *vcMemAddr = (VC_MEM_ADDR_T)sym->addr; |
474 | } |
475 | if ( vcMemSize != NULL ) |
476 | { |
477 | *vcMemSize = sym->size; |
478 | } |
479 | |
480 | return 0; |
481 | } |
482 | |
483 | /**************************************************************************** |
484 | * |
485 | * Looks up the named, symbol. If the symbol is found, it's value and size |
486 | * are returned. |
487 | * |
488 | * Returns true if the lookup was successful. |
489 | * |
490 | ***************************************************************************/ |
491 | |
492 | int LookupVideoCoreSymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, const char *symbol, VC_MEM_ADDR_T *vcMemAddr, size_t *vcMemSize ) |
493 | { |
494 | unsigned idx; |
495 | char symName[ 64 ]; |
496 | VC_MEM_ADDR_T symAddr = 0; |
497 | size_t symSize = 0; |
498 | |
499 | for ( idx = 0; idx < vcHandle->numSymbols; idx++ ) |
500 | { |
501 | GetVideoCoreSymbol( vcHandle, idx, symName, sizeof( symName ), &symAddr, &symSize ); |
502 | if ( strcmp( symbol, symName ) == 0 ) |
503 | { |
504 | if ( vcMemAddr != NULL ) |
505 | { |
506 | *vcMemAddr = symAddr; |
507 | } |
508 | if ( vcMemSize != 0 ) |
509 | { |
510 | *vcMemSize = symSize; |
511 | } |
512 | |
513 | DBG( "%s found, addr = 0x%08x size = %zu" , symbol, symAddr, symSize ); |
514 | return 1; |
515 | } |
516 | } |
517 | |
518 | if ( vcMemAddr != NULL ) |
519 | { |
520 | *vcMemAddr = 0; |
521 | } |
522 | if ( vcMemSize != 0 ) |
523 | { |
524 | *vcMemSize = 0; |
525 | } |
526 | DBG( "%s not found" , symbol ); |
527 | return 0; |
528 | } |
529 | |
530 | /**************************************************************************** |
531 | * |
532 | * Looks up the named, symbol. If the symbol is found, and it's size is equal |
533 | * to the sizeof a uint32_t, then true is returned. |
534 | * |
535 | ***************************************************************************/ |
536 | |
537 | int LookupVideoCoreUInt32Symbol( VC_MEM_ACCESS_HANDLE_T vcHandle, |
538 | const char *symbol, |
539 | VC_MEM_ADDR_T *vcMemAddr ) |
540 | { |
541 | size_t vcMemSize; |
542 | |
543 | if ( !LookupVideoCoreSymbol( vcHandle, symbol, vcMemAddr, &vcMemSize )) |
544 | { |
545 | return 0; |
546 | } |
547 | |
548 | if ( vcMemSize != sizeof( uint32_t )) |
549 | { |
550 | ERR( "Symbol: '%s' has a size of %zu, expecting %zu" , symbol, vcMemSize, sizeof( uint32_t )); |
551 | return 0; |
552 | } |
553 | return 1; |
554 | } |
555 | |
556 | /**************************************************************************** |
557 | * |
558 | * Does Reads or Writes on the videocore memory. |
559 | * |
560 | ***************************************************************************/ |
561 | |
562 | static int AccessVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle, |
563 | MEM_OP_T mem_op, |
564 | void *buf, |
565 | VC_MEM_ADDR_T vcMemAddr, |
566 | size_t numBytes ) |
567 | { |
568 | VC_MEM_ADDR_T origVcMemAddr = vcMemAddr; |
569 | DBG( "%s %zu bytes @ 0x%08x" , mem_op == WRITE_MEM ? "Write" : "Read" , numBytes, vcMemAddr ); |
570 | |
571 | /* |
572 | * Since we'll be passed videocore pointers, we need to deal with the high bits. |
573 | * |
574 | * We need to strip off the high 2 bits to convert to a physical address, except |
575 | * for when the high 3 bits are equal to 011, which means that it corresponds to |
576 | * a peripheral and isn't accessible. |
577 | */ |
578 | |
579 | if ( IS_ALIAS_PERIPHERAL( vcMemAddr )) |
580 | { |
581 | // This is a peripheral address. |
582 | |
583 | ERR( "Can't access peripheral address 0x%08x" , vcMemAddr ); |
584 | return 0; |
585 | } |
586 | vcMemAddr = TO_VC_MEM_ADDR(ALIAS_NORMAL( vcMemAddr )); |
587 | |
588 | #if 0 |
589 | if ( (vcMemAddr < vcHandle->vcMemBase) || |
590 | (vcMemAddr > vcHandle->vcMemEnd) ) |
591 | { |
592 | ERR( "Memory address 0x%08x is outside range 0x%08x-0x%08x" , vcMemAddr, |
593 | vcHandle->vcMemBase, vcHandle->vcMemEnd ); |
594 | return 0; |
595 | } |
596 | #endif |
597 | if (( vcMemAddr + numBytes - 1) > vcHandle->vcMemEnd ) |
598 | { |
599 | ERR( "Memory address 0x%08x + numBytes 0x%08zx is > memory end 0x%08x" , |
600 | vcMemAddr, numBytes, vcHandle->vcMemEnd ); |
601 | return 0; |
602 | } |
603 | |
604 | vcMemAddr -= vcHandle->memFdBase; |
605 | |
606 | #if defined( WIN32 ) |
607 | if ( mem_op != READ_MEM ) |
608 | { |
609 | ERR( "Only reads are supported" ); |
610 | return 0; |
611 | } |
612 | if ( _lseek( vcHandle->memFd, vcMemAddr, SEEK_SET ) < 0 ) |
613 | { |
614 | ERR( "_lseek position 0x%08x failed" , vcMemAddr ); |
615 | return 0; |
616 | } |
617 | if ( _read( vcHandle->memFd, buf, numBytes ) < 0 ) |
618 | { |
619 | ERR( "_read failed: %s(%d)" , strerror( errno ), errno ); |
620 | return 0; |
621 | } |
622 | #else |
623 | // DMA allows memory to be accessed above 1008M and is more coherent so try this first |
624 | if (mem_op == READ_MEM && vcHandle->use_vc_mem) |
625 | { |
626 | DBG( "AccessVideoCoreMemory: %p, %x, %d" , buf, origVcMemAddr, numBytes ); |
627 | int s = vc_mem_copy(buf, (uint32_t)origVcMemAddr, numBytes); |
628 | if (s == 0) |
629 | return 1; |
630 | } |
631 | |
632 | { |
633 | uint8_t *mapAddr; |
634 | size_t mapSize; |
635 | size_t memOffset; |
636 | off_t vcMapAddr; |
637 | int mmap_prot; |
638 | |
639 | if ( mem_op == WRITE_MEM ) |
640 | { |
641 | mmap_prot = PROT_WRITE; |
642 | } |
643 | else |
644 | { |
645 | mmap_prot = PROT_READ; |
646 | } |
647 | |
648 | // We can only map pages on 4K boundaries, so round the address down and the size up. |
649 | |
650 | memOffset = vcMemAddr & ~PAGE_MASK; |
651 | |
652 | vcMapAddr = vcMemAddr & PAGE_MASK; |
653 | |
654 | mapSize = ( memOffset + numBytes + PAGE_SIZE - 1 ) & PAGE_MASK; |
655 | if (( mapAddr = mmap( 0, mapSize, mmap_prot, MAP_SHARED, vcHandle->memFd, vcMapAddr )) == MAP_FAILED ) |
656 | { |
657 | ERR( "mmap failed: %s(%d)" , strerror( errno ), errno ); |
658 | return 0; |
659 | } |
660 | if ( mem_op == WRITE_MEM ) |
661 | { |
662 | memcpy( mapAddr + memOffset, buf, numBytes ); |
663 | } |
664 | else |
665 | { |
666 | memcpy( buf, mapAddr + memOffset, numBytes ); |
667 | } |
668 | |
669 | munmap( mapAddr, mapSize ); |
670 | } |
671 | #endif |
672 | |
673 | return 1; |
674 | } |
675 | |
676 | |
677 | /**************************************************************************** |
678 | * |
679 | * Reads 'numBytes' from the videocore memory starting at 'vcMemAddr'. The |
680 | * results are stored in 'buf'. |
681 | * |
682 | * Returns true if the read was successful. |
683 | * |
684 | ***************************************************************************/ |
685 | |
686 | int ReadVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle, void *buf, VC_MEM_ADDR_T vcMemAddr, size_t numBytes ) |
687 | { |
688 | return AccessVideoCoreMemory( vcHandle, READ_MEM, buf, vcMemAddr, numBytes ); |
689 | } |
690 | |
691 | /**************************************************************************** |
692 | * |
693 | * Reads 'numBytes' from the videocore memory starting at 'vcMemAddr'. The |
694 | * results are stored in 'buf'. |
695 | * |
696 | * Returns true if the read was successful. |
697 | * |
698 | ***************************************************************************/ |
699 | |
700 | int ReadVideoCoreMemoryBySymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, const char *symbol, void *buf, size_t bufSize ) |
701 | { |
702 | VC_MEM_ADDR_T vcMemAddr; |
703 | size_t vcMemSize; |
704 | |
705 | if ( !LookupVideoCoreSymbol( vcHandle, symbol, &vcMemAddr, &vcMemSize )) |
706 | { |
707 | ERR( "Symbol not found: '%s'" , symbol ); |
708 | return 0; |
709 | } |
710 | |
711 | if ( vcMemSize > bufSize ) |
712 | { |
713 | vcMemSize = bufSize; |
714 | } |
715 | |
716 | if ( !ReadVideoCoreMemory( vcHandle, buf, vcMemAddr, vcMemSize )) |
717 | { |
718 | ERR( "Unable to read %zu bytes @ 0x%08x" , vcMemSize, vcMemAddr ); |
719 | return 0; |
720 | } |
721 | return 1; |
722 | } |
723 | |
724 | /**************************************************************************** |
725 | * |
726 | * Looks up a symbol and reads the contents into a user supplied buffer. |
727 | * |
728 | * Returns true if the read was successful. |
729 | * |
730 | ***************************************************************************/ |
731 | |
732 | int ReadVideoCoreStringBySymbol( VC_MEM_ACCESS_HANDLE_T vcHandle, |
733 | const char *symbol, |
734 | char *buf, |
735 | size_t bufSize ) |
736 | { |
737 | VC_MEM_ADDR_T vcMemAddr; |
738 | size_t vcMemSize; |
739 | |
740 | if ( !LookupVideoCoreSymbol( vcHandle, symbol, &vcMemAddr, &vcMemSize )) |
741 | { |
742 | ERR( "Symbol not found: '%s'" , symbol ); |
743 | return 0; |
744 | } |
745 | |
746 | if ( vcMemSize > bufSize ) |
747 | { |
748 | vcMemSize = bufSize; |
749 | } |
750 | |
751 | if ( !ReadVideoCoreMemory( vcHandle, buf, vcMemAddr, vcMemSize )) |
752 | { |
753 | ERR( "Unable to read %zu bytes @ 0x%08x" , vcMemSize, vcMemAddr ); |
754 | return 0; |
755 | } |
756 | |
757 | // Make sure that the result is null-terminated |
758 | |
759 | buf[vcMemSize-1] = '\0'; |
760 | return 1; |
761 | } |
762 | |
763 | /**************************************************************************** |
764 | * |
765 | * Writes 'numBytes' into the videocore memory starting at 'vcMemAddr'. The |
766 | * data is taken from 'buf'. |
767 | * |
768 | * Returns true if the write was successful. |
769 | * |
770 | ***************************************************************************/ |
771 | |
772 | int WriteVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle, |
773 | void *buf, |
774 | VC_MEM_ADDR_T vcMemAddr, |
775 | size_t numBytes ) |
776 | { |
777 | return AccessVideoCoreMemory( vcHandle, WRITE_MEM, buf, vcMemAddr, numBytes ); |
778 | } |
779 | |
780 | /**************************************************************************** |
781 | * |
782 | * Closes the memory space opened previously via OpenVideoCoreMemory. |
783 | * |
784 | ***************************************************************************/ |
785 | |
786 | void CloseVideoCoreMemory( VC_MEM_ACCESS_HANDLE_T vcHandle ) |
787 | { |
788 | unsigned i; |
789 | if ( vcHandle->symbol ) |
790 | for ( i = 0; i < vcHandle->numSymbols; i++ ) |
791 | free( (char *)vcHandle->symbol[i].label ); |
792 | free( vcHandle->symbol ); |
793 | |
794 | if ( vcHandle->memFd >= 0 ) |
795 | close( vcHandle->memFd ); |
796 | |
797 | free( vcHandle ); |
798 | } |
799 | |
800 | /**************************************************************************** |
801 | * |
802 | * Returns the base address of the videocore memory space. |
803 | * |
804 | ***************************************************************************/ |
805 | |
806 | VC_MEM_ADDR_T GetVideoCoreMemoryBase( VC_MEM_ACCESS_HANDLE_T vcHandle ) |
807 | { |
808 | return vcHandle->vcMemBase; |
809 | } |
810 | |
811 | /**************************************************************************** |
812 | * |
813 | * Returns the size of the videocore memory space. |
814 | * |
815 | ***************************************************************************/ |
816 | |
817 | VC_MEM_SIZE_T GetVideoCoreMemorySize( VC_MEM_ACCESS_HANDLE_T vcHandle ) |
818 | { |
819 | return vcHandle->vcMemSize; |
820 | } |
821 | |
822 | /**************************************************************************** |
823 | * |
824 | * Returns the videocore memory physical address. |
825 | * |
826 | ***************************************************************************/ |
827 | |
828 | VC_MEM_ADDR_T GetVideoCoreMemoryPhysicalAddress( VC_MEM_ACCESS_HANDLE_T vcHandle ) |
829 | { |
830 | return vcHandle->vcMemPhys; |
831 | } |
832 | |
833 | |