2 * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
47 /** Start of our image (defined by linker) */
50 /** End of our image (defined by linker) */
59 /** Length of initrd */
62 /** bootmgr.exe path within WIM */
63 static const wchar_t bootmgr_path
[] = L
"\\Windows\\Boot\\PXE\\bootmgr.exe";
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",
77 /** bootmgr.exe file */
78 static struct vdisk_file
*bootmgr
;
81 static struct vdisk_file
*bootwim
;
83 /** Minimal length of embedded bootmgr.exe */
84 #define BOOTMGR_MIN_LEN 16384
86 /** 1MB memory threshold */
87 #define ADDR_1MB 0x00100000
89 /** 2GB memory threshold */
90 #define ADDR_2GB 0x80000000
101 * Wrap interrupt callback
103 * @v params Parameters
105 static void call_interrupt_wrapper ( struct bootapp_callback_params
*params
) {
106 struct paging_state state
;
107 uint16_t *attributes
;
109 /* Handle/modify/pass-through interrupt as required */
110 if ( params
->vector
.interrupt
== 0x13 ) {
113 enable_paging ( &state
);
115 /* Intercept INT 13 calls for the emulated drive */
116 emulate_int13 ( params
);
119 disable_paging ( &state
);
121 } else if ( ( params
->vector
.interrupt
== 0x10 ) &&
122 ( params
->ax
== 0x4f01 ) &&
123 ( ! cmdline_gui
) ) {
125 /* Mark all VESA video modes as unsupported */
126 attributes
= REAL_PTR ( params
->es
, params
->di
);
127 call_interrupt ( params
);
128 *attributes
&= ~0x0001;
132 /* Pass through interrupt */
133 call_interrupt ( params
);
137 /** Real-mode callback functions */
138 static struct bootapp_callback_functions callback_fns
= {
139 .call_interrupt
= call_interrupt_wrapper
,
140 .call_real
= call_real
,
143 /** Real-mode callbacks */
144 static struct bootapp_callback callback
= {
145 .fns
= &callback_fns
,
148 /** Boot application descriptor set */
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
= {
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
),
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
),
186 .signature
= BOOTAPP_ENTRY_SIGNATURE
,
187 .flags
= BOOTAPP_ENTRY_FLAGS
,
191 .len
= sizeof ( bootapps
.wtf1
),
192 .extra_len
= ( sizeof ( bootapps
.wtf2
) +
193 sizeof ( bootapps
.wtf3
) ),
197 .len
= sizeof ( bootapps
.wtf3
),
198 .boot_partition_offset
= ( VDISK_VBR_LBA
* VDISK_SECTOR_SIZE
),
200 .mbr_signature
= VDISK_MBR_SIGNATURE
,
204 .len
= sizeof ( bootapps
.wtf3
),
205 .boot_partition_offset
= ( VDISK_VBR_LBA
* VDISK_SECTOR_SIZE
),
207 .mbr_signature
= VDISK_MBR_SIGNATURE
,
210 .callback
= &callback
,
213 .version
= BOOTAPP_POINTLESS_VERSION
,
218 * Test if a paragraph is empty
221 * @ret is_empty Paragraph is empty (all zeroes)
223 static int is_empty_pgh ( const void *pgh
) {
224 const uint32_t *dwords
= pgh
;
226 return ( ( dwords
[0] | dwords
[1] | dwords
[2] | dwords
[3] ) == 0 );
232 * @v file Virtual file
233 * @v data Data buffer
237 static void read_file ( struct vdisk_file
*file
, void *data
, size_t offset
,
240 memcpy ( data
, ( file
->opaque
+ offset
), len
);
244 * Add embedded bootmgr.exe extracted from bootmgr
248 * @ret file Virtual file, or NULL
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.
256 * A compressed version of bootmgr.exe is contained within bootmgr,
257 * which is trivial to obtain.
259 static struct vdisk_file
* add_bootmgr ( const void *data
, size_t len
) {
260 const uint8_t *compressed
;
262 size_t compressed_len
;
263 ssize_t ( * decompress
) ( const void *data
, size_t len
, void *buf
);
264 ssize_t decompressed_len
;
267 /* Look for an embedded compressed bootmgr.exe on an
268 * eight-byte boundary.
270 for ( offset
= BOOTMGR_MIN_LEN
; offset
< ( len
- BOOTMGR_MIN_LEN
) ;
273 /* Initialise checks */
275 compressed
= ( data
+ offset
);
276 compressed_len
= ( len
- offset
);
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.
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 "
291 decompress
= lznt1_decompress
;
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.
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 "
313 decompress
= xca_decompress
;
316 /* If we have not found a possible bootmgr.exe, skip
317 * to the next offset.
322 /* Find length of decompressed image */
323 decompressed_len
= decompress ( compressed
, compressed_len
,
325 if ( decompressed_len
< 0 ) {
326 /* May be a false positive signature match */
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
);
338 /* Add decompressed image */
339 return vdisk_add_file ( "bootmgr.exe", initrd
,
340 decompressed_len
, read_file
);
343 DBG ( "...no embedded bootmgr.exe found\n" );
353 * @ret rc Return status code
355 static int add_file ( const char *name
, void *data
, size_t len
) {
356 struct vdisk_file
*file
;
359 file
= vdisk_add_file ( name
, data
, len
, read_file
);
361 /* Check for special-case files */
362 if ( strcasecmp ( name
, "bootmgr.exe" ) == 0 ) {
363 DBG ( "...found bootmgr.exe\n" );
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" );
371 } else if ( strcasecmp ( ( name
+ strlen ( name
) - 4 ),
373 DBG ( "...found WIM file %s\n", name
);
381 * Relocate data between 1MB and 2GB if possible
383 * @v data Start of data
384 * @v len Length of data
385 * @ret start Start address
387 static void * relocate_memory_low ( void *data
, size_t len
) {
388 struct e820_entry
*e820
= NULL
;
392 /* Read system memory map */
393 while ( ( e820
= memmap_next ( e820
) ) != NULL
) {
395 /* Find highest compatible placement within this region */
396 end
= ( e820
->start
+ e820
->len
);
397 start
= ( ( end
> ADDR_2GB
) ? ADDR_2GB
: end
);
401 start
&= ~( PAGE_SIZE
- 1 );
402 if ( start
< e820
->start
)
404 if ( start
< ADDR_1MB
)
407 /* Relocate to this region */
408 memmove ( ( void * ) start
, data
, len
);
409 return ( ( void * ) start
);
412 /* Leave at original location */
424 struct paging_state state
;
425 uint64_t initrd_phys
;
427 /* Initialise stack cookie */
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" );
435 /* Process command line */
436 process_cmdline ( cmdline
);
438 /* Initialise paging */
442 enable_paging ( &state
);
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
) );
449 /* Extract files from initrd */
450 if ( cpio_extract ( initrd
, initrd_len
, add_file
) != 0 )
451 die ( "FATAL: could not extract initrd files\n" );
453 /* Process WIM image */
455 vdisk_patch_file ( bootwim
, patch_wim
);
456 if ( ( ! bootmgr
) &&
457 ( bootmgr
= wim_add_file ( bootwim
, cmdline_index
,
459 L
"bootmgr.exe" ) ) ) {
460 DBG ( "...extracted bootmgr.exe\n" );
462 wim_add_files ( bootwim
, cmdline_index
, wim_paths
);
465 /* Add INT 13 drive */
466 callback
.drive
= initialise_int13();
468 /* Read bootmgr.exe into memory */
470 die ( "FATAL: no bootmgr.exe\n" );
471 if ( bootmgr
->read
== read_file
) {
472 raw_pe
= bootmgr
->opaque
;
474 padded_len
= ( ( bootmgr
->len
+ PAGE_SIZE
- 1 ) &
476 raw_pe
= ( initrd
- padded_len
);
477 bootmgr
->read ( bootmgr
, raw_pe
, 0, bootmgr
->len
);
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" );
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
) );
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
);
502 /* Omit initrd region descriptor if located above 4GB */
503 if ( initrd_phys
>= ADDR_4GB
)
504 bootapps
.memory
.num_regions
--;
507 disable_paging ( &state
);
509 /* Jump to PE image */
510 DBG ( "Entering bootmgr.exe with parameters at %p\n", &bootapps
);
513 pe
.entry ( &bootapps
.bootapp
);
514 die ( "FATAL: bootmgr.exe returned\n" );