VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/BIOS/MakeDebianBiosAssembly.cpp@ 42057

Last change on this file since 42057 was 42057, checked in by vboxsync, 12 years ago

Renamed PC/BIOS-new to PC/BIOS and dropped the VBOX_WITH_NEW_BIOS makefile variable.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.0 KB
Line 
1/* $Id: MakeDebianBiosAssembly.cpp 42057 2012-07-09 12:54:37Z vboxsync $ */
2/** @file
3 * MakeDebianBiosAssembly - Generate Assembly Source for Debian-minded Distros.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <iprt/asm.h>
23#include <iprt/buildconfig.h>
24#include <iprt/ctype.h>
25#include <iprt/dbg.h>
26#include <iprt/file.h>
27#include <iprt/getopt.h>
28#include <iprt/initterm.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/message.h>
32#include <iprt/string.h>
33#include <iprt/stream.h>
34#include <iprt/x86.h>
35
36#include <VBox/dis.h>
37
38
39/*******************************************************************************
40* Defined Constants And Macros *
41*******************************************************************************/
42/** The flat ROM base address. */
43#define VBOX_BIOS_BASE UINT32_C(0xf0000)
44
45
46/*******************************************************************************
47* Structures and Typedefs *
48*******************************************************************************/
49/**
50 * A BIOS segment.
51 */
52typedef struct BIOSSEG
53{
54 char szName[32];
55 char szClass[32];
56 char szGroup[32];
57 RTFAR16 Address;
58 uint32_t uFlatAddr;
59 uint32_t cb;
60} BIOSSEG;
61/** Pointer to a BIOS segment. */
62typedef BIOSSEG *PBIOSSEG;
63
64
65/**
66 * A BIOS object file.
67 */
68typedef struct BIOSOBJFILE
69{
70 RTLISTNODE Node;
71 char *pszSource;
72 char *pszObject;
73} BIOSOBJFILE;
74/** A BIOS object file. */
75typedef BIOSOBJFILE *PBIOSOBJFILE;
76
77
78/**
79 * Pointer to a BIOS map parser handle.
80 */
81typedef struct BIOSMAP
82{
83 /** The stream pointer. */
84 PRTSTREAM hStrm;
85 /** The file name. */
86 const char *pszMapFile;
87 /** Set when EOF has been reached. */
88 bool fEof;
89 /** The current line number (0 based).*/
90 uint32_t iLine;
91 /** The length of the current line. */
92 uint32_t cch;
93 /** The offset of the first non-white character on the line. */
94 uint32_t offNW;
95 /** The line buffer. */
96 char szLine[16384];
97} BIOSMAP;
98/** Pointer to a BIOS map parser handle. */
99typedef BIOSMAP *PBIOSMAP;
100
101
102/*******************************************************************************
103* Global Variables *
104*******************************************************************************/
105static unsigned g_cVerbose = 1 /*0*/;
106/** Pointer to the BIOS image. */
107static uint8_t const *g_pbImg;
108/** The size of the BIOS image. */
109static size_t g_cbImg;
110
111/** Debug module for the map file. */
112static RTDBGMOD g_hMapMod = NIL_RTDBGMOD;
113/** The number of BIOS segments found in the map file. */
114static uint32_t g_cSegs = 0;
115/** Array of BIOS segments from the map file. */
116static BIOSSEG g_aSegs[32];
117/** List of BIOSOBJFILE. */
118static RTLISTANCHOR g_ObjList;
119
120/** The output stream. */
121static PRTSTREAM g_hStrmOutput = NULL;
122
123
124static bool outputPrintfV(const char *pszFormat, va_list va)
125{
126 int rc = RTStrmPrintfV(g_hStrmOutput, pszFormat, va);
127 if (RT_FAILURE(rc))
128 {
129 RTMsgError("Output error: %Rrc\n", rc);
130 return false;
131 }
132 return true;
133}
134
135
136static bool outputPrintf(const char *pszFormat, ...)
137{
138 va_list va;
139 va_start(va, pszFormat);
140 bool fRc = outputPrintfV(pszFormat, va);
141 va_end(va);
142 return fRc;
143}
144
145
146/**
147 * Opens the output file for writing.
148 *
149 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
150 * @param pszOutput Path to the output file.
151 */
152static RTEXITCODE OpenOutputFile(const char *pszOutput)
153{
154 if (!pszOutput)
155 g_hStrmOutput = g_pStdOut;
156 else
157 {
158 int rc = RTStrmOpen(pszOutput, "w", &g_hStrmOutput);
159 if (RT_FAILURE(rc))
160 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open output file '%s': %Rrc", pszOutput, rc);
161 }
162 return RTEXITCODE_SUCCESS;
163}
164
165
166/**
167 * Displays a disassembly error and returns @c false.
168 *
169 * @returns @c false.
170 * @param pszFormat The error format string.
171 * @param ... Format argument.
172 */
173static bool disError(const char *pszFormat, ...)
174{
175 va_list va;
176 va_start(va, pszFormat);
177 RTMsgErrorV(pszFormat, va);
178 va_end(va);
179 return false;
180}
181
182
183/**
184 * Output the disassembly file header.
185 *
186 * @returns @c true on success,
187 */
188static bool disFileHeader(void)
189{
190 bool fRc;
191 fRc = outputPrintf("; $Id: MakeDebianBiosAssembly.cpp 42057 2012-07-09 12:54:37Z vboxsync $ \n"
192 ";; @file\n"
193 "; Auto Generated source file. Do not edit.\n"
194 ";\n"
195 );
196 if (!fRc)
197 return fRc;
198
199 /*
200 * List the header of each source file, up to and including the
201 * copyright notice.
202 */
203 PBIOSOBJFILE pObjFile;
204 RTListForEach(&g_ObjList, pObjFile, BIOSOBJFILE, Node)
205 {
206 PRTSTREAM hStrm;
207 int rc = RTStrmOpen(pObjFile->pszSource, "r", &hStrm);
208 if (RT_SUCCESS(rc))
209 {
210 fRc = outputPrintf("\n"
211 ";\n"
212 "; Source file: %Rbn\n"
213 ";\n"
214 , pObjFile->pszSource);
215 uint32_t iLine = 0;
216 bool fSeenCopyright = false;
217 char szLine[4096];
218 while ((rc = RTStrmGetLine(hStrm, szLine, sizeof(szLine))) == VINF_SUCCESS)
219 {
220 iLine++;
221
222 /* Check if we're done. */
223 char *psz = RTStrStrip(szLine);
224 if ( fSeenCopyright
225 && ( (psz[0] == '*' && psz[1] == '/')
226 || psz[0] == '\0') )
227 break;
228
229 /* Strip comment suffix. */
230 size_t cch = strlen(psz);
231 if (cch >= 2 && psz[cch - 1] == '/' && psz[cch - 2] == '*')
232 {
233 psz[cch - 2] = '\0';
234 RTStrStripR(psz);
235 }
236
237 /* Skip line prefix. */
238 if (psz[0] == '/' && psz[1] == '*')
239 psz += 2;
240 else if (psz[0] == '*')
241 psz += 1;
242 else
243 while (*psz == ';')
244 psz++;
245 if (RT_C_IS_SPACE(*psz))
246 psz++;
247
248 /* Skip the doxygen file tag line. */
249 if (!strcmp(psz, "* @file") || !strcmp(psz, "@file"))
250 continue;
251
252 /* Detect copyright section. */
253 if ( !fSeenCopyright
254 && ( strstr(psz, "Copyright")
255 || strstr(psz, "copyright")) )
256 fSeenCopyright = true;
257
258 fRc = outputPrintf("; %s\n", psz) && fRc;
259 }
260
261 RTStrmClose(hStrm);
262 if (rc != VINF_SUCCESS)
263 return disError("Error reading '%s': rc=%Rrc iLine=%u", pObjFile->pszSource, rc, iLine);
264 }
265 }
266
267 /*
268 * Set the org.
269 */
270 fRc = outputPrintf("\n"
271 "\n"
272 "\n"
273 ) && fRc;
274 return fRc;
275}
276
277
278/**
279 * Checks if a byte sequence could be a string litteral.
280 *
281 * @returns @c true if it is, @c false if it isn't.
282 * @param uFlatAddr The address of the byte sequence.
283 * @param cb The length of the sequence.
284 */
285static bool disIsString(uint32_t uFlatAddr, uint32_t cb)
286{
287 if (cb < 6)
288 return false;
289
290 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
291 while (cb > 0)
292 {
293 if ( !RT_C_IS_PRINT(*pb)
294 && *pb != '\r'
295 && *pb != '\n'
296 && *pb != '\t')
297 {
298 if (*pb == '\0')
299 {
300 do
301 {
302 pb++;
303 cb--;
304 } while (cb > 0 && *pb == '\0');
305 return cb == 0;
306 }
307 return false;
308 }
309 pb++;
310 cb--;
311 }
312
313 return true;
314}
315
316
317/**
318 * Checks if a dword could be a far 16:16 BIOS address.
319 *
320 * @returns @c true if it is, @c false if it isn't.
321 * @param uFlatAddr The address of the dword.
322 */
323static bool disIsFarBiosAddr(uint32_t uFlatAddr)
324{
325 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
326 if (pu16[1] < 0xf000)
327 return false;
328 if (pu16[1] > 0xfff0)
329 return false;
330 uint32_t uFlatAddr2 = (uint32_t)(pu16[1] << 4) | pu16[0];
331 if (uFlatAddr2 >= VBOX_BIOS_BASE + _64K)
332 return false;
333 return true;
334}
335
336
337static bool disByteData(uint32_t uFlatAddr, uint32_t cb)
338{
339 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
340 size_t cbOnLine = 0;
341 while (cb-- > 0)
342 {
343 bool fRc;
344 if (cbOnLine >= 16)
345 {
346 fRc = outputPrintf("\n"
347 " db 0%02xh", *pb);
348 cbOnLine = 1;
349 }
350 else if (!cbOnLine)
351 {
352 fRc = outputPrintf(" db 0%02xh", *pb);
353 cbOnLine = 1;
354 }
355 else
356 {
357 fRc = outputPrintf(", 0%02xh", *pb);
358 cbOnLine++;
359 }
360 if (!fRc)
361 return false;
362 pb++;
363 }
364 return outputPrintf("\n");
365}
366
367
368static bool disWordData(uint32_t uFlatAddr, uint32_t cb)
369{
370 if (cb & 1)
371 return disError("disWordData expects word aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
372
373 uint16_t const *pu16 = (uint16_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
374 size_t cbOnLine = 0;
375 while (cb > 0)
376 {
377 bool fRc;
378 if (cbOnLine >= 16)
379 {
380 fRc = outputPrintf("\n"
381 " dw 0%04xh", *pu16);
382 cbOnLine = 2;
383 }
384 else if (!cbOnLine)
385 {
386 fRc = outputPrintf(" dw 0%04xh", *pu16);
387 cbOnLine = 2;
388 }
389 else
390 {
391 fRc = outputPrintf(", 0%04xh", *pu16);
392 cbOnLine += 2;
393 }
394 if (!fRc)
395 return false;
396 pu16++;
397 cb -= 2;
398 }
399 return outputPrintf("\n");
400}
401
402
403static bool disDWordData(uint32_t uFlatAddr, uint32_t cb)
404{
405 if (cb & 3)
406 return disError("disWordData expects dword aligned size: cb=%#x uFlatAddr=%#x", uFlatAddr, cb);
407
408 uint32_t const *pu32 = (uint32_t const *)&g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
409 size_t cbOnLine = 0;
410 while (cb > 0)
411 {
412 bool fRc;
413 if (cbOnLine >= 16)
414 {
415 fRc = outputPrintf("\n"
416 " dd 0%08xh", *pu32);
417 cbOnLine = 4;
418 }
419 else if (!cbOnLine)
420 {
421 fRc = outputPrintf(" dd 0%08xh", *pu32);
422 cbOnLine = 4;
423 }
424 else
425 {
426 fRc = outputPrintf(", 0%08xh", *pu32);
427 cbOnLine += 4;
428 }
429 if (!fRc)
430 return false;
431 pu32++;
432 cb -= 4;
433 }
434 return outputPrintf("\n");
435}
436
437
438static bool disStringData(uint32_t uFlatAddr, uint32_t cb)
439{
440 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
441 uint32_t cchOnLine = 0;
442 while (cb > 0)
443 {
444 /* Line endings and beginnings. */
445 if (cchOnLine >= 72)
446 {
447 if (!outputPrintf("\n"))
448 return false;
449 cchOnLine = 0;
450 }
451 if ( !cchOnLine
452 && !outputPrintf(" db "))
453 return false;
454
455 /* See how many printable character we've got. */
456 uint32_t cchPrintable = 0;
457 while ( cchPrintable < cb
458 && RT_C_IS_PRINT(pb[cchPrintable])
459 && pb[cchPrintable] != '\'')
460 cchPrintable++;
461
462 bool fRc = true;
463 if (cchPrintable)
464 {
465 if (cchPrintable + cchOnLine > 72)
466 cchPrintable = 72 - cchOnLine;
467 if (cchOnLine)
468 {
469 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
470 cchOnLine += 4 + cchPrintable;
471 }
472 else
473 {
474 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
475 cchOnLine += 2 + cchPrintable;
476 }
477 pb += cchPrintable;
478 cb -= cchPrintable;
479 }
480 else
481 {
482 if (cchOnLine)
483 {
484 fRc = outputPrintf(", 0%02xh", *pb);
485 cchOnLine += 6;
486 }
487 else
488 {
489 fRc = outputPrintf("0%02xh", *pb);
490 cchOnLine += 4;
491 }
492 pb++;
493 cb--;
494 }
495 if (!fRc)
496 return false;
497 }
498 return outputPrintf("\n");
499}
500
501
502/**
503 * For dumping a portion of a string table.
504 *
505 * @returns @c true on success, @c false on failure.
506 * @param uFlatAddr The start address.
507 * @param cb The size of the string table.
508 */
509static bool disStringsData(uint32_t uFlatAddr, uint32_t cb)
510{
511 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
512 uint32_t cchOnLine = 0;
513 uint8_t bPrev = 255;
514 while (cb > 0)
515 {
516 /* Line endings and beginnings. */
517 if ( cchOnLine >= 72
518 || (bPrev == '\0' && *pb != '\0'))
519 {
520 if (!outputPrintf("\n"))
521 return false;
522 cchOnLine = 0;
523 }
524 if ( !cchOnLine
525 && !outputPrintf(" db "))
526 return false;
527
528 /* See how many printable character we've got. */
529 uint32_t cchPrintable = 0;
530 while ( cchPrintable < cb
531 && RT_C_IS_PRINT(pb[cchPrintable])
532 && pb[cchPrintable] != '\'')
533 cchPrintable++;
534
535 bool fRc = true;
536 if (cchPrintable)
537 {
538 if (cchPrintable + cchOnLine > 72)
539 cchPrintable = 72 - cchOnLine;
540 if (cchOnLine)
541 {
542 fRc = outputPrintf(", '%.*s'", cchPrintable, pb);
543 cchOnLine += 4 + cchPrintable;
544 }
545 else
546 {
547 fRc = outputPrintf("'%.*s'", cchPrintable, pb);
548 cchOnLine += 2 + cchPrintable;
549 }
550 pb += cchPrintable;
551 cb -= cchPrintable;
552 }
553 else
554 {
555 if (cchOnLine)
556 {
557 fRc = outputPrintf(", 0%02xh", *pb);
558 cchOnLine += 6;
559 }
560 else
561 {
562 fRc = outputPrintf("0%02xh", *pb);
563 cchOnLine += 4;
564 }
565 pb++;
566 cb--;
567 }
568 if (!fRc)
569 return false;
570 bPrev = pb[-1];
571 }
572 return outputPrintf("\n");
573}
574
575
576/**
577 * Minds the gap between two segments.
578 *
579 * Gaps should generally be zero filled.
580 *
581 * @returns @c true on success, @c false on failure.
582 * @param uFlatAddr The address of the gap.
583 * @param cbPadding The size of the gap.
584 */
585static bool disCopySegmentGap(uint32_t uFlatAddr, uint32_t cbPadding)
586{
587 if (g_cVerbose > 0)
588 outputPrintf("\n"
589 " ; Padding %#x bytes at %#x\n", cbPadding, uFlatAddr);
590 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
591 if (!ASMMemIsAll8(pb, cbPadding, 0))
592 return outputPrintf(" times %u db 0\n", cbPadding);
593
594 return disByteData(uFlatAddr, cbPadding);
595}
596
597
598/**
599 * Worker for disGetNextSymbol that only does the looking up, no RTDBSYMBOL::cb
600 * calc.
601 *
602 * @param uFlatAddr The address to start searching at.
603 * @param cbMax The size of the search range.
604 * @param poff Where to return the offset between the symbol
605 * and @a uFlatAddr.
606 * @param pSym Where to return the symbol data.
607 */
608static void disGetNextSymbolWorker(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
609{
610 RTINTPTR off = 0;
611 int rc = RTDbgModSymbolByAddr(g_hMapMod, RTDBGSEGIDX_RVA, uFlatAddr, RTDBGSYMADDR_FLAGS_GREATER_OR_EQUAL, &off, pSym);
612 if (RT_SUCCESS(rc))
613 {
614 /* negative offset, indicates beyond. */
615 if (off <= 0)
616 {
617 *poff = (uint32_t)-off;
618 return;
619 }
620
621 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> off=%RTptr cb=%RTptr uValue=%RTptr '%s'\n",
622 uFlatAddr, off, pSym->cb, pSym->Value, pSym->szName);
623 }
624 else if (rc != VERR_SYMBOL_NOT_FOUND)
625 outputPrintf(" ; !! RTDbgModSymbolByAddr(,,%#x,,) -> %Rrc\n", uFlatAddr, rc);
626
627 RTStrPrintf(pSym->szName, sizeof(pSym->szName), "_dummy_addr_%#x", uFlatAddr + cbMax);
628 pSym->Value = uFlatAddr + cbMax;
629 pSym->cb = 0;
630 pSym->offSeg = uFlatAddr + cbMax;
631 pSym->iSeg = RTDBGSEGIDX_RVA;
632 pSym->iOrdinal = 0;
633 pSym->fFlags = 0;
634 *poff = cbMax;
635}
636
637
638/**
639 * Gets the symbol at or after the given address.
640 *
641 * If there are no symbols in the specified range, @a pSym and @a poff will be
642 * set up to indicate a symbol at the first byte after the range.
643 *
644 * @param uFlatAddr The address to start searching at.
645 * @param cbMax The size of the search range.
646 * @param poff Where to return the offset between the symbol
647 * and @a uFlatAddr.
648 * @param pSym Where to return the symbol data.
649 */
650static void disGetNextSymbol(uint32_t uFlatAddr, uint32_t cbMax, uint32_t *poff, PRTDBGSYMBOL pSym)
651{
652 disGetNextSymbolWorker(uFlatAddr, cbMax, poff, pSym);
653 if ( *poff < cbMax
654 && pSym->cb == 0)
655 {
656 if (*poff + 1 < cbMax)
657 {
658 uint32_t off2;
659 RTDBGSYMBOL Sym2;
660 disGetNextSymbolWorker(uFlatAddr + *poff + 1, cbMax - *poff - 1, &off2, &Sym2);
661 pSym->cb = off2 + 1;
662 }
663 else
664 pSym->cb = 1;
665 }
666 if (pSym->cb > cbMax - *poff)
667 pSym->cb = cbMax - *poff;
668
669 if (g_cVerbose > 1)
670 outputPrintf(" ; disGetNextSymbol %#x LB %#x -> off=%#x cb=%RTptr uValue=%RTptr '%s'\n",
671 uFlatAddr, cbMax, *poff, pSym->cb, pSym->Value, pSym->szName);
672
673}
674
675
676/**
677 * For dealing with the const segment (string constants).
678 *
679 * @returns @c true on success, @c false on failure.
680 * @param iSeg The segment.
681 */
682static bool disConstSegment(uint32_t iSeg)
683{
684 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
685 uint32_t cb = g_aSegs[iSeg].cb;
686
687 while (cb > 0)
688 {
689 uint32_t off;
690 RTDBGSYMBOL Sym;
691 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
692
693 if (off > 0)
694 {
695 if (!disStringsData(uFlatAddr, off))
696 return false;
697 cb -= off;
698 uFlatAddr += off;
699 off = 0;
700 if (!cb)
701 break;
702 }
703
704 bool fRc;
705 if (off == 0)
706 {
707 size_t cchName = strlen(Sym.szName);
708 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
709 if (!fRc)
710 return false;
711 fRc = disStringsData(uFlatAddr, Sym.cb);
712 uFlatAddr += Sym.cb;
713 cb -= Sym.cb;
714 }
715 else
716 {
717 fRc = disStringsData(uFlatAddr, Sym.cb);
718 uFlatAddr += cb;
719 cb = 0;
720 }
721 if (!fRc)
722 return false;
723 }
724
725 return true;
726}
727
728
729
730static bool disDataSegment(uint32_t iSeg)
731{
732 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
733 uint32_t cb = g_aSegs[iSeg].cb;
734
735 while (cb > 0)
736 {
737 uint32_t off;
738 RTDBGSYMBOL Sym;
739 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
740
741 if (off > 0)
742 {
743 if (!disByteData(uFlatAddr, off))
744 return false;
745 cb -= off;
746 uFlatAddr += off;
747 off = 0;
748 if (!cb)
749 break;
750 }
751
752 bool fRc;
753 if (off == 0)
754 {
755 size_t cchName = strlen(Sym.szName);
756 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
757 if (!fRc)
758 return false;
759
760 if (Sym.cb == 2)
761 fRc = disWordData(uFlatAddr, 2);
762 //else if (Sym.cb == 4 && disIsFarBiosAddr(uFlatAddr))
763 // fRc = disDWordData(uFlatAddr, 4);
764 else if (Sym.cb == 4)
765 fRc = disDWordData(uFlatAddr, 4);
766 else if (disIsString(uFlatAddr, Sym.cb))
767 fRc = disStringData(uFlatAddr, Sym.cb);
768 else
769 fRc = disByteData(uFlatAddr, Sym.cb);
770
771 uFlatAddr += Sym.cb;
772 cb -= Sym.cb;
773 }
774 else
775 {
776 fRc = disByteData(uFlatAddr, cb);
777 uFlatAddr += cb;
778 cb = 0;
779 }
780 if (!fRc)
781 return false;
782 }
783
784 return true;
785}
786
787
788static bool disIsCodeAndAdjustSize(uint32_t uFlatAddr, PRTDBGSYMBOL pSym, PBIOSSEG pSeg)
789{
790 if (!strcmp(pSeg->szName, "BIOSSEG"))
791 {
792 if ( !strcmp(pSym->szName, "rom_fdpt")
793 || !strcmp(pSym->szName, "pmbios_gdt")
794 || !strcmp(pSym->szName, "pmbios_gdt_desc")
795 || !strcmp(pSym->szName, "_pmode_IDT")
796 || !strcmp(pSym->szName, "_rmode_IDT")
797 || !strncmp(pSym->szName, RT_STR_TUPLE("font"))
798 || !strcmp(pSym->szName, "bios_string")
799 || !strcmp(pSym->szName, "vector_table")
800 || !strcmp(pSym->szName, "pci_routing_table_structure")
801 )
802 return false;
803 }
804
805 if (!strcmp(pSym->szName, "cpu_reset"))
806 pSym->cb = RT_MIN(pSym->cb, 5);
807 else if (!strcmp(pSym->szName, "pci_init_end"))
808 pSym->cb = RT_MIN(pSym->cb, 3);
809
810 return true;
811}
812
813
814static bool disIs16BitCode(const char *pszSymbol)
815{
816 return true;
817}
818
819
820/**
821 * Deals with instructions that YASM will assemble differently than WASM/WCC.
822 */
823static size_t disHandleYasmDifferences(PDISCPUSTATE pCpuState, uint32_t uFlatAddr, uint32_t cbInstr,
824 char *pszBuf, size_t cbBuf, size_t cchUsed)
825{
826 bool fDifferent = DISFormatYasmIsOddEncoding(pCpuState);
827 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
828
829 /*
830 * Disassembler bugs.
831 */
832 /** @todo Group 1a and 11 seems to be disassembled incorrectly when
833 * modrm.reg != 0. Those encodings should be invalid AFAICT. */
834
835 if ( ( pCpuState->bOpCode == 0x8f /* group 1a */
836 || pCpuState->bOpCode == 0xc7 /* group 11 */
837 || pCpuState->bOpCode == 0xc6 /* group 11 - not verified */
838 )
839 && pCpuState->ModRM.Bits.Reg != 0)
840 fDifferent = true;
841 /*
842 * Check these out and consider adding them to DISFormatYasmIsOddEncoding.
843 */
844 else if ( pb[0] == 0xf3
845 && pb[1] == 0x66
846 && pb[2] == 0x6d)
847 fDifferent = true; /* rep insd - prefix switched. */
848 else if ( pb[0] == 0xc6
849 && pb[1] == 0xc5
850 && pb[2] == 0xba)
851 fDifferent = true; /* mov ch, 0bah - yasm uses a short sequence: 0xb5 0xba. */
852
853
854 /*
855 * Handle different stuff.
856 */
857 if (fDifferent)
858 {
859 disByteData(uFlatAddr, cbInstr); /* lazy bird. */
860
861 if (cchUsed + 2 < cbBuf)
862 {
863 memmove(pszBuf + 2, pszBuf, cchUsed + 2);
864 cchUsed += 2;
865 }
866
867 pszBuf[0] = ';';
868 pszBuf[1] = ' ';
869 }
870
871 return cchUsed;
872}
873
874
875/**
876 * @callback_method_impl{FNDISREADBYTES}
877 *
878 * @remarks @a uSrcAddr is the flat address.
879 */
880static DECLCALLBACK(int) disReadOpcodeBytes(PDISCPUSTATE pDis, uint8_t offInstr, uint8_t cbMinRead, uint8_t cbMaxRead)
881{
882 RTUINTPTR offBios = pDis->uInstrAddr + offInstr - VBOX_BIOS_BASE;
883 size_t cbToRead = cbMaxRead;
884 if (offBios + cbToRead > _64K)
885 {
886 if (offBios >= _64K)
887 cbToRead = 0;
888 else
889 cbToRead = _64K - offBios;
890 }
891 memcpy(&pDis->abInstr[offInstr], &g_pbImg[offBios], cbToRead);
892 pDis->cbCachedInstr = offInstr + cbToRead;
893 return VINF_SUCCESS;
894}
895
896
897/**
898 * Disassembles code.
899 *
900 * @returns @c true on success, @c false on failure.
901 * @param uFlatAddr The address where the code starts.
902 * @param cb The amount of code to disassemble.
903 * @param fIs16Bit Is is 16-bit (@c true) or 32-bit (@c false).
904 */
905static bool disCode(uint32_t uFlatAddr, uint32_t cb, bool fIs16Bit)
906{
907 uint8_t const *pb = &g_pbImg[uFlatAddr - VBOX_BIOS_BASE];
908
909 while (cb > 0)
910 {
911 /* Trailing zero padding detection. */
912 if ( *pb == '\0'
913 && ASMMemIsAll8(pb, RT_MIN(cb, 8), 0) == NULL)
914 {
915 void *pv = ASMMemIsAll8(pb, cb, 0);
916 uint32_t cbZeros = pv ? (uint32_t)((uint8_t const *)pv - pb) : cb;
917 if (!outputPrintf(" times %#x db 0\n", cbZeros))
918 return false;
919 cb -= cbZeros;
920 pb += cbZeros;
921 uFlatAddr += cbZeros;
922 if ( cb == 2
923 && pb[0] == 'X'
924 && pb[1] == 'M')
925 return disStringData(uFlatAddr, cb);
926 }
927 /* Work arounds for switch tables and such (disas assertions). */
928 else if ( ( pb[0] == 0x11 /* int13_cdemu switch */
929 && pb[1] == 0xda
930 && pb[2] == 0x05
931 && pb[3] == 0xff
932 && pb[4] == 0xff
933 )
934 || 0
935 )
936 return disByteData(uFlatAddr, cb);
937 else
938 {
939 unsigned cbInstr;
940 DISCPUSTATE CpuState;
941 int rc = DISInstrWithReader(uFlatAddr, fIs16Bit ? DISCPUMODE_16BIT : DISCPUMODE_32BIT,
942 disReadOpcodeBytes, NULL, &CpuState, &cbInstr);
943 if ( RT_SUCCESS(rc)
944 && cbInstr <= cb
945 && CpuState.pCurInstr
946 && CpuState.pCurInstr->uOpcode != OP_INVALID)
947 {
948 char szTmp[4096];
949 size_t cch = DISFormatYasmEx(&CpuState, szTmp, sizeof(szTmp),
950 DIS_FMT_FLAGS_STRICT
951 | DIS_FMT_FLAGS_BYTES_RIGHT | DIS_FMT_FLAGS_BYTES_COMMENT | DIS_FMT_FLAGS_BYTES_SPACED,
952 NULL, NULL);
953 cch = disHandleYasmDifferences(&CpuState, uFlatAddr, cbInstr, szTmp, sizeof(szTmp), cch);
954 Assert(cch < sizeof(szTmp));
955
956 if (g_cVerbose > 1)
957 {
958 while (cch < 72)
959 szTmp[cch++] = ' ';
960 RTStrPrintf(&szTmp[cch], sizeof(szTmp) - cch, "; %#x", uFlatAddr);
961 }
962
963 if (!outputPrintf(" %s\n", szTmp))
964 return false;
965 cb -= cbInstr;
966 pb += cbInstr;
967 uFlatAddr += cbInstr;
968 }
969 else
970 {
971 if (!disByteData(uFlatAddr, 1))
972 return false;
973 cb--;
974 pb++;
975 uFlatAddr++;
976 }
977 }
978 }
979 return true;
980}
981
982
983static bool disCodeSegment(uint32_t iSeg)
984{
985 uint32_t uFlatAddr = g_aSegs[iSeg].uFlatAddr;
986 uint32_t cb = g_aSegs[iSeg].cb;
987
988 while (cb > 0)
989 {
990 uint32_t off;
991 RTDBGSYMBOL Sym;
992 disGetNextSymbol(uFlatAddr, cb, &off, &Sym);
993
994 if (off > 0)
995 {
996 if (!disByteData(uFlatAddr, off))
997 return false;
998 cb -= off;
999 uFlatAddr += off;
1000 off = 0;
1001 if (!cb)
1002 break;
1003 }
1004
1005 bool fRc;
1006 if (off == 0)
1007 {
1008 size_t cchName = strlen(Sym.szName);
1009 fRc = outputPrintf("%s: %*s; %#x LB %#x\n", Sym.szName, cchName < 41 - 2 ? cchName - 41 - 2 : 0, "", uFlatAddr, Sym.cb);
1010 if (!fRc)
1011 return false;
1012
1013 if (disIsCodeAndAdjustSize(uFlatAddr, &Sym, &g_aSegs[iSeg]))
1014 fRc = disCode(uFlatAddr, Sym.cb, disIs16BitCode(Sym.szName));
1015 else
1016 fRc = disByteData(uFlatAddr, Sym.cb);
1017
1018 uFlatAddr += Sym.cb;
1019 cb -= Sym.cb;
1020 }
1021 else
1022 {
1023 fRc = disByteData(uFlatAddr, cb);
1024 uFlatAddr += cb;
1025 cb = 0;
1026 }
1027 if (!fRc)
1028 return false;
1029 }
1030
1031 return true;
1032}
1033
1034
1035static RTEXITCODE DisassembleBiosImage(void)
1036{
1037 if (!disFileHeader())
1038 return RTEXITCODE_FAILURE;
1039
1040 /*
1041 * Work the image segment by segment.
1042 */
1043 bool fRc = true;
1044 uint32_t uFlatAddr = VBOX_BIOS_BASE;
1045 for (uint32_t iSeg = 0; iSeg < g_cSegs && fRc; iSeg++)
1046 {
1047 /* Is there a gap between the segments? */
1048 if (uFlatAddr < g_aSegs[iSeg].uFlatAddr)
1049 {
1050 fRc = disCopySegmentGap(uFlatAddr, g_aSegs[iSeg].uFlatAddr - uFlatAddr);
1051 if (!fRc)
1052 break;
1053 uFlatAddr = g_aSegs[iSeg].uFlatAddr;
1054 }
1055 else if (uFlatAddr > g_aSegs[iSeg].uFlatAddr)
1056 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Overlapping segments: %u and %u; uFlatAddr=%#x\n", iSeg - 1, iSeg, uFlatAddr);
1057
1058 /* Disassemble the segment. */
1059 fRc = outputPrintf("\n"
1060 "section %s progbits vstart=%#x align=1 ; size=%#x class=%s group=%s\n",
1061 g_aSegs[iSeg].szName, g_aSegs[iSeg].uFlatAddr - VBOX_BIOS_BASE,
1062 g_aSegs[iSeg].cb, g_aSegs[iSeg].szClass, g_aSegs[iSeg].szGroup);
1063 if (!fRc)
1064 return RTEXITCODE_FAILURE;
1065 if (!strcmp(g_aSegs[iSeg].szName, "CONST"))
1066 fRc = disConstSegment(iSeg);
1067 else if (!strcmp(g_aSegs[iSeg].szClass, "DATA"))
1068 fRc = disDataSegment(iSeg);
1069 else
1070 fRc = disCodeSegment(iSeg);
1071
1072 /* Advance. */
1073 uFlatAddr += g_aSegs[iSeg].cb;
1074 }
1075
1076 /* Final gap. */
1077 if (uFlatAddr < VBOX_BIOS_BASE + _64K)
1078 fRc = disCopySegmentGap(uFlatAddr, VBOX_BIOS_BASE + _64K - uFlatAddr);
1079 else if (uFlatAddr > VBOX_BIOS_BASE + _64K)
1080 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Last segment spills beyond 1MB; uFlatAddr=%#x\n", uFlatAddr);
1081
1082 if (!fRc)
1083 return RTEXITCODE_FAILURE;
1084 return RTEXITCODE_SUCCESS;
1085}
1086
1087
1088
1089/**
1090 * Parses the symbol file for the BIOS.
1091 *
1092 * This is in ELF/DWARF format.
1093 *
1094 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1095 * @param pszBiosSym Path to the sym file.
1096 */
1097static RTEXITCODE ParseSymFile(const char *pszBiosSym)
1098{
1099#if 1
1100 /** @todo use RTDbg* later. (Just checking for existance currently.) */
1101 PRTSTREAM hStrm;
1102 int rc = RTStrmOpen(pszBiosSym, "rb", &hStrm);
1103 if (RT_FAILURE(rc))
1104 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosSym, rc);
1105 RTStrmClose(hStrm);
1106#else
1107 RTDBGMOD hDbgMod;
1108 int rc = RTDbgModCreateFromImage(&hDbgMod, pszBiosSym, "VBoxBios", 0 /*fFlags*/);
1109 RTMsgInfo("RTDbgModCreateFromImage -> %Rrc\n", rc);
1110#endif
1111 return RTEXITCODE_SUCCESS;
1112}
1113
1114
1115/**
1116 * Display an error with the mapfile name and current line, return false.
1117 *
1118 * @returns @c false.
1119 * @param pMap The map file handle.
1120 * @param pszFormat The format string.
1121 * @param ... Format arguments.
1122 */
1123static bool mapError(PBIOSMAP pMap, const char *pszFormat, ...)
1124{
1125 va_list va;
1126 va_start(va, pszFormat);
1127 RTMsgError("%s:%d: %N", pMap->pszMapFile, pMap->iLine, pszFormat, va);
1128 va_end(va);
1129 return false;
1130}
1131
1132
1133/**
1134 * Reads a line from the file.
1135 *
1136 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1137 * @param pMap The map file handle.
1138 */
1139static bool mapReadLine(PBIOSMAP pMap)
1140{
1141 int rc = RTStrmGetLine(pMap->hStrm, pMap->szLine, sizeof(pMap->szLine));
1142 if (RT_FAILURE(rc))
1143 {
1144 if (rc == VERR_EOF)
1145 {
1146 pMap->fEof = true;
1147 pMap->cch = 0;
1148 pMap->offNW = 0;
1149 pMap->szLine[0] = '\0';
1150 }
1151 else
1152 RTMsgError("%s:%d: Read error %Rrc", pMap->pszMapFile, pMap->iLine + 1, rc);
1153 return false;
1154 }
1155 pMap->iLine++;
1156 pMap->cch = (uint32_t)strlen(pMap->szLine);
1157
1158 /* Check out leading white space. */
1159 if (!RT_C_IS_SPACE(pMap->szLine[0]))
1160 pMap->offNW = 0;
1161 else
1162 {
1163 uint32_t off = 1;
1164 while (RT_C_IS_SPACE(pMap->szLine[off]))
1165 off++;
1166 pMap->offNW = off;
1167 }
1168
1169 return true;
1170}
1171
1172
1173/**
1174 * Checks if it is an empty line.
1175 * @returns @c true if empty, @c false if not.
1176 * @param pMap The map file handle.
1177 */
1178static bool mapIsEmptyLine(PBIOSMAP pMap)
1179{
1180 Assert(pMap->offNW <= pMap->cch);
1181 return pMap->offNW == pMap->cch;
1182}
1183
1184
1185/**
1186 * Reads ahead in the map file until a non-empty line or EOF is encountered.
1187 *
1188 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1189 * @param pMap The map file handle.
1190 */
1191static bool mapSkipEmptyLines(PBIOSMAP pMap)
1192{
1193 for (;;)
1194 {
1195 if (!mapReadLine(pMap))
1196 return false;
1197 if (pMap->offNW < pMap->cch)
1198 return true;
1199 }
1200}
1201
1202
1203/**
1204 * Reads ahead in the map file until an empty line or EOF is encountered.
1205 *
1206 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1207 * @param pMap The map file handle.
1208 */
1209static bool mapSkipNonEmptyLines(PBIOSMAP pMap)
1210{
1211 for (;;)
1212 {
1213 if (!mapReadLine(pMap))
1214 return false;
1215 if (pMap->offNW == pMap->cch)
1216 return true;
1217 }
1218}
1219
1220
1221/**
1222 * Strips the current line.
1223 *
1224 * The string length may change.
1225 *
1226 * @returns Pointer to the first non-space character.
1227 * @param pMap The map file handle.
1228 * @param pcch Where to return the length of the unstripped
1229 * part. Optional.
1230 */
1231static char *mapStripCurrentLine(PBIOSMAP pMap, size_t *pcch)
1232{
1233 char *psz = &pMap->szLine[pMap->offNW];
1234 char *pszEnd = &pMap->szLine[pMap->cch];
1235 while ( (uintptr_t)pszEnd > (uintptr_t)psz
1236 && RT_C_IS_SPACE(pszEnd[-1]))
1237 {
1238 *--pszEnd = '\0';
1239 pMap->cch--;
1240 }
1241 if (pcch)
1242 *pcch = pszEnd - psz;
1243 return psz;
1244}
1245
1246
1247/**
1248 * Reads a line from the file and right strips it.
1249 *
1250 * @returns Pointer to szLine on success, @c NULL + msg on failure, @c NULL on
1251 * EOF.
1252 * @param pMap The map file handle.
1253 * @param pcch Where to return the length of the unstripped
1254 * part. Optional.
1255 */
1256static char *mapReadLineStripRight(PBIOSMAP pMap, size_t *pcch)
1257{
1258 if (!mapReadLine(pMap))
1259 return NULL;
1260 mapStripCurrentLine(pMap, NULL);
1261 if (pcch)
1262 *pcch = pMap->cch;
1263 return pMap->szLine;
1264}
1265
1266
1267/**
1268 * mapReadLine() + mapStripCurrentLine().
1269 *
1270 * @returns Pointer to the first non-space character in the new line. NULL on
1271 * read error (bitched already) or end of file.
1272 * @param pMap The map file handle.
1273 * @param pcch Where to return the length of the unstripped
1274 * part. Optional.
1275 */
1276static char *mapReadLineStrip(PBIOSMAP pMap, size_t *pcch)
1277{
1278 if (!mapReadLine(pMap))
1279 return NULL;
1280 return mapStripCurrentLine(pMap, pcch);
1281}
1282
1283
1284/**
1285 * Parses a word, copying it into the supplied buffer, and skipping any spaces
1286 * following it.
1287 *
1288 * @returns @c true on success, @c false on failure.
1289 * @param ppszCursor Pointer to the cursor variable.
1290 * @param pszBuf The output buffer.
1291 * @param cbBuf The size of the output buffer.
1292 */
1293static bool mapParseWord(char **ppszCursor, char *pszBuf, size_t cbBuf)
1294{
1295 /* Check that we start on a non-blank. */
1296 char *pszStart = *ppszCursor;
1297 if (!*pszStart || RT_C_IS_SPACE(*pszStart))
1298 return false;
1299
1300 /* Find the end of the word. */
1301 char *psz = pszStart + 1;
1302 while (*psz && !RT_C_IS_SPACE(*psz))
1303 psz++;
1304
1305 /* Copy it. */
1306 size_t cchWord = (uintptr_t)psz - (uintptr_t)pszStart;
1307 if (cchWord >= cbBuf)
1308 return false;
1309 memcpy(pszBuf, pszStart, cchWord);
1310 pszBuf[cchWord] = '\0';
1311
1312 /* Skip blanks following it. */
1313 while (RT_C_IS_SPACE(*psz))
1314 psz++;
1315 *ppszCursor = psz;
1316 return true;
1317}
1318
1319
1320/**
1321 * Parses an 16:16 address.
1322 *
1323 * @returns @c true on success, @c false on failure.
1324 * @param ppszCursor Pointer to the cursor variable.
1325 * @param pAddr Where to return the address.
1326 */
1327static bool mapParseAddress(char **ppszCursor, PRTFAR16 pAddr)
1328{
1329 char szWord[32];
1330 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1331 return false;
1332 size_t cchWord = strlen(szWord);
1333
1334 /* An address is at least 16:16 format. It may be 16:32. It may also be flagged. */
1335 size_t cchAddr = 4 + 1 + 4;
1336 if (cchWord < cchAddr)
1337 return false;
1338 if ( !RT_C_IS_XDIGIT(szWord[0])
1339 || !RT_C_IS_XDIGIT(szWord[1])
1340 || !RT_C_IS_XDIGIT(szWord[2])
1341 || !RT_C_IS_XDIGIT(szWord[3])
1342 || szWord[4] != ':'
1343 || !RT_C_IS_XDIGIT(szWord[5])
1344 || !RT_C_IS_XDIGIT(szWord[6])
1345 || !RT_C_IS_XDIGIT(szWord[7])
1346 || !RT_C_IS_XDIGIT(szWord[8])
1347 )
1348 return false;
1349 if ( cchWord > cchAddr
1350 && RT_C_IS_XDIGIT(szWord[9])
1351 && RT_C_IS_XDIGIT(szWord[10])
1352 && RT_C_IS_XDIGIT(szWord[11])
1353 && RT_C_IS_XDIGIT(szWord[12]))
1354 cchAddr += 4;
1355
1356 /* Drop flag if present. */
1357 if (cchWord > cchAddr)
1358 {
1359 if (RT_C_IS_XDIGIT(szWord[4+1+4]))
1360 return false;
1361 szWord[cchAddr] = '\0';
1362 cchWord = cchAddr;
1363 }
1364
1365 /* Convert it. */
1366 szWord[4] = '\0';
1367 int rc1 = RTStrToUInt16Full(szWord, 16, &pAddr->sel);
1368 if (rc1 != VINF_SUCCESS)
1369 return false;
1370
1371 int rc2 = RTStrToUInt16Full(szWord + 5, 16, &pAddr->off);
1372 if (rc2 != VINF_SUCCESS)
1373 return false;
1374 return true;
1375}
1376
1377
1378/**
1379 * Parses a size.
1380 *
1381 * @returns @c true on success, @c false on failure.
1382 * @param ppszCursor Pointer to the cursor variable.
1383 * @param pcb Where to return the size.
1384 */
1385static bool mapParseSize(char **ppszCursor, uint32_t *pcb)
1386{
1387 char szWord[32];
1388 if (!mapParseWord(ppszCursor, szWord, sizeof(szWord)))
1389 return false;
1390 size_t cchWord = strlen(szWord);
1391 if (cchWord != 8)
1392 return false;
1393
1394 int rc = RTStrToUInt32Full(szWord, 16, pcb);
1395 if (rc != VINF_SUCCESS)
1396 return false;
1397 return true;
1398}
1399
1400
1401/**
1402 * Parses a section box and the following column header.
1403 *
1404 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1405 * @param pMap Map file handle.
1406 * @param pszSectionNm The expected section name.
1407 * @param cColumns The number of columns.
1408 * @param ... The column names.
1409 */
1410static bool mapSkipThruColumnHeadings(PBIOSMAP pMap, const char *pszSectionNm, uint32_t cColumns, ...)
1411{
1412 if ( mapIsEmptyLine(pMap)
1413 && !mapSkipEmptyLines(pMap))
1414 return false;
1415
1416 /* +------------+ */
1417 size_t cch;
1418 char *psz = mapStripCurrentLine(pMap, &cch);
1419 if (!psz)
1420 return false;
1421
1422 if ( psz[0] != '+'
1423 || psz[1] != '-'
1424 || psz[2] != '-'
1425 || psz[3] != '-'
1426 || psz[cch - 4] != '-'
1427 || psz[cch - 3] != '-'
1428 || psz[cch - 2] != '-'
1429 || psz[cch - 1] != '+'
1430 )
1431 {
1432 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1433 return false;
1434 }
1435
1436 /* | pszSectionNm | */
1437 psz = mapReadLineStrip(pMap, &cch);
1438 if (!psz)
1439 return false;
1440
1441 size_t cchSectionNm = strlen(pszSectionNm);
1442 if ( psz[0] != '|'
1443 || psz[1] != ' '
1444 || psz[2] != ' '
1445 || psz[3] != ' '
1446 || psz[cch - 4] != ' '
1447 || psz[cch - 3] != ' '
1448 || psz[cch - 2] != ' '
1449 || psz[cch - 1] != '|'
1450 || cch != 1 + 3 + cchSectionNm + 3 + 1
1451 || strncmp(&psz[4], pszSectionNm, cchSectionNm)
1452 )
1453 {
1454 RTMsgError("%s:%d: Expected section box: | %s |", pMap->pszMapFile, pMap->iLine, pszSectionNm);
1455 return false;
1456 }
1457
1458 /* +------------+ */
1459 psz = mapReadLineStrip(pMap, &cch);
1460 if (!psz)
1461 return false;
1462 if ( psz[0] != '+'
1463 || psz[1] != '-'
1464 || psz[2] != '-'
1465 || psz[3] != '-'
1466 || psz[cch - 4] != '-'
1467 || psz[cch - 3] != '-'
1468 || psz[cch - 2] != '-'
1469 || psz[cch - 1] != '+'
1470 )
1471 {
1472 RTMsgError("%s:%d: Expected section box: +-----...", pMap->pszMapFile, pMap->iLine);
1473 return false;
1474 }
1475
1476 /* There may be a few lines describing the table notation now, surrounded by blank lines. */
1477 do
1478 {
1479 psz = mapReadLineStripRight(pMap, &cch);
1480 if (!psz)
1481 return false;
1482 } while ( *psz == '\0'
1483 || ( !RT_C_IS_SPACE(psz[0])
1484 && RT_C_IS_SPACE(psz[1])
1485 && psz[2] == '='
1486 && RT_C_IS_SPACE(psz[3]))
1487 );
1488
1489 /* Should have the column heading now. */
1490 va_list va;
1491 va_start(va, cColumns);
1492 for (uint32_t i = 0; i < cColumns; i++)
1493 {
1494 const char *pszColumn = va_arg(va, const char *);
1495 size_t cchColumn = strlen(pszColumn);
1496 if ( strncmp(psz, pszColumn, cchColumn)
1497 || ( psz[cchColumn] != '\0'
1498 && !RT_C_IS_SPACE(psz[cchColumn])))
1499 {
1500 va_end(va);
1501 RTMsgError("%s:%d: Expected column '%s' found '%s'", pMap->pszMapFile, pMap->iLine, pszColumn, psz);
1502 return false;
1503 }
1504 psz += cchColumn;
1505 while (RT_C_IS_SPACE(*psz))
1506 psz++;
1507 }
1508 va_end(va);
1509
1510 /* The next line is the underlining. */
1511 psz = mapReadLineStripRight(pMap, &cch);
1512 if (!psz)
1513 return false;
1514 if (*psz != '=' || psz[cch - 1] != '=')
1515 {
1516 RTMsgError("%s:%d: Expected column header underlining", pMap->pszMapFile, pMap->iLine);
1517 return false;
1518 }
1519
1520 /* Skip one blank line. */
1521 psz = mapReadLineStripRight(pMap, &cch);
1522 if (!psz)
1523 return false;
1524 if (*psz)
1525 {
1526 RTMsgError("%s:%d: Expected blank line beneath the column headers", pMap->pszMapFile, pMap->iLine);
1527 return false;
1528 }
1529
1530 return true;
1531}
1532
1533
1534/**
1535 * Parses a segment list.
1536 *
1537 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1538 * @param pMap The map file handle.
1539 */
1540static bool mapParseSegments(PBIOSMAP pMap)
1541{
1542 for (;;)
1543 {
1544 if (!mapReadLineStripRight(pMap, NULL))
1545 return false;
1546
1547 /* The end? The line should be empty. Expectes segment name to not
1548 start with a space. */
1549 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1550 {
1551 if (!pMap->szLine[0])
1552 return true;
1553 RTMsgError("%s:%u: Malformed segment line", pMap->pszMapFile, pMap->iLine);
1554 return false;
1555 }
1556
1557 /* Parse the segment line. */
1558 uint32_t iSeg = g_cSegs;
1559 if (iSeg >= RT_ELEMENTS(g_aSegs))
1560 {
1561 RTMsgError("%s:%u: Too many segments", pMap->pszMapFile, pMap->iLine);
1562 return false;
1563 }
1564
1565 char *psz = pMap->szLine;
1566 if (!mapParseWord(&psz, g_aSegs[iSeg].szName, sizeof(g_aSegs[iSeg].szName)))
1567 RTMsgError("%s:%u: Segment name parser error", pMap->pszMapFile, pMap->iLine);
1568 else if (!mapParseWord(&psz, g_aSegs[iSeg].szClass, sizeof(g_aSegs[iSeg].szClass)))
1569 RTMsgError("%s:%u: Segment class parser error", pMap->pszMapFile, pMap->iLine);
1570 else if (!mapParseWord(&psz, g_aSegs[iSeg].szGroup, sizeof(g_aSegs[iSeg].szGroup)))
1571 RTMsgError("%s:%u: Segment group parser error", pMap->pszMapFile, pMap->iLine);
1572 else if (!mapParseAddress(&psz, &g_aSegs[iSeg].Address))
1573 RTMsgError("%s:%u: Segment address parser error", pMap->pszMapFile, pMap->iLine);
1574 else if (!mapParseSize(&psz, &g_aSegs[iSeg].cb))
1575 RTMsgError("%s:%u: Segment size parser error", pMap->pszMapFile, pMap->iLine);
1576 else
1577 {
1578 g_aSegs[iSeg].uFlatAddr = ((uint32_t)g_aSegs[iSeg].Address.sel << 4) + g_aSegs[iSeg].Address.off;
1579 g_cSegs++;
1580 if (g_cVerbose > 2)
1581 RTStrmPrintf(g_pStdErr, "read segment at %08x / %04x:%04x LB %04x %s / %s / %s\n",
1582 g_aSegs[iSeg].uFlatAddr,
1583 g_aSegs[iSeg].Address.sel,
1584 g_aSegs[iSeg].Address.off,
1585 g_aSegs[iSeg].cb,
1586 g_aSegs[iSeg].szName,
1587 g_aSegs[iSeg].szClass,
1588 g_aSegs[iSeg].szGroup);
1589
1590 while (RT_C_IS_SPACE(*psz))
1591 psz++;
1592 if (!*psz)
1593 continue;
1594 RTMsgError("%s:%u: Junk at end of line", pMap->pszMapFile, pMap->iLine);
1595 }
1596 return false;
1597 }
1598}
1599
1600
1601/**
1602 * Sorts the segment array by flat address and adds them to the debug module.
1603 *
1604 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1605 */
1606static bool mapSortAndAddSegments(void)
1607{
1608 for (uint32_t i = 0; i < g_cSegs; i++)
1609 {
1610 for (uint32_t j = i + 1; j < g_cSegs; j++)
1611 if (g_aSegs[j].uFlatAddr < g_aSegs[i].uFlatAddr)
1612 {
1613 BIOSSEG Tmp = g_aSegs[i];
1614 g_aSegs[i] = g_aSegs[j];
1615 g_aSegs[j] = Tmp;
1616 }
1617 if (g_cVerbose > 0)
1618 RTStrmPrintf(g_pStdErr, "segment at %08x / %04x:%04x LB %04x %s / %s / %s\n",
1619 g_aSegs[i].uFlatAddr,
1620 g_aSegs[i].Address.sel,
1621 g_aSegs[i].Address.off,
1622 g_aSegs[i].cb,
1623 g_aSegs[i].szName,
1624 g_aSegs[i].szClass,
1625 g_aSegs[i].szGroup);
1626
1627 RTDBGSEGIDX idx = i;
1628 int rc = RTDbgModSegmentAdd(g_hMapMod, g_aSegs[i].uFlatAddr, g_aSegs[i].cb, g_aSegs[i].szName, 0 /*fFlags*/, &idx);
1629 if (RT_FAILURE(rc))
1630 {
1631 RTMsgError("RTDbgModSegmentAdd failed on %s: %Rrc", g_aSegs[i].szName);
1632 return false;
1633 }
1634 }
1635 return true;
1636}
1637
1638
1639/**
1640 * Parses a segment list.
1641 *
1642 * @returns @c true on success, @c false + msg on failure, @c false on eof.
1643 * @param pMap The map file handle.
1644 */
1645static bool mapParseSymbols(PBIOSMAP pMap)
1646{
1647 for (;;)
1648 {
1649 if (!mapReadLineStripRight(pMap, NULL))
1650 return false;
1651
1652 /* The end? The line should be empty. Expectes segment name to not
1653 start with a space. */
1654 if (!pMap->szLine[0] || RT_C_IS_SPACE(pMap->szLine[0]))
1655 {
1656 if (!pMap->szLine[0])
1657 return true;
1658 return mapError(pMap, "Malformed symbol line");
1659 }
1660
1661 if (!strncmp(pMap->szLine, RT_STR_TUPLE("Module: ")))
1662 {
1663 /* Parse the module line. */
1664 size_t offObj = sizeof("Module: ") - 1;
1665 while (RT_C_IS_SPACE(pMap->szLine[offObj]))
1666 offObj++;
1667 size_t offSrc = offObj;
1668 char ch;
1669 while ((ch = pMap->szLine[offSrc]) != '(' && ch != '\0')
1670 offSrc++;
1671 size_t cchObj = offSrc - offObj;
1672
1673 offSrc++;
1674 size_t cchSrc = offSrc;
1675 while ((ch = pMap->szLine[cchSrc]) != ')' && ch != '\0')
1676 cchSrc++;
1677 cchSrc -= offSrc;
1678 if (ch != ')')
1679 return mapError(pMap, "Symbol/Module line parse error");
1680
1681 PBIOSOBJFILE pObjFile = (PBIOSOBJFILE)RTMemAllocZ(sizeof(*pObjFile) + cchSrc + cchObj + 2);
1682 if (!pObjFile)
1683 return mapError(pMap, "Out of memory");
1684 char *psz = (char *)(pObjFile + 1);
1685 pObjFile->pszObject = psz;
1686 memcpy(psz, &pMap->szLine[offObj], cchObj);
1687 psz += cchObj;
1688 *psz++ = '\0';
1689 pObjFile->pszSource = psz;
1690 memcpy(psz, &pMap->szLine[offSrc], cchSrc);
1691 psz[cchSrc] = '\0';
1692 RTListAppend(&g_ObjList, &pObjFile->Node);
1693 }
1694 else
1695 {
1696 /* Parse the segment line. */
1697 RTFAR16 Addr;
1698 char *psz = pMap->szLine;
1699 if (!mapParseAddress(&psz, &Addr))
1700 return mapError(pMap, "Symbol address parser error");
1701
1702 char szName[4096];
1703 if (!mapParseWord(&psz, szName, sizeof(szName)))
1704 return mapError(pMap, "Symbol name parser error");
1705
1706 uint32_t uFlatAddr = ((uint32_t)Addr.sel << 4) + Addr.off;
1707 if (uFlatAddr != 0)
1708 {
1709 int rc = RTDbgModSymbolAdd(g_hMapMod, szName, RTDBGSEGIDX_RVA, uFlatAddr, 0 /*cb*/, 0 /*fFlags*/, NULL);
1710 if (RT_FAILURE(rc) && rc != VERR_DBG_ADDRESS_CONFLICT)
1711 return mapError(pMap, "RTDbgModSymbolAdd failed: %Rrc", rc);
1712
1713 if (g_cVerbose > 2)
1714 RTStrmPrintf(g_pStdErr, "read symbol - %08x %s\n", uFlatAddr, szName);
1715 while (RT_C_IS_SPACE(*psz))
1716 psz++;
1717 if (*psz)
1718 return mapError(pMap, "Junk at end of line");
1719 }
1720
1721 }
1722 }
1723}
1724
1725
1726/**
1727 * Parses the given map file.
1728 *
1729 * @returns RTEXITCODE_SUCCESS and lots of globals, or RTEXITCODE_FAILURE and a
1730 * error message.
1731 * @param pMap The map file handle.
1732 */
1733static RTEXITCODE mapParseFile(PBIOSMAP pMap)
1734{
1735 int rc = RTDbgModCreate(&g_hMapMod, "VBoxBios", 0 /*cbSeg*/, 0 /*fFlags*/);
1736 if (RT_FAILURE(rc))
1737 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDbgModCreate failed: %Rrc", rc);
1738
1739 /*
1740 * Read the header.
1741 */
1742 if (!mapReadLine(pMap))
1743 return RTEXITCODE_FAILURE;
1744 if (strncmp(pMap->szLine, RT_STR_TUPLE("Open Watcom Linker Version")))
1745 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected map-file header: '%s'", pMap->szLine);
1746 if ( !mapSkipNonEmptyLines(pMap)
1747 || !mapSkipEmptyLines(pMap))
1748 return RTEXITCODE_FAILURE;
1749
1750 /*
1751 * Skip groups.
1752 */
1753 if (!mapSkipThruColumnHeadings(pMap, "Groups", 3, "Group", "Address", "Size", NULL))
1754 return RTEXITCODE_FAILURE;
1755 if (!mapSkipNonEmptyLines(pMap))
1756 return RTEXITCODE_FAILURE;
1757
1758 /*
1759 * Parse segments.
1760 */
1761 if (!mapSkipThruColumnHeadings(pMap, "Segments", 5, "Segment", "Class", "Group", "Address", "Size"))
1762 return RTEXITCODE_FAILURE;
1763 if (!mapParseSegments(pMap))
1764 return RTEXITCODE_FAILURE;
1765 if (!mapSortAndAddSegments())
1766 return RTEXITCODE_FAILURE;
1767
1768 /*
1769 * Parse symbols.
1770 */
1771 if (!mapSkipThruColumnHeadings(pMap, "Memory Map", 2, "Address", "Symbol"))
1772 return RTEXITCODE_FAILURE;
1773 if (!mapParseSymbols(pMap))
1774 return RTEXITCODE_FAILURE;
1775
1776 /* Ignore the rest of the file. */
1777 return RTEXITCODE_SUCCESS;
1778}
1779
1780
1781/**
1782 * Parses the linker map file for the BIOS.
1783 *
1784 * This is generated by the Watcom linker.
1785 *
1786 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1787 * @param pszBiosMap Path to the map file.
1788 */
1789static RTEXITCODE ParseMapFile(const char *pszBiosMap)
1790{
1791 BIOSMAP Map;
1792 Map.pszMapFile = pszBiosMap;
1793 Map.hStrm = NULL;
1794 Map.iLine = 0;
1795 Map.fEof = false;
1796 Map.cch = 0;
1797 Map.offNW = 0;
1798 int rc = RTStrmOpen(pszBiosMap, "r", &Map.hStrm);
1799 if (RT_FAILURE(rc))
1800 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error opening '%s': %Rrc", pszBiosMap, rc);
1801 RTEXITCODE rcExit = mapParseFile(&Map);
1802 RTStrmClose(Map.hStrm);
1803 return rcExit;
1804}
1805
1806
1807/**
1808 * Reads the BIOS image into memory (g_pbImg and g_cbImg).
1809 *
1810 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1811 * @param pszBiosImg Path to the image file.
1812 */
1813static RTEXITCODE ReadBiosImage(const char *pszBiosImg)
1814{
1815 void *pvImg;
1816 size_t cbImg;
1817 int rc = RTFileReadAll(pszBiosImg, &pvImg, &cbImg);
1818 if (RT_FAILURE(rc))
1819 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Error reading '%s': %Rrc", pszBiosImg, rc);
1820 if (cbImg != _64K)
1821 {
1822 RTFileReadAllFree(pvImg, cbImg);
1823 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The BIOS image %u bytes intead of 64KB", cbImg);
1824 }
1825
1826 g_pbImg = (uint8_t *)pvImg;
1827 g_cbImg = cbImg;
1828 return RTEXITCODE_SUCCESS;
1829}
1830
1831
1832int main(int argc, char **argv)
1833{
1834 int rc = RTR3InitExe(argc, &argv, 0);
1835 if (RT_FAILURE(rc))
1836 return RTMsgInitFailure(rc);
1837
1838 RTListInit(&g_ObjList);
1839
1840 /*
1841 * Option config.
1842 */
1843 static RTGETOPTDEF const s_aOpts[] =
1844 {
1845 { "--bios-image", 'i', RTGETOPT_REQ_STRING },
1846 { "--bios-map", 'm', RTGETOPT_REQ_STRING },
1847 { "--bios-sym", 's', RTGETOPT_REQ_STRING },
1848 { "--output", 'o', RTGETOPT_REQ_STRING },
1849 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1850 { "--quiet", 'q', RTGETOPT_REQ_NOTHING },
1851 };
1852
1853 const char *pszBiosMap = NULL;
1854 const char *pszBiosSym = NULL;
1855 const char *pszBiosImg = NULL;
1856 const char *pszOutput = NULL;
1857
1858 RTGETOPTUNION ValueUnion;
1859 RTGETOPTSTATE GetOptState;
1860 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1861 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
1862
1863 /*
1864 * Process the options.
1865 */
1866 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1867 {
1868 switch (rc)
1869 {
1870 case 'i':
1871 if (pszBiosImg)
1872 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is given more than once");
1873 pszBiosImg = ValueUnion.psz;
1874 break;
1875
1876 case 'm':
1877 if (pszBiosMap)
1878 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is given more than once");
1879 pszBiosMap = ValueUnion.psz;
1880 break;
1881
1882 case 's':
1883 if (pszBiosSym)
1884 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is given more than once");
1885 pszBiosSym = ValueUnion.psz;
1886 break;
1887
1888 case 'o':
1889 if (pszOutput)
1890 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--output is given more than once");
1891 pszOutput = ValueUnion.psz;
1892 break;
1893
1894 case 'v':
1895 g_cVerbose++;
1896 break;
1897
1898 case 'q':
1899 g_cVerbose = 0;
1900 break;
1901
1902 case 'H':
1903 RTPrintf("usage: %Rbn --bios-image <file.img> --bios-map <file.map> [--output <file.asm>]\n",
1904 argv[0]);
1905 return RTEXITCODE_SUCCESS;
1906
1907 case 'V':
1908 {
1909 /* The following is assuming that svn does it's job here. */
1910 RTPrintf("r%u\n", RTBldCfgRevision());
1911 return RTEXITCODE_SUCCESS;
1912 }
1913
1914 default:
1915 return RTGetOptPrintError(rc, &ValueUnion);
1916 }
1917 }
1918
1919 /*
1920 * Got it all?
1921 */
1922 if (!pszBiosImg)
1923 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-image is required");
1924 if (!pszBiosMap)
1925 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-map is required");
1926 if (!pszBiosSym)
1927 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--bios-sym is required");
1928
1929 /*
1930 * Do the job.
1931 */
1932 RTEXITCODE rcExit;
1933 rcExit = ReadBiosImage(pszBiosImg);
1934 if (rcExit == RTEXITCODE_SUCCESS)
1935 rcExit = ParseMapFile(pszBiosMap);
1936 if (rcExit == RTEXITCODE_SUCCESS)
1937 rcExit = ParseSymFile(pszBiosSym);
1938 if (rcExit == RTEXITCODE_SUCCESS)
1939 rcExit = OpenOutputFile(pszOutput);
1940 if (rcExit == RTEXITCODE_SUCCESS)
1941 rcExit = DisassembleBiosImage();
1942
1943 return rcExit;
1944}
1945
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette