VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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