]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - wimboot/wimboot-2.7.3/src/main.c
1.1.07 release
[Ventoy.git] / wimboot / wimboot-2.7.3 / src / main.c
1 /*
2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 * 02110-1301, USA.
18 */
19
20 /**
21 * @file
22 *
23 * Main entry point
24 *
25 */
26
27 #include <stdint.h>
28 #include <stddef.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <strings.h>
33 #include "wimboot.h"
34 #include "peloader.h"
35 #include "int13.h"
36 #include "vdisk.h"
37 #include "cpio.h"
38 #include "lznt1.h"
39 #include "xca.h"
40 #include "cmdline.h"
41 #include "wimpatch.h"
42 #include "wimfile.h"
43 #include "pause.h"
44 #include "paging.h"
45 #include "memmap.h"
46
47 /** Start of our image (defined by linker) */
48 extern char _start[];
49
50 /** End of our image (defined by linker) */
51 extern char _end[];
52
53 /** Command line */
54 char *cmdline;
55
56 /** initrd */
57 void *initrd;
58
59 /** Length of initrd */
60 size_t initrd_len;
61
62 /** bootmgr.exe path within WIM */
63 static const wchar_t bootmgr_path[] = L"\\Windows\\Boot\\PXE\\bootmgr.exe";
64
65 /** Other paths within WIM */
66 static const wchar_t *wim_paths[] = {
67 L"\\Windows\\Boot\\DVD\\PCAT\\boot.sdi",
68 L"\\Windows\\Boot\\DVD\\PCAT\\BCD",
69 L"\\Windows\\Boot\\Fonts\\segmono_boot.ttf",
70 L"\\Windows\\Boot\\Fonts\\segoen_slboot.ttf",
71 L"\\Windows\\Boot\\Fonts\\segoe_slboot.ttf",
72 L"\\Windows\\Boot\\Fonts\\wgl4_boot.ttf",
73 L"\\sms\\boot\\boot.sdi",
74 NULL
75 };
76
77 /** bootmgr.exe file */
78 static struct vdisk_file *bootmgr;
79
80 /** WIM image file */
81 static struct vdisk_file *bootwim;
82
83 /** Minimal length of embedded bootmgr.exe */
84 #define BOOTMGR_MIN_LEN 16384
85
86 /** 1MB memory threshold */
87 #define ADDR_1MB 0x00100000
88
89 /** 2GB memory threshold */
90 #define ADDR_2GB 0x80000000
91
92 /** Memory regions */
93 enum {
94 WIMBOOT_REGION = 0,
95 PE_REGION,
96 INITRD_REGION,
97 NUM_REGIONS
98 };
99
100 /**
101 * Wrap interrupt callback
102 *
103 * @v params Parameters
104 */
105 static void call_interrupt_wrapper ( struct bootapp_callback_params *params ) {
106 struct paging_state state;
107 uint16_t *attributes;
108
109 /* Handle/modify/pass-through interrupt as required */
110 if ( params->vector.interrupt == 0x13 ) {
111
112 /* Enable paging */
113 enable_paging ( &state );
114
115 /* Intercept INT 13 calls for the emulated drive */
116 emulate_int13 ( params );
117
118 /* Disable paging */
119 disable_paging ( &state );
120
121 } else if ( ( params->vector.interrupt == 0x10 ) &&
122 ( params->ax == 0x4f01 ) &&
123 ( ! cmdline_gui ) ) {
124
125 /* Mark all VESA video modes as unsupported */
126 attributes = REAL_PTR ( params->es, params->di );
127 call_interrupt ( params );
128 *attributes &= ~0x0001;
129
130 } else {
131
132 /* Pass through interrupt */
133 call_interrupt ( params );
134 }
135 }
136
137 /** Real-mode callback functions */
138 static struct bootapp_callback_functions callback_fns = {
139 .call_interrupt = call_interrupt_wrapper,
140 .call_real = call_real,
141 };
142
143 /** Real-mode callbacks */
144 static struct bootapp_callback callback = {
145 .fns = &callback_fns,
146 };
147
148 /** Boot application descriptor set */
149 static struct {
150 /** Boot application descriptor */
151 struct bootapp_descriptor bootapp;
152 /** Boot application memory descriptor */
153 struct bootapp_memory_descriptor memory;
154 /** Boot application memory descriptor regions */
155 struct bootapp_memory_region regions[NUM_REGIONS];
156 /** Boot application entry descriptor */
157 struct bootapp_entry_descriptor entry;
158 struct bootapp_entry_wtf1_descriptor wtf1;
159 struct bootapp_entry_wtf2_descriptor wtf2;
160 struct bootapp_entry_wtf3_descriptor wtf3;
161 struct bootapp_entry_wtf3_descriptor wtf3_copy;
162 /** Boot application callback descriptor */
163 struct bootapp_callback_descriptor callback;
164 /** Boot application pointless descriptor */
165 struct bootapp_pointless_descriptor pointless;
166 } __attribute__ (( packed )) bootapps = {
167 .bootapp = {
168 .signature = BOOTAPP_SIGNATURE,
169 .version = BOOTAPP_VERSION,
170 .len = sizeof ( bootapps ),
171 .arch = BOOTAPP_ARCH_I386,
172 .memory = offsetof ( typeof ( bootapps ), memory ),
173 .entry = offsetof ( typeof ( bootapps ), entry ),
174 .xxx = offsetof ( typeof ( bootapps ), wtf3_copy ),
175 .callback = offsetof ( typeof ( bootapps ), callback ),
176 .pointless = offsetof ( typeof ( bootapps ), pointless ),
177 },
178 .memory = {
179 .version = BOOTAPP_MEMORY_VERSION,
180 .len = sizeof ( bootapps.memory ),
181 .num_regions = NUM_REGIONS,
182 .region_len = sizeof ( bootapps.regions[0] ),
183 .reserved_len = sizeof ( bootapps.regions[0].reserved ),
184 },
185 .entry = {
186 .signature = BOOTAPP_ENTRY_SIGNATURE,
187 .flags = BOOTAPP_ENTRY_FLAGS,
188 },
189 .wtf1 = {
190 .flags = 0x11000001,
191 .len = sizeof ( bootapps.wtf1 ),
192 .extra_len = ( sizeof ( bootapps.wtf2 ) +
193 sizeof ( bootapps.wtf3 ) ),
194 },
195 .wtf3 = {
196 .flags = 0x00000006,
197 .len = sizeof ( bootapps.wtf3 ),
198 .boot_partition_offset = ( VDISK_VBR_LBA * VDISK_SECTOR_SIZE ),
199 .xxx = 0x01,
200 .mbr_signature = VDISK_MBR_SIGNATURE,
201 },
202 .wtf3_copy = {
203 .flags = 0x00000006,
204 .len = sizeof ( bootapps.wtf3 ),
205 .boot_partition_offset = ( VDISK_VBR_LBA * VDISK_SECTOR_SIZE ),
206 .xxx = 0x01,
207 .mbr_signature = VDISK_MBR_SIGNATURE,
208 },
209 .callback = {
210 .callback = &callback,
211 },
212 .pointless = {
213 .version = BOOTAPP_POINTLESS_VERSION,
214 },
215 };
216
217 /**
218 * Test if a paragraph is empty
219 *
220 * @v pgh Paragraph
221 * @ret is_empty Paragraph is empty (all zeroes)
222 */
223 static int is_empty_pgh ( const void *pgh ) {
224 const uint32_t *dwords = pgh;
225
226 return ( ( dwords[0] | dwords[1] | dwords[2] | dwords[3] ) == 0 );
227 }
228
229 /**
230 * Read from file
231 *
232 * @v file Virtual file
233 * @v data Data buffer
234 * @v offset Offset
235 * @v len Length
236 */
237 static void read_file ( struct vdisk_file *file, void *data, size_t offset,
238 size_t len ) {
239
240 memcpy ( data, ( file->opaque + offset ), len );
241 }
242
243 /**
244 * Add embedded bootmgr.exe extracted from bootmgr
245 *
246 * @v data File data
247 * @v len Length
248 * @ret file Virtual file, or NULL
249 *
250 * bootmgr.exe is awkward to obtain, since it is not available as a
251 * standalone file on the installation media, or on an installed
252 * system, or in a Windows PE image as created by WAIK or WADK. It
253 * can be extracted from a typical boot.wim image using ImageX, but
254 * this requires installation of the WAIK/WADK/wimlib.
255 *
256 * A compressed version of bootmgr.exe is contained within bootmgr,
257 * which is trivial to obtain.
258 */
259 static struct vdisk_file * add_bootmgr ( const void *data, size_t len ) {
260 const uint8_t *compressed;
261 size_t offset;
262 size_t compressed_len;
263 ssize_t ( * decompress ) ( const void *data, size_t len, void *buf );
264 ssize_t decompressed_len;
265 size_t padded_len;
266
267 /* Look for an embedded compressed bootmgr.exe on an
268 * eight-byte boundary.
269 */
270 for ( offset = BOOTMGR_MIN_LEN ; offset < ( len - BOOTMGR_MIN_LEN ) ;
271 offset += 0x08 ) {
272
273 /* Initialise checks */
274 decompress = NULL;
275 compressed = ( data + offset );
276 compressed_len = ( len - offset );
277
278 /* Check for an embedded LZNT1-compressed bootmgr.exe.
279 * Since there is no way for LZNT1 to compress the
280 * initial "MZ" bytes of bootmgr.exe, we look for this
281 * signature starting three bytes after a paragraph
282 * boundary, with a preceding tag byte indicating that
283 * these two bytes would indeed be uncompressed.
284 */
285 if ( ( ( offset & 0x0f ) == 0x00 ) &&
286 ( ( compressed[0x02] & 0x03 ) == 0x00 ) &&
287 ( compressed[0x03] == 'M' ) &&
288 ( compressed[0x04] == 'Z' ) ) {
289 DBG ( "...checking for LZNT1-compressed bootmgr.exe at "
290 "+%#zx\n", offset );
291 decompress = lznt1_decompress;
292 }
293
294 /* Check for an embedded XCA-compressed bootmgr.exe.
295 * The bytes 0x00, 'M', and 'Z' will always be
296 * present, and so the corresponding symbols must have
297 * a non-zero Huffman length. The embedded image
298 * tends to have a large block of zeroes immediately
299 * beforehand, which we check for. It's implausible
300 * that the compressed data could contain substantial
301 * runs of zeroes, so we check for that too, in order
302 * to eliminate some common false positive matches.
303 */
304 if ( ( ( compressed[0x00] & 0x0f ) != 0x00 ) &&
305 ( ( compressed[0x26] & 0xf0 ) != 0x00 ) &&
306 ( ( compressed[0x2d] & 0x0f ) != 0x00 ) &&
307 ( is_empty_pgh ( compressed - 0x10 ) ) &&
308 ( ! is_empty_pgh ( ( compressed + 0x400 ) ) ) &&
309 ( ! is_empty_pgh ( ( compressed + 0x800 ) ) ) &&
310 ( ! is_empty_pgh ( ( compressed + 0xc00 ) ) ) ) {
311 DBG ( "...checking for XCA-compressed bootmgr.exe at "
312 "+%#zx\n", offset );
313 decompress = xca_decompress;
314 }
315
316 /* If we have not found a possible bootmgr.exe, skip
317 * to the next offset.
318 */
319 if ( ! decompress )
320 continue;
321
322 /* Find length of decompressed image */
323 decompressed_len = decompress ( compressed, compressed_len,
324 NULL );
325 if ( decompressed_len < 0 ) {
326 /* May be a false positive signature match */
327 continue;
328 }
329
330 /* Prepend decompressed image to initrd */
331 DBG ( "...extracting embedded bootmgr.exe\n" );
332 padded_len = ( ( decompressed_len + PAGE_SIZE - 1 ) &
333 ~( PAGE_SIZE - 1 ) );
334 initrd -= padded_len;
335 initrd_len += padded_len;
336 decompress ( compressed, compressed_len, initrd );
337
338 /* Add decompressed image */
339 return vdisk_add_file ( "bootmgr.exe", initrd,
340 decompressed_len, read_file );
341 }
342
343 DBG ( "...no embedded bootmgr.exe found\n" );
344 return NULL;
345 }
346
347 /**
348 * File handler
349 *
350 * @v name File name
351 * @v data File data
352 * @v len Length
353 * @ret rc Return status code
354 */
355 static int add_file ( const char *name, void *data, size_t len ) {
356 struct vdisk_file *file;
357
358 /* Store file */
359 file = vdisk_add_file ( name, data, len, read_file );
360
361 /* Check for special-case files */
362 if ( strcasecmp ( name, "bootmgr.exe" ) == 0 ) {
363 DBG ( "...found bootmgr.exe\n" );
364 bootmgr = file;
365 } else if ( strcasecmp ( name, "bootmgr" ) == 0 ) {
366 DBG ( "...found bootmgr\n" );
367 if ( ( ! bootmgr ) &&
368 ( bootmgr = add_bootmgr ( data, len ) ) ) {
369 DBG ( "...extracted bootmgr.exe\n" );
370 }
371 } else if ( strcasecmp ( ( name + strlen ( name ) - 4 ),
372 ".wim" ) == 0 ) {
373 DBG ( "...found WIM file %s\n", name );
374 bootwim = file;
375 }
376
377 return 0;
378 }
379
380 /**
381 * Relocate data between 1MB and 2GB if possible
382 *
383 * @v data Start of data
384 * @v len Length of data
385 * @ret start Start address
386 */
387 static void * relocate_memory_low ( void *data, size_t len ) {
388 struct e820_entry *e820 = NULL;
389 uint64_t end;
390 intptr_t start;
391
392 /* Read system memory map */
393 while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {
394
395 /* Find highest compatible placement within this region */
396 end = ( e820->start + e820->len );
397 start = ( ( end > ADDR_2GB ) ? ADDR_2GB : end );
398 if ( start < len )
399 continue;
400 start -= len;
401 start &= ~( PAGE_SIZE - 1 );
402 if ( start < e820->start )
403 continue;
404 if ( start < ADDR_1MB )
405 continue;
406
407 /* Relocate to this region */
408 memmove ( ( void * ) start, data, len );
409 return ( ( void * ) start );
410 }
411
412 /* Leave at original location */
413 return data;
414 }
415
416 /**
417 * Main entry point
418 *
419 */
420 int main ( void ) {
421 size_t padded_len;
422 void *raw_pe;
423 struct loaded_pe pe;
424 struct paging_state state;
425 uint64_t initrd_phys;
426
427 /* Initialise stack cookie */
428 init_cookie();
429
430 /* Print welcome banner */
431 printf ( "\n\nBooting wim file...... (This may take a few minutes, please wait)\n\n");
432 //printf ( "\n\nwimboot " VERSION " -- Windows Imaging Format "
433 // "bootloader -- https://ipxe.org/wimboot\n\n" );
434
435 /* Process command line */
436 process_cmdline ( cmdline );
437
438 /* Initialise paging */
439 init_paging();
440
441 /* Enable paging */
442 enable_paging ( &state );
443
444 /* Relocate initrd below 2GB if possible, to avoid collisions */
445 DBG ( "Found initrd at [%p,%p)\n", initrd, ( initrd + initrd_len ) );
446 initrd = relocate_memory_low ( initrd, initrd_len );
447 DBG ( "Placing initrd at [%p,%p)\n", initrd, ( initrd + initrd_len ) );
448
449 /* Extract files from initrd */
450 if ( cpio_extract ( initrd, initrd_len, add_file ) != 0 )
451 die ( "FATAL: could not extract initrd files\n" );
452
453 /* Process WIM image */
454 if ( bootwim ) {
455 vdisk_patch_file ( bootwim, patch_wim );
456 if ( ( ! bootmgr ) &&
457 ( bootmgr = wim_add_file ( bootwim, cmdline_index,
458 bootmgr_path,
459 L"bootmgr.exe" ) ) ) {
460 DBG ( "...extracted bootmgr.exe\n" );
461 }
462 wim_add_files ( bootwim, cmdline_index, wim_paths );
463 }
464
465 /* Add INT 13 drive */
466 callback.drive = initialise_int13();
467
468 /* Read bootmgr.exe into memory */
469 if ( ! bootmgr )
470 die ( "FATAL: no bootmgr.exe\n" );
471 if ( bootmgr->read == read_file ) {
472 raw_pe = bootmgr->opaque;
473 } else {
474 padded_len = ( ( bootmgr->len + PAGE_SIZE - 1 ) &
475 ~( PAGE_SIZE -1 ) );
476 raw_pe = ( initrd - padded_len );
477 bootmgr->read ( bootmgr, raw_pe, 0, bootmgr->len );
478 }
479
480 /* Load bootmgr.exe into memory */
481 if ( load_pe ( raw_pe, bootmgr->len, &pe ) != 0 )
482 die ( "FATAL: Could not load bootmgr.exe\n" );
483
484 /* Relocate initrd above 4GB if possible, to free up 32-bit memory */
485 initrd_phys = relocate_memory_high ( initrd, initrd_len );
486 DBG ( "Placing initrd at physical [%#llx,%#llx)\n",
487 initrd_phys, ( initrd_phys + initrd_len ) );
488
489 /* Complete boot application descriptor set */
490 bootapps.bootapp.pe_base = pe.base;
491 bootapps.bootapp.pe_len = pe.len;
492 bootapps.regions[WIMBOOT_REGION].start_page = page_start ( _start );
493 bootapps.regions[WIMBOOT_REGION].num_pages = page_len ( _start, _end );
494 bootapps.regions[PE_REGION].start_page = page_start ( pe.base );
495 bootapps.regions[PE_REGION].num_pages =
496 page_len ( pe.base, ( pe.base + pe.len ) );
497 bootapps.regions[INITRD_REGION].start_page =
498 ( initrd_phys / PAGE_SIZE );
499 bootapps.regions[INITRD_REGION].num_pages =
500 page_len ( initrd, initrd + initrd_len );
501
502 /* Omit initrd region descriptor if located above 4GB */
503 if ( initrd_phys >= ADDR_4GB )
504 bootapps.memory.num_regions--;
505
506 /* Disable paging */
507 disable_paging ( &state );
508
509 /* Jump to PE image */
510 DBG ( "Entering bootmgr.exe with parameters at %p\n", &bootapps );
511 if ( cmdline_pause )
512 pause();
513 pe.entry ( &bootapps.bootapp );
514 die ( "FATAL: bootmgr.exe returned\n" );
515 }