VirtualBox

source: vbox/trunk/src/VBox/ValidationKit/bootsectors/bs3kit/VBoxBs3Linker.cpp@ 97574

Last change on this file since 97574 was 97574, checked in by vboxsync, 2 years ago

ValKit/bs3kit: Detect 320KB and 360KB image sizes and pad them too to make sure the FDC doesn't think they are 5.25" floppy images. bugref:9898

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/* $Id: VBoxBs3Linker.cpp 97574 2022-11-16 22:38:34Z vboxsync $ */
2/** @file
3 * VirtualBox Validation Kit - Boot Sector 3 "linker".
4 */
5
6/*
7 * Copyright (C) 2006-2022 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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <stdio.h>
42#include <string.h>
43#include <stdlib.h>
44#include <iprt/types.h>
45#include <iprt/assert.h>
46
47
48/*********************************************************************************************************************************
49* Structures and Typedefs *
50*********************************************************************************************************************************/
51#pragma pack(1)
52typedef struct BS3BOOTSECTOR
53{
54 uint8_t abJmp[3];
55 char abOemId[8];
56 /** @name EBPB, DOS 4.0 style.
57 * @{ */
58 uint16_t cBytesPerSector; /**< 00bh */
59 uint8_t cSectorsPerCluster; /**< 00dh */
60 uint16_t cReservedSectors; /**< 00eh */
61 uint8_t cFATs; /**< 010h */
62 uint16_t cRootDirEntries; /**< 011h */
63 uint16_t cTotalSectors; /**< 013h */
64 uint8_t bMediaDescriptor; /**< 015h */
65 uint16_t cSectorsPerFAT; /**< 016h */
66 uint16_t cPhysSectorsPerTrack; /**< 018h */
67 uint16_t cHeads; /**< 01ah */
68 uint32_t cHiddentSectors; /**< 01ch */
69 uint32_t cLargeTotalSectors; /**< 020h - We (ab)use this to indicate the number of sectors to load. */
70 uint8_t bBootDrv; /**< 024h */
71 uint8_t bFlagsEtc; /**< 025h */
72 uint8_t bExtendedSignature; /**< 026h */
73 uint32_t dwSerialNumber; /**< 027h */
74 char abLabel[11]; /**< 02bh */
75 char abFSType[8]; /**< 036h */
76 /** @} */
77} BS3BOOTSECTOR;
78#pragma pack()
79typedef BS3BOOTSECTOR *PBS3BOOTSECTOR;
80
81AssertCompileMemberOffset(BS3BOOTSECTOR, cLargeTotalSectors, 0x20);
82AssertCompileMemberOffset(BS3BOOTSECTOR, abLabel, 0x2b);
83AssertCompileMemberOffset(BS3BOOTSECTOR, abFSType, 0x36);
84
85#define BS3_OEMID "BS3Kit\n\n"
86#define BS3_FSTYPE "RawCode\n"
87#define BS3_LABEL "VirtualBox\n"
88#define BS3_MAX_SIZE UINT32_C(491520) /* 480KB */
89
90
91int main(int argc, char **argv)
92{
93 const char *pszOutput = NULL;
94 struct BS3LNKINPUT
95 {
96 const char *pszFile;
97 FILE *pFile;
98 uint32_t cbFile;
99 } *paInputs = (struct BS3LNKINPUT *)calloc(sizeof(paInputs[0]), argc);
100 unsigned cInputs = 0;
101 uint32_t cSectors = 0;
102
103 /*
104 * Scan the arguments.
105 */
106 for (int i = 1; i < argc; i++)
107 {
108 if (argv[i][0] == '-')
109 {
110 const char *pszOpt = &argv[i][1];
111 if (*pszOpt == '-')
112 {
113 /* Convert long options to short ones. */
114 pszOpt--;
115 if (!strcmp(pszOpt, "--output"))
116 pszOpt = "o";
117 else if (!strcmp(pszOpt, "--version"))
118 pszOpt = "V";
119 else if (!strcmp(pszOpt, "--help"))
120 pszOpt = "h";
121 else
122 {
123 fprintf(stderr, "syntax errro: Unknown options '%s'\n", pszOpt);
124 free(paInputs);
125 return 2;
126 }
127 }
128
129 /* Process the list of short options. */
130 while (*pszOpt)
131 {
132 switch (*pszOpt++)
133 {
134 case 'o':
135 {
136 const char *pszValue = pszOpt;
137 pszOpt = strchr(pszOpt, '\0');
138 if (*pszValue == '=')
139 pszValue++;
140 else if (!*pszValue)
141 {
142 if (i + 1 >= argc)
143 {
144 fprintf(stderr, "syntax error: The --output option expects a filename.\n");
145 free(paInputs);
146 return 12;
147 }
148 pszValue = argv[++i];
149 }
150 if (pszOutput)
151 {
152 fprintf(stderr, "Only one output file is allowed. You've specified '%s' and '%s'\n",
153 pszOutput, pszValue);
154 free(paInputs);
155 return 2;
156 }
157 pszOutput = pszValue;
158 pszOpt = "";
159 break;
160 }
161
162 case 'V':
163 printf("%s\n", "$Revision: 97574 $");
164 free(paInputs);
165 return 0;
166
167 case '?':
168 case 'h':
169 printf("usage: %s [options] -o <output> <input1> [input2 ... [inputN]]\n",
170 argv[0]);
171 free(paInputs);
172 return 0;
173 }
174 }
175 }
176 else
177 {
178 /*
179 * Add to input file collection.
180 */
181 paInputs[cInputs].pszFile = argv[i];
182#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
183 FILE *pFile = fopen(paInputs[cInputs].pszFile, "rb");
184#else
185 FILE *pFile = fopen(paInputs[cInputs].pszFile, "r");
186#endif
187 if (pFile)
188 {
189 if (fseek(pFile, 0, SEEK_END) == 0)
190 {
191 paInputs[cInputs].cbFile = (uint32_t)ftell(pFile);
192 if (fseek(pFile, 0, SEEK_SET) == 0)
193 {
194 if (cInputs != 0 || paInputs[cInputs].cbFile == 512)
195 {
196 cSectors += RT_ALIGN_32(paInputs[cInputs].cbFile, 512) / 512;
197 if (cSectors <= BS3_MAX_SIZE / 512)
198 {
199 if (cSectors > 0)
200 {
201 paInputs[cInputs].pFile = pFile;
202 pFile = NULL;
203 }
204 else
205 fprintf(stderr, "error: empty input file: '%s'\n", paInputs[cInputs].pszFile);
206 }
207 else
208 fprintf(stderr, "error: input is too big: %u bytes, %u sectors (max %u bytes, %u sectors)\n"
209 "info: detected loading '%s'\n",
210 cSectors * 512, cSectors, BS3_MAX_SIZE, BS3_MAX_SIZE / 512,
211 paInputs[cInputs].pszFile);
212 }
213 else
214 fprintf(stderr, "error: first input file (%s) must be exactly 512 bytes\n", paInputs[cInputs].pszFile);
215 }
216 else
217 fprintf(stderr, "error: seeking to start of '%s' failed\n", paInputs[cInputs].pszFile);
218 }
219 else
220 fprintf(stderr, "error: seeking to end of '%s' failed\n", paInputs[cInputs].pszFile);
221 }
222 else
223 fprintf(stderr, "error: Failed to open input file '%s' for reading\n", paInputs[cInputs].pszFile);
224 if (pFile)
225 {
226 free(paInputs);
227 return 1;
228 }
229 cInputs++;
230 }
231 }
232
233 if (!pszOutput)
234 {
235 fprintf(stderr, "syntax error: No output file was specified (-o or --output).\n");
236 free(paInputs);
237 return 2;
238 }
239 if (cInputs == 0)
240 {
241 fprintf(stderr, "syntax error: No input files was specified.\n");
242 free(paInputs);
243 return 2;
244 }
245
246 /*
247 * Do the job.
248 */
249 /* Open the output file. */
250#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
251 FILE *pOutput = fopen(pszOutput, "wb");
252#else
253 FILE *pOutput = fopen(pszOutput, "w");
254#endif
255 if (!pOutput)
256 {
257 fprintf(stderr, "error: Failed to open output file '%s' for writing\n", pszOutput);
258 free(paInputs);
259 return 1;
260 }
261
262 /* Copy the input files to the output file, with sector padding applied. */
263 int rcExit = 0;
264 size_t off = 0;
265 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
266 {
267 uint8_t abBuf[4096]; /* Must be multiple of 512! */
268 uint32_t cbToRead = paInputs[i].cbFile;
269 while (cbToRead > 0)
270 {
271 /* Read a block from the input file. */
272 uint32_t const cbThisRead = RT_MIN(cbToRead, sizeof(abBuf));
273 size_t cbRead = fread(abBuf, sizeof(uint8_t), cbThisRead, paInputs[i].pFile);
274 if (cbRead != cbThisRead)
275 {
276 fprintf(stderr, "error: Error reading '%s' (got %d bytes, wanted %u).\n",
277 paInputs[i].pszFile, (int)cbRead, (unsigned)cbThisRead);
278 rcExit = 1;
279 break;
280 }
281 cbToRead -= cbThisRead;
282
283 /* Padd the end of the file if necessary. */
284 if ((cbRead & 0x1ff) != 0)
285 {
286 memset(&abBuf[cbRead], 0, 4096 - cbRead);
287 cbRead = (cbRead + 0x1ff) & ~0x1ffU;
288 }
289
290 /* Patch the BPB of the first file. */
291 if (off == 0)
292 {
293 PBS3BOOTSECTOR pBs = (PBS3BOOTSECTOR)&abBuf[0];
294 if ( memcmp(pBs->abLabel, RT_STR_TUPLE(BS3_LABEL)) == 0
295 && memcmp(pBs->abFSType, RT_STR_TUPLE(BS3_FSTYPE)) == 0
296 && memcmp(pBs->abOemId, RT_STR_TUPLE(BS3_OEMID)) == 0)
297 pBs->cLargeTotalSectors = cSectors;
298 else
299 {
300 fprintf(stderr, "error: Didn't find magic strings in the first file (%s).\n", paInputs[i].pszFile);
301 rcExit = 1;
302 }
303 }
304
305 /* Write the block to the output file. */
306 if (fwrite(abBuf, sizeof(uint8_t), cbRead, pOutput) == cbRead)
307 off += cbRead;
308 else
309 {
310 fprintf(stderr, "error: fwrite failed\n");
311 rcExit = 1;
312 break;
313 }
314 }
315
316 if (ferror(paInputs[i].pFile))
317 {
318 fprintf(stderr, "error: Error reading '%s'.\n", paInputs[i].pszFile);
319 rcExit = 1;
320 }
321 }
322
323 /* Close the input files. */
324 for (unsigned i = 0; i < cInputs && rcExit == 0; i++)
325 fclose(paInputs[i].pFile);
326 free(paInputs);
327
328 /* Avoid output sizes that makes the FDC code think it's a single sided
329 floppy. The BIOS always report double sided floppies, and even if we
330 the bootsector adjust it's bMaxHeads value when getting a 20h error
331 we end up with a garbaged image (seems somewhere in the BIOS/FDC it is
332 still treated as a double sided floppy and we get half the data we want
333 and with gaps).
334
335 Similarly, if the size is 320KB or 360KB the FDC detects it as a double
336 sided 5.25" floppy with 40 tracks, while the BIOS keeps reporting a
337 1.44MB 3.5" floppy. So, just avoid those sizes too. */
338 uint32_t cbOutput = ftell(pOutput);
339 if ( cbOutput == 512 * 8 * 40 * 1 /* 160kB 5"1/4 SS */
340 || cbOutput == 512 * 9 * 40 * 1 /* 180kB 5"1/4 SS */
341 || cbOutput == 512 * 8 * 40 * 2 /* 320kB 5"1/4 DS */
342 || cbOutput == 512 * 9 * 40 * 2 /* 360kB 5"1/4 DS */ )
343 {
344 static uint8_t const s_abZeroSector[512] = { 0 };
345 if (fwrite(s_abZeroSector, sizeof(uint8_t), sizeof(s_abZeroSector), pOutput) != sizeof(s_abZeroSector))
346 {
347 fprintf(stderr, "error: fwrite failed (padding)\n");
348 rcExit = 1;
349 }
350 }
351
352 /* Finally, close the output file (can fail because of buffered data). */
353 if (fclose(pOutput) != 0)
354 {
355 fprintf(stderr, "error: Error closing '%s'.\n", pszOutput);
356 rcExit = 1;
357 }
358
359 fclose(stderr);
360 return rcExit;
361}
362
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