VirtualBox

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

Last change on this file since 105381 was 103005, checked in by vboxsync, 10 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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