VirtualBox

source: vbox/trunk/src/VBox/Devices/BiosCommonCode/MakeAlternativeSource.cpp@ 42147

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

Share kBuild template for the VGA and System BIOSes as well as the alternative source generator code.

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