Commit 93996cf7 authored by longpanda's avatar longpanda
Browse files

1. Optimization for WIMBOOT mode.

2. Add WIMBOOT for UEFI mode.
parent ca62128f
#ifndef _STDIO_H
#define _STDIO_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Standard Input/Output
*
*/
#include <stdint.h>
#include <stdarg.h>
extern int putchar ( int character );
extern int getchar ( void );
extern int __attribute__ (( format ( printf, 1, 2 ) ))
printf ( const char *fmt, ... );
extern int __attribute__ (( format ( printf, 3, 4 ) ))
snprintf ( char *buf, size_t size, const char *fmt, ... );
extern int vprintf ( const char *fmt, va_list args );
extern int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args );
#endif /* _STDIO_H */
#ifndef _STDLIB_H
#define _STDLIB_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Standard library
*
*/
extern unsigned long strtoul ( const char *nptr, char **endptr, int base );
#endif /* _STDLIB_H */
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* String functions
*
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "ctype.h"
#include "wctype.h"
/**
* Copy memory area
*
* @v dest Destination address
* @v src Source address
* @v len Length
* @ret dest Destination address
*/
void * memcpy ( void *dest, const void *src, size_t len ) {
void *edi = dest;
const void *esi = src;
int discard_ecx;
/* Perform dword-based copy for bulk, then byte-based for remainder */
__asm__ __volatile__ ( "rep movsl"
: "=&D" ( edi ), "=&S" ( esi ),
"=&c" ( discard_ecx )
: "0" ( edi ), "1" ( esi ), "2" ( len >> 2 )
: "memory" );
__asm__ __volatile__ ( "rep movsb"
: "=&D" ( edi ), "=&S" ( esi ),
"=&c" ( discard_ecx )
: "0" ( edi ), "1" ( esi ), "2" ( len & 3 )
: "memory" );
return dest;
}
/**
* Copy memory area backwards
*
* @v dest Destination address
* @v src Source address
* @v len Length
* @ret dest Destination address
*/
static void * memcpy_reverse ( void *dest, const void *src, size_t len ) {
void *edi = ( dest + len - 1 );
const void *esi = ( src + len - 1 );
int discard_ecx;
/* Assume memmove() is not performance-critical, and perform a
* bytewise copy for simplicity.
*
* Disable interrupts to avoid known problems on platforms
* that assume the direction flag always remains cleared.
*/
__asm__ __volatile__ ( "pushf\n\t"
"cli\n\t"
"std\n\t"
"rep movsb\n\t"
"popf\n\t"
: "=&D" ( edi ), "=&S" ( esi ),
"=&c" ( discard_ecx )
: "0" ( edi ), "1" ( esi ),
"2" ( len )
: "memory" );
return dest;
}
/**
* Copy (possibly overlapping) memory area
*
* @v dest Destination address
* @v src Source address
* @v len Length
* @ret dest Destination address
*/
void * memmove ( void *dest, const void *src, size_t len ) {
if ( dest <= src ) {
return memcpy ( dest, src, len );
} else {
return memcpy_reverse ( dest, src, len );
}
}
/**
* Set memory area
*
* @v dest Destination address
* @v src Source address
* @v len Length
* @ret dest Destination address
*/
void * memset ( void *dest, int c, size_t len ) {
void *edi = dest;
int eax = c;
int discard_ecx;
/* Expand byte to whole dword */
eax |= ( eax << 8 );
eax |= ( eax << 16 );
/* Perform dword-based set for bulk, then byte-based for remainder */
__asm__ __volatile__ ( "rep stosl"
: "=&D" ( edi ), "=&a" ( eax ),
"=&c" ( discard_ecx )
: "0" ( edi ), "1" ( eax ), "2" ( len >> 2 )
: "memory" );
__asm__ __volatile__ ( "rep stosb"
: "=&D" ( edi ), "=&a" ( eax ),
"=&c" ( discard_ecx )
: "0" ( edi ), "1" ( eax ), "2" ( len & 3 )
: "memory" );
return dest;
}
/**
* Compare memory areas
*
* @v src1 First source area
* @v src2 Second source area
* @v len Length
* @ret diff Difference
*/
int memcmp ( const void *src1, const void *src2, size_t len ) {
const uint8_t *bytes1 = src1;
const uint8_t *bytes2 = src2;
int diff;
while ( len-- ) {
if ( ( diff = ( *(bytes1++) - *(bytes2++) ) ) )
return diff;
}
return 0;
}
/**
* Compare two strings
*
* @v str1 First string
* @v str2 Second string
* @ret diff Difference
*/
int strcmp ( const char *str1, const char *str2 ) {
int c1;
int c2;
do {
c1 = *(str1++);
c2 = *(str2++);
} while ( ( c1 != '\0' ) && ( c1 == c2 ) );
return ( c1 - c2 );
}
/**
* Compare two strings, case-insensitively
*
* @v str1 First string
* @v str2 Second string
* @ret diff Difference
*/
int strcasecmp ( const char *str1, const char *str2 ) {
int c1;
int c2;
do {
c1 = toupper ( *(str1++) );
c2 = toupper ( *(str2++) );
} while ( ( c1 != '\0' ) && ( c1 == c2 ) );
return ( c1 - c2 );
}
/**
* Compare two wide-character strings, case-insensitively
*
* @v str1 First string
* @v str2 Second string
* @ret diff Difference
*/
int wcscasecmp ( const wchar_t *str1, const wchar_t *str2 ) {
int c1;
int c2;
do {
c1 = towupper ( *(str1++) );
c2 = towupper ( *(str2++) );
} while ( ( c1 != L'\0' ) && ( c1 == c2 ) );
return ( c1 - c2 );
}
/**
* Get length of string
*
* @v str String
* @ret len Length
*/
size_t strlen ( const char *str ) {
size_t len = 0;
while ( *(str++) )
len++;
return len;
}
/**
* Get length of wide-character string
*
* @v str String
* @ret len Length (in characters)
*/
size_t wcslen ( const wchar_t *str ) {
size_t len = 0;
while ( *(str++) )
len++;
return len;
}
/**
* Find character in wide-character string
*
* @v str String
* @v c Wide character
* @ret first First occurrence of wide character in string, or NULL
*/
wchar_t * wcschr ( const wchar_t *str, wchar_t c ) {
for ( ; *str ; str++ ) {
if ( *str == c )
return ( ( wchar_t * )str );
}
return NULL;
}
char *strchr(const char *str, char c) {
for ( ; *str ; str++ ) {
if ( *str == c )
return ( ( char * )str );
}
return NULL;
}
/**
* Check to see if character is a space
*
* @v c Character
* @ret isspace Character is a space
*/
int isspace ( int c ) {
switch ( c ) {
case ' ' :
case '\f' :
case '\n' :
case '\r' :
case '\t' :
case '\v' :
return 1;
default:
return 0;
}
}
/**
* Convert a string to an unsigned integer
*
* @v nptr String
* @v endptr End pointer to fill in (or NULL)
* @v base Numeric base
* @ret val Value
*/
unsigned long strtoul ( const char *nptr, char **endptr, int base ) {
unsigned long val = 0;
int negate = 0;
unsigned int digit;
/* Skip any leading whitespace */
while ( isspace ( *nptr ) )
nptr++;
/* Parse sign, if present */
if ( *nptr == '+' ) {
nptr++;
} else if ( *nptr == '-' ) {
nptr++;
negate = 1;
}
/* Parse base */
if ( base == 0 ) {
/* Default to decimal */
base = 10;
/* Check for octal or hexadecimal markers */
if ( *nptr == '0' ) {
nptr++;
base = 8;
if ( ( *nptr | 0x20 ) == 'x' ) {
nptr++;
base = 16;
}
}
}
/* Parse digits */
for ( ; ; nptr++ ) {
digit = *nptr;
if ( digit >= 'a' ) {
digit = ( digit - 'a' + 10 );
} else if ( digit >= 'A' ) {
digit = ( digit - 'A' + 10 );
} else if ( digit <= '9' ) {
digit = ( digit - '0' );
}
if ( digit >= ( unsigned int ) base )
break;
val = ( ( val * base ) + digit );
}
/* Record end marker, if applicable */
if ( endptr )
*endptr = ( ( char * ) nptr );
/* Return value */
return ( negate ? -val : val );
}
#ifndef _STRING_H
#define _STRING_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* String operations
*
*/
#include <stdint.h>
extern void * memcpy ( void *dest, const void *src, size_t len );
extern void * memmove ( void *dest, const void *src, size_t len );
extern void * memset ( void *dest, int c, size_t len );
extern int memcmp ( const void *src1, const void *src2, size_t len );
extern int strcmp ( const char *str1, const char *str2 );
extern size_t strlen ( const char *str );
#endif /* _STRING_H */
#ifndef _STRINGS_H
#define _STRINGS_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* String operations
*
*/
extern int strcasecmp ( const char *str1, const char *str2 );
#endif /* _STRINGS_H */
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Virtual disk
*
*/
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include "ctype.h"
#include "wimboot.h"
#include "vdisk.h"
/** Virtual files */
struct vdisk_file vdisk_files[VDISK_MAX_FILES];
/**
* Read from virtual Master Boot Record
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_mbr ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_mbr *mbr = data;
/* Construct MBR */
memset ( mbr, 0, sizeof ( *mbr ) );
mbr->partitions[0].bootable = VDISK_MBR_BOOTABLE;
mbr->partitions[0].type = VDISK_MBR_TYPE_FAT32;
mbr->partitions[0].start = VDISK_PARTITION_LBA;
mbr->partitions[0].length = VDISK_PARTITION_COUNT;
mbr->signature = VDISK_MBR_SIGNATURE;
mbr->magic = VDISK_MBR_MAGIC;
}
/**
* Read from virtual Volume Boot Record
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_vbr ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_vbr *vbr = data;
/* Construct VBR */
memset ( vbr, 0, sizeof ( *vbr ) );
vbr->jump[0] = VDISK_VBR_JUMP_WTF_MS;
memcpy ( vbr->oemid, VDISK_VBR_OEMID, sizeof ( vbr->oemid ) );
vbr->bytes_per_sector = VDISK_SECTOR_SIZE;
vbr->sectors_per_cluster = VDISK_CLUSTER_COUNT;
vbr->reserved_sectors = VDISK_RESERVED_COUNT;
vbr->fats = 1;
vbr->media = VDISK_VBR_MEDIA;
vbr->sectors_per_track = VDISK_SECTORS_PER_TRACK;
vbr->heads = VDISK_HEADS;
vbr->hidden_sectors = VDISK_VBR_LBA;
vbr->sectors = VDISK_PARTITION_COUNT;
vbr->sectors_per_fat = VDISK_SECTORS_PER_FAT;
vbr->root = VDISK_ROOT_CLUSTER;
vbr->fsinfo = VDISK_FSINFO_SECTOR;
vbr->backup = VDISK_BACKUP_VBR_SECTOR;
vbr->signature = VDISK_VBR_SIGNATURE;
vbr->serial = VDISK_VBR_SERIAL;
memcpy ( vbr->label, VDISK_VBR_LABEL, sizeof ( vbr->label ) );
memcpy ( vbr->system, VDISK_VBR_SYSTEM, sizeof ( vbr->system ) );
vbr->magic = VDISK_VBR_MAGIC;
}
/**
* Read from virtual FSInfo
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_fsinfo ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_fsinfo *fsinfo = data;
/* Construct FSInfo */
memset ( fsinfo, 0, sizeof ( *fsinfo ) );
fsinfo->magic1 = VDISK_FSINFO_MAGIC1;
fsinfo->magic2 = VDISK_FSINFO_MAGIC2;
fsinfo->next_free = VDISK_FSINFO_NEXT_FREE;
fsinfo->magic3 = VDISK_FSINFO_MAGIC3;
}
/**
* Read from virtual FAT
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_fat ( uint64_t lba, unsigned int count, void *data ) {
uint32_t *next = data;
uint32_t start;
uint32_t end;
uint32_t file_end_marker;
unsigned int i;
/* Calculate window within FAT */
start = ( ( lba - VDISK_FAT_LBA ) *
( VDISK_SECTOR_SIZE / sizeof ( *next ) ) );
end = ( start + ( count * ( VDISK_SECTOR_SIZE / sizeof ( *next ) ) ) );
next -= start;
/* Start by marking each cluster as chaining to the next */
for ( i = start ; i < end ; i++ )
next[i] = ( i + 1 );
/* Add first-sector special values, if applicable */
if ( start == 0 ) {
next[0] = ( ( VDISK_FAT_END_MARKER & ~0xff ) |
VDISK_VBR_MEDIA );
for ( i = 1; i < ( VDISK_SECTOR_SIZE / sizeof ( *next ) ); i++ )
next[i] = VDISK_FAT_END_MARKER;
}
/* Add end-of-file markers, if applicable */
for ( i = 0 ; i < VDISK_MAX_FILES ; i++ ) {
if ( vdisk_files[i].read ) {
file_end_marker = ( VDISK_FILE_CLUSTER ( i ) +
( ( vdisk_files[i].xlen - 1 ) /
VDISK_CLUSTER_SIZE ) );
if ( ( file_end_marker >= start ) &&
( file_end_marker < end ) ) {
next[file_end_marker] = VDISK_FAT_END_MARKER;
}
}
}
}
/**
* Initialise empty directory
*
* @v dir Virtual directory
* @ret dirent Starting (i.e. final) directory entry
*/
static union vdisk_directory_entry *
vdisk_empty_dir ( struct vdisk_directory *dir ) {
unsigned int i;
/* Mark all entries as present and deleted */
memset ( dir, 0, sizeof ( *dir ) );
for ( i = 0 ; i < VDISK_DIRENT_PER_SECTOR ; i++ ) {
dir->entry[i].deleted = VDISK_DIRENT_DELETED;
}
return &dir->entry[ VDISK_DIRENT_PER_SECTOR - 1 ];
}
/**
* Construct directory entry
*
* @v dirent Starting (i.e. final) directory entry
* @v name File name
* @v len File length
* @v attr File attributes
* @v cluster File starting cluster
* @ret next Next available directory entry
*/
static union vdisk_directory_entry *
vdisk_directory_entry ( union vdisk_directory_entry *dirent, const char *name,
size_t len,unsigned int attr, uint32_t cluster ) {
union vdisk_directory_entry *dos = dirent;
union vdisk_directory_entry *lfn = ( dos - 1 );
uint8_t *checksum_data;
uint8_t checksum;
unsigned int sequence;
uint16_t *lfn_char;
char c;
unsigned int i;
/* Populate directory entry (with invalid 8.3 filename) */
memset ( dos->dos.filename.raw, ' ', sizeof ( dos->dos.filename.raw ) );
dos->dos.attr = attr;
dos->dos.size = len;
dos->dos.cluster_high = ( cluster >> 16 );
dos->dos.cluster_low = ( cluster & 0xffff );
/* Calculate checksum of 8.3 filename */
checksum = 0;
checksum_data = dos->dos.filename.raw;
for ( i = 0 ; i < sizeof ( dos->dos.filename.raw ) ; i++ ) {
checksum = ( ( ( ( checksum & 1 ) << 7 ) |
( checksum >> 1 ) ) +
*(checksum_data++) );
}
/* Construct long filename record */
lfn_char = &lfn->lfn.name_1[0];
sequence = 1;
while ( 1 ) {
/* Initialise long filename, if necessary */
if ( lfn->lfn.attr != VDISK_LFN_ATTR ) {
lfn->lfn.sequence = sequence++;
memset ( lfn->lfn.name_1, 0xff,
sizeof ( lfn->lfn.name_1 ) );
lfn->lfn.attr = VDISK_LFN_ATTR;
lfn->lfn.checksum = checksum;
memset ( lfn->lfn.name_2, 0xff,
sizeof ( lfn->lfn.name_2 ) );
memset ( lfn->lfn.name_3, 0xff,
sizeof ( lfn->lfn.name_3 ) );
}
/* Add character to long filename */
c = *(name++);
*lfn_char = c;
if ( ! c )
break;
/* Move to next character within long filename */
if ( lfn_char == &lfn->lfn.name_1[4] ) {
lfn_char = &lfn->lfn.name_2[0];
} else if ( lfn_char == &lfn->lfn.name_2[5] ) {
lfn_char = &lfn->lfn.name_3[0];
} else if ( lfn_char == &lfn->lfn.name_3[1] ) {
lfn--;
lfn_char = &lfn->lfn.name_1[0];
} else {
lfn_char++;
}
}
lfn->lfn.sequence |= VDISK_LFN_END;
return ( lfn - 1 );
}
/**
* Read subdirectories from virtual root directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_root ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_directory *dir = data;
union vdisk_directory_entry *dirent;
/* Construct subdirectories */
dirent = vdisk_empty_dir ( dir );
dirent = vdisk_directory_entry ( dirent, "BOOT", 0, VDISK_DIRECTORY,
VDISK_BOOT_CLUSTER );
dirent = vdisk_directory_entry ( dirent, "SOURCES", 0, VDISK_DIRECTORY,
VDISK_SOURCES_CLUSTER );
dirent = vdisk_directory_entry ( dirent, "EFI", 0, VDISK_DIRECTORY,
VDISK_EFI_CLUSTER );
}
/**
* Read subdirectories from virtual boot directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_boot ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_directory *dir = data;
union vdisk_directory_entry *dirent;
/* Construct subdirectories */
dirent = vdisk_empty_dir ( dir );
dirent = vdisk_directory_entry ( dirent, "FONTS", 0, VDISK_DIRECTORY,
VDISK_FONTS_CLUSTER );
dirent = vdisk_directory_entry ( dirent, "RESOURCES", 0,
VDISK_DIRECTORY,
VDISK_RESOURCES_CLUSTER );
}
/**
* Read subdirectories from virtual sources directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_sources ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_directory *dir = data;
/* Construct subdirectories */
vdisk_empty_dir ( dir );
}
/**
* Read subdirectories from virtual fonts directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_fonts ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_directory *dir = data;
/* Construct subdirectories */
vdisk_empty_dir ( dir );
}
/**
* Read subdirectories from virtual resources directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_resources ( uint64_t lba __unused,
unsigned int count __unused, void *data ) {
struct vdisk_directory *dir = data;
/* Construct subdirectories */
vdisk_empty_dir ( dir );
}
/**
* Read subdirectories from virtual EFI directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_efi ( uint64_t lba __unused, unsigned int count __unused,
void *data ) {
struct vdisk_directory *dir = data;
union vdisk_directory_entry *dirent;
/* Construct subdirectories */
dirent = vdisk_empty_dir ( dir );
dirent = vdisk_directory_entry ( dirent, "BOOT", 0, VDISK_DIRECTORY,
VDISK_BOOT_CLUSTER );
dirent = vdisk_directory_entry ( dirent, "MICROSOFT", 0,
VDISK_DIRECTORY,
VDISK_MICROSOFT_CLUSTER );
}
/**
* Read subdirectories from virtual Microsoft directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_microsoft ( uint64_t lba __unused,
unsigned int count __unused, void *data ) {
struct vdisk_directory *dir = data;
union vdisk_directory_entry *dirent;
/* Construct subdirectories */
dirent = vdisk_empty_dir ( dir );
dirent = vdisk_directory_entry ( dirent, "BOOT", 0, VDISK_DIRECTORY,
VDISK_BOOT_CLUSTER );
}
/**
* Read files from virtual directory
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_dir_files ( uint64_t lba, unsigned int count, void *data ) {
struct vdisk_directory *dir;
union vdisk_directory_entry *dirent;
struct vdisk_file *file;
unsigned int idx;
for ( ; count ; lba++, count--, data += VDISK_SECTOR_SIZE ) {
/* Initialise directory */
dir = data;
vdisk_empty_dir ( dir );
dirent = &dir->entry[ VDISK_DIRENT_PER_SECTOR - 1 ];
/* Identify file */
idx = VDISK_FILE_DIRENT_IDX ( lba );
assert ( idx < ( sizeof ( vdisk_files ) /
sizeof ( vdisk_files[0] ) ) );
file = &vdisk_files[idx];
if ( ! file->read )
continue;
/* Populate directory entry */
vdisk_directory_entry ( dirent, file->name, file->xlen,
VDISK_READ_ONLY,
VDISK_FILE_CLUSTER ( idx ) );
}
}
/**
* Read from virtual file (or empty space)
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
static void vdisk_file ( uint64_t lba, unsigned int count, void *data ) {
struct vdisk_file *file;
size_t offset;
size_t len;
size_t copy_len;
size_t pad_len;
size_t patch_len;
/* Construct file portion */
file = &vdisk_files[ VDISK_FILE_IDX ( lba ) ];
offset = VDISK_FILE_OFFSET ( lba );
len = ( count * VDISK_SECTOR_SIZE );
/* Copy any initialised-data portion */
copy_len = ( ( offset < file->len ) ? ( file->len - offset ) : 0 );
if ( copy_len > len )
copy_len = len;
if ( copy_len )
file->read ( file, data, offset, copy_len );
/* Zero any uninitialised-data portion */
pad_len = ( len - copy_len );
memset ( ( data + copy_len ), 0, pad_len );
/* Patch any applicable portion */
patch_len = ( ( offset < file->xlen ) ? ( file->xlen - offset ) : 0 );
if ( patch_len > len )
patch_len = len;
if ( file->patch )
file->patch ( file, data, offset, patch_len );
}
/** A virtual disk region */
struct vdisk_region {
/** Name */
const char *name;
/** Starting LBA */
uint64_t lba;
/** Number of blocks */
unsigned int count;
/**
* Build data from this region
*
* @v start Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
void ( * build ) ( uint64_t lba, unsigned int count, void *data );
};
/** Define a virtual disk region */
#define VDISK_REGION( _name, _build, _lba, _count ) { \
.name = _name, \
.lba = _lba, \
.count = _count, \
.build = _build, \
}
/** Define a virtual disk directory region */
#define VDISK_DIRECTORY_REGION( _name, _build_subdirs, _lba ) { \
.name = _name " subdirs", \
.lba = _lba, \
.count = 1, \
.build = _build_subdirs, \
}, { \
.name = _name " files", \
.lba = ( _lba + 1 ), \
.count = ( VDISK_CLUSTER_COUNT - 1 ), \
.build = vdisk_dir_files, \
}
/** Virtual disk regions */
static struct vdisk_region vdisk_regions[] = {
VDISK_REGION ( "MBR", vdisk_mbr,
VDISK_MBR_LBA, VDISK_MBR_COUNT ),
VDISK_REGION ( "VBR", vdisk_vbr,
VDISK_VBR_LBA, VDISK_VBR_COUNT ),
VDISK_REGION ( "FSInfo", vdisk_fsinfo,
VDISK_FSINFO_LBA, VDISK_FSINFO_COUNT ),
VDISK_REGION ( "VBR Backup", vdisk_vbr,
VDISK_BACKUP_VBR_LBA, VDISK_BACKUP_VBR_COUNT ),
VDISK_REGION ( "FAT", vdisk_fat,
VDISK_FAT_LBA, VDISK_FAT_COUNT ),
VDISK_DIRECTORY_REGION ( "Root", vdisk_root, VDISK_ROOT_LBA ),
VDISK_DIRECTORY_REGION ( "Boot", vdisk_boot, VDISK_BOOT_LBA ),
VDISK_DIRECTORY_REGION ( "Sources", vdisk_sources, VDISK_SOURCES_LBA ),
VDISK_DIRECTORY_REGION ( "Fonts", vdisk_fonts, VDISK_FONTS_LBA ),
VDISK_DIRECTORY_REGION ( "Resources", vdisk_resources,
VDISK_RESOURCES_LBA ),
VDISK_DIRECTORY_REGION ( "EFI", vdisk_efi, VDISK_EFI_LBA ),
VDISK_DIRECTORY_REGION ( "Microsoft", vdisk_microsoft,
VDISK_MICROSOFT_LBA ),
};
/**
* Read from virtual disk
*
* @v lba Starting LBA
* @v count Number of blocks to read
* @v data Data buffer
*/
void vdisk_read ( uint64_t lba, unsigned int count, void *data ) {
struct vdisk_region *region;
void ( * build ) ( uint64_t lba, unsigned int count, void *data );
const char *name;
uint64_t start = lba;
uint64_t end = ( lba + count );
uint64_t frag_start = start;
uint64_t frag_end;
int file_idx;
uint64_t file_end;
uint64_t region_start;
uint64_t region_end;
unsigned int frag_count;
unsigned int i;
DBG2 ( "Read to %p from %#llx+%#x: ", data, lba, count );
do {
/* Initialise fragment to fill remaining space */
frag_end = end;
name = NULL;
build = NULL;
/* Truncate fragment and generate data */
file_idx = VDISK_FILE_IDX ( frag_start );
if ( file_idx >= 0 ) {
/* Truncate fragment to end of file */
file_end = VDISK_FILE_LBA ( file_idx + 1 );
if ( frag_end > file_end )
frag_end = file_end;
/* Generate data from file */
if ( file_idx < VDISK_MAX_FILES ) {
name = vdisk_files[file_idx].name;
build = vdisk_file;
}
} else {
/* Truncate fragment to region boundaries */
for ( i = 0 ; i < ( sizeof ( vdisk_regions ) /
sizeof ( vdisk_regions[0] ) ); i++){
region = &vdisk_regions[i];
region_start = region->lba;
region_end = ( region_start + region->count );
/* Avoid crossing start of any region */
if ( ( frag_start < region_start ) &&
( frag_end > region_start ) ){
frag_end = region_start;
}
/* Ignore unless we overlap with this region */
if ( ( frag_start >= region_end ) ||
( frag_end <= region_start ) ) {
continue;
}
/* Avoid crossing end of region */
if ( frag_end > region_end )
frag_end = region_end;
/* Found a suitable region */
name = region->name;
build = region->build;
break;
}
}
/* Generate data from this region */
frag_count = ( frag_end - frag_start );
DBG2 ( "%s%s (%#x)", ( ( frag_start == start ) ? "" : ", " ),
( name ? name : "empty" ), frag_count );
if ( build ) {
build ( frag_start, frag_count, data );
} else {
memset ( data, 0, ( frag_count * VDISK_SECTOR_SIZE ) );
}
/* Move to next fragment */
frag_start += frag_count;
data += ( frag_count * VDISK_SECTOR_SIZE );
} while ( frag_start != end );
DBG2 ( "\n" );
}
/**
* Add file to virtual disk
*
* @v name Name
* @v opaque Opaque token
* @v len Length
* @v read Read data method
* @ret file Virtual file
*/
struct vdisk_file * vdisk_add_file ( const char *name, void *opaque, size_t len,
void ( * read ) ( struct vdisk_file *file,
void *data,
size_t offset,
size_t len ) ) {
static unsigned int index = 0;
struct vdisk_file *file;
/* Sanity check */
if ( index >= VDISK_MAX_FILES )
die ( "Too many files\n" );
/* Store file */
file = &vdisk_files[index++];
snprintf ( file->name, sizeof ( file->name ), "%s", name );
file->opaque = opaque;
file->len = len;
file->xlen = len;
file->read = read;
DBG ( "Using %s via %p len %#zx\n", file->name, file->opaque,
file->len );
return file;
}
/**
* Patch virtual file
*
* @v file Virtual file
* @v patch Patch method
*/
void vdisk_patch_file ( struct vdisk_file *file,
void ( * patch ) ( struct vdisk_file *file, void *data,
size_t offset, size_t len ) ) {
/* Record patch method */
file->patch = patch;
/* Allow patch method to update file length */
patch ( file, NULL, 0, 0 );
}
#ifndef _VDISK_H
#define _VDISK_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Virtual disk emulation
*
*/
#include <stdint.h>
/** Number of cylinders */
#define VDISK_CYLINDERS 1024 /* Maximum possible */
/** Number of heads */
#define VDISK_HEADS 255
/** Number of sectors per track */
#define VDISK_SECTORS_PER_TRACK 63
/** Sector size (in bytes) */
#define VDISK_SECTOR_SIZE 512
/** Partition start LBA */
#define VDISK_PARTITION_LBA 128
/** Cluster size (in sectors) */
#define VDISK_CLUSTER_COUNT 64
/** Cluster size (in bytes) */
#define VDISK_CLUSTER_SIZE ( VDISK_CLUSTER_COUNT * VDISK_SECTOR_SIZE )
/** Number of clusters */
#define VDISK_CLUSTERS 0x03ffc000ULL /* Fill 2TB disk */
/** Maximum number of virtual files
*
* The total number of files must be strictly less than the number of
* sectors per cluster.
*/
#define VDISK_MAX_FILES ( VDISK_CLUSTER_COUNT - 1 )
/** Maximum file size (in sectors) */
#define VDISK_FILE_COUNT 0x800000UL /* max for 32-bit address space */
/** Maximum file size (in clusters) */
#define VDISK_FILE_CLUSTERS ( VDISK_FILE_COUNT / VDISK_CLUSTER_COUNT )
/** File starting LBA */
#define VDISK_FILE_LBA( idx ) ( ( (idx) + 1 ) * VDISK_FILE_COUNT )
/** File index from LBA */
#define VDISK_FILE_IDX( lba ) ( ( (lba) / VDISK_FILE_COUNT ) - 1 )
/** File offset (in bytes) from LBA */
#define VDISK_FILE_OFFSET( lba ) \
( ( (lba) % VDISK_FILE_COUNT ) * VDISK_SECTOR_SIZE )
/** File index from directory entry LBA */
#define VDISK_FILE_DIRENT_IDX( lba ) ( ( (lba) - 1 ) % VDISK_CLUSTER_COUNT )
/** Number of sectors allocated for FAT */
#define VDISK_SECTORS_PER_FAT \
( ( ( VDISK_CLUSTERS * sizeof ( uint32_t ) + \
VDISK_CLUSTER_SIZE - 1 ) / VDISK_CLUSTER_SIZE ) \
* VDISK_CLUSTER_COUNT )
/** Number of reserved sectors */
#define VDISK_RESERVED_COUNT VDISK_CLUSTER_COUNT
/** Starting cluster number for file */
#define VDISK_FILE_CLUSTER( idx ) \
( ( ( ( VDISK_FILE_COUNT - VDISK_PARTITION_LBA - \
VDISK_RESERVED_COUNT - VDISK_SECTORS_PER_FAT ) / \
VDISK_CLUSTER_COUNT ) + 2 ) + \
( (idx) * VDISK_FILE_CLUSTERS ) )
/** Total number of sectors within partition */
#define VDISK_PARTITION_COUNT \
( VDISK_RESERVED_COUNT + VDISK_SECTORS_PER_FAT + \
( VDISK_CLUSTERS * VDISK_CLUSTER_COUNT ) )
/** Number of sectors */
#define VDISK_COUNT ( VDISK_PARTITION_LBA + VDISK_PARTITION_COUNT )
/** Calculate sector from cluster */
#define VDISK_CLUSTER_SECTOR( cluster ) \
( ( ( (cluster) - 2 ) * VDISK_CLUSTER_COUNT ) + \
VDISK_RESERVED_COUNT + VDISK_SECTORS_PER_FAT )
/*****************************************************************************
*
* Master Boot Record
*
*****************************************************************************
*/
/** Master Boot Record LBA */
#define VDISK_MBR_LBA 0x00000000
/** Master Boot Record sector count */
#define VDISK_MBR_COUNT 1
/** Partition table entry */
struct vdisk_partition {
/** Bootable flag */
uint8_t bootable;
/** C/H/S start address */
uint8_t chs_start[3];
/** System indicator (partition type) */
uint8_t type;
/** C/H/S end address */
uint8_t chs_end[3];
/** Linear start address */
uint32_t start;
/** Linear length */
uint32_t length;
} __attribute__ (( packed ));
/** Master Boot Record */
struct vdisk_mbr {
/** Code area */
uint8_t code[440];
/** Disk signature */
uint32_t signature;
/** Padding */
uint8_t pad[2];
/** Partition table */
struct vdisk_partition partitions[4];
/** 0x55aa signature */
uint16_t magic;
} __attribute__ (( packed ));
/** MBR boot partition indiciator */
#define VDISK_MBR_BOOTABLE 0x80
/** MBR type indicator for FAT32 */
#define VDISK_MBR_TYPE_FAT32 0x0c
/** MBR signature */
#define VDISK_MBR_SIGNATURE 0xc0ffeeee
/** MBR magic */
#define VDISK_MBR_MAGIC 0xaa55
/*****************************************************************************
*
* Volume Boot Record
*
*****************************************************************************
*/
/** Volume Boot Record LBA */
#define VDISK_VBR_LBA VDISK_PARTITION_LBA
/** Volume Boot Record sector count */
#define VDISK_VBR_COUNT 1
/** Volume Boot Record */
struct vdisk_vbr {
/** Jump instruction */
uint8_t jump[3];
/** OEM identifier */
char oemid[8];
/** Number of bytes per sector */
uint16_t bytes_per_sector;
/** Number of sectors per cluster */
uint8_t sectors_per_cluster;
/** Number of reserved sectors */
uint16_t reserved_sectors;
/** Number of FATs */
uint8_t fats;
/** Number of root directory entries (FAT12/FAT16 only) */
uint16_t root_directory_entries;
/** Total number of sectors (0 if more than 65535) */
uint16_t sectors_short;
/** Media descriptor type */
uint8_t media;
/** Number of sectors per FAT (FAT12/FAT16 only) */
uint16_t sectors_per_fat_short;
/** Number of sectors per track */
uint16_t sectors_per_track;
/** Number of heads */
uint16_t heads;
/** Number of hidden sectors (i.e. LBA of start of partition) */
uint32_t hidden_sectors;
/** Total number of sectors */
uint32_t sectors;
/* FAT32-specific fields */
/** Sectors per FAT */
uint32_t sectors_per_fat;
/** Flags */
uint16_t flags;
/** FAT version number */
uint16_t version;
/** Root directory cluster */
uint32_t root;
/** FSInfo sector */
uint16_t fsinfo;
/** Backup boot sector */
uint16_t backup;
/** Reserved */
uint8_t reserved[12];
/** Drive number */
uint8_t drive;
/** Windows NT flags */
uint8_t nt_flags;
/** Signature */
uint8_t signature;
/** Volume ID serial */
uint32_t serial;
/** Label (space-padded) */
char label[11];
/** System identifier */
char system[8];
/** Boot code */
uint8_t code[420];
/** 0x55aa signature */
uint16_t magic;
} __attribute__ (( packed ));
/** VBR jump instruction
*
* bootmgr.exe will actually fail unless this is present. Someone
* must read specification documents without bothering to understand
* what's really happening.
*/
#define VDISK_VBR_JUMP_WTF_MS 0xe9
/** VBR OEM ID */
#define VDISK_VBR_OEMID "wimboot\0"
/** VBR media type */
#define VDISK_VBR_MEDIA 0xf8
/** VBR signature */
#define VDISK_VBR_SIGNATURE 0x29
/** VBR serial number */
#define VDISK_VBR_SERIAL 0xf00df00d
/** VBR label */
#define VDISK_VBR_LABEL "wimboot "
/** VBR system identifier */
#define VDISK_VBR_SYSTEM "FAT32 "
/** VBR magic */
#define VDISK_VBR_MAGIC 0xaa55
/*****************************************************************************
*
* FSInfo
*
*****************************************************************************
*/
/** FSInfo sector */
#define VDISK_FSINFO_SECTOR 0x00000001
/** FSInfo LBA */
#define VDISK_FSINFO_LBA ( VDISK_VBR_LBA + VDISK_FSINFO_SECTOR )
/** FSInfo sector count */
#define VDISK_FSINFO_COUNT 1
/** FSInfo */
struct vdisk_fsinfo {
/** First signature */
uint32_t magic1;
/** Reserved */
uint8_t reserved_1[480];
/** Second signature */
uint32_t magic2;
/** Free cluster count */
uint32_t free_count;
/** Next free cluster */
uint32_t next_free;
/** Reserved */
uint8_t reserved_2[12];
/** Third signature */
uint32_t magic3;
} __attribute__ (( packed ));
/** FSInfo first signature */
#define VDISK_FSINFO_MAGIC1 0x41615252
/** FSInfo second signature */
#define VDISK_FSINFO_MAGIC2 0x61417272
/** FSInfo next free cluster */
#define VDISK_FSINFO_NEXT_FREE 0xffffffff /* No free clusters */
/** FSInfo third signature */
#define VDISK_FSINFO_MAGIC3 0xaa550000
/*****************************************************************************
*
* Backup Volume Boot Record
*
*****************************************************************************
*/
/** Backup Volume Boot Record sector */
#define VDISK_BACKUP_VBR_SECTOR 0x00000006
/** Backup Volume Boot Record LBA */
#define VDISK_BACKUP_VBR_LBA ( VDISK_VBR_LBA + VDISK_BACKUP_VBR_SECTOR )
/** Backup Volume Boot Record sector count */
#define VDISK_BACKUP_VBR_COUNT 1
/*****************************************************************************
*
* File Allocation Table
*
*****************************************************************************
*/
/** FAT sector */
#define VDISK_FAT_SECTOR VDISK_RESERVED_COUNT
/** FAT LBA */
#define VDISK_FAT_LBA ( VDISK_VBR_LBA + VDISK_FAT_SECTOR )
/** FAT sector count */
#define VDISK_FAT_COUNT VDISK_SECTORS_PER_FAT
/** FAT end marker */
#define VDISK_FAT_END_MARKER 0x0ffffff8
/*****************************************************************************
*
* Directory entries
*
*****************************************************************************
*/
/** An 8.3 filename record */
struct vdisk_short_filename {
/** Filename */
union {
/** Structured 8.3 base name and extension */
struct {
/** Base name */
char base[8];
/** Extension */
char ext[3];
} __attribute__ (( packed ));
/** Raw bytes */
uint8_t raw[11];
} filename;
/** Attributes */
uint8_t attr;
/** Reserved */
uint8_t reserved;
/** Creation time in tenths of a second */
uint8_t created_deciseconds;
/** Creation time (HMS packed) */
uint16_t created_time;
/** Creation date (YMD packed) */
uint16_t created_date;
/** Last accessed date (YMD packed) */
uint16_t accessed_date;
/** High 16 bits of starting cluster number */
uint16_t cluster_high;
/** Modification time (HMS packed) */
uint16_t modified_time;
/** Modification date (YMD packed) */
uint16_t modified_date;
/** Low 16 bits of starting cluster number */
uint16_t cluster_low;
/** Size */
uint32_t size;
} __attribute__ (( packed ));
/** A long filename record */
struct vdisk_long_filename {
/** Sequence number */
uint8_t sequence;
/** Name characters */
uint16_t name_1[5];
/** Attributes */
uint8_t attr;
/** Type */
uint8_t type;
/** Checksum of 8.3 name */
uint8_t checksum;
/** Name characters */
uint16_t name_2[6];
/** Reserved */
uint16_t reserved;
/** Name characters */
uint16_t name_3[2];
} __attribute__ (( packed ));
/** Directory entry attributes */
enum vdisk_directory_entry_attributes {
VDISK_READ_ONLY = 0x01,
VDISK_HIDDEN = 0x02,
VDISK_SYSTEM = 0x04,
VDISK_VOLUME_LABEL = 0x08,
VDISK_DIRECTORY = 0x10,
};
/** Long filename end-of-sequence marker */
#define VDISK_LFN_END 0x40
/** Long filename attributes */
#define VDISK_LFN_ATTR \
( VDISK_READ_ONLY | VDISK_HIDDEN | VDISK_SYSTEM | VDISK_VOLUME_LABEL )
/** A directory entry */
union vdisk_directory_entry {
/** Deleted file marker */
uint8_t deleted;
/** 8.3 filename */
struct vdisk_short_filename dos;
/** Long filename */
struct vdisk_long_filename lfn;
} __attribute__ (( packed ));
/** Magic marker for deleted files */
#define VDISK_DIRENT_DELETED 0xe5
/** Number of directory entries per sector */
#define VDISK_DIRENT_PER_SECTOR \
( VDISK_SECTOR_SIZE / \
sizeof ( union vdisk_directory_entry ) )
/** A directory sector */
struct vdisk_directory {
/** Entries */
union vdisk_directory_entry entry[VDISK_DIRENT_PER_SECTOR];
} __attribute__ (( packed ));
/*****************************************************************************
*
* Root directory
*
*****************************************************************************
*/
/** Root directory cluster */
#define VDISK_ROOT_CLUSTER 2
/** Root directory sector */
#define VDISK_ROOT_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_ROOT_CLUSTER )
/** Root directory LBA */
#define VDISK_ROOT_LBA ( VDISK_VBR_LBA + VDISK_ROOT_SECTOR )
/*****************************************************************************
*
* Boot directory
*
*****************************************************************************
*/
/** Boot directory cluster */
#define VDISK_BOOT_CLUSTER 3
/** Boot directory sector */
#define VDISK_BOOT_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_BOOT_CLUSTER )
/** Boot directory LBA */
#define VDISK_BOOT_LBA ( VDISK_VBR_LBA + VDISK_BOOT_SECTOR )
/*****************************************************************************
*
* Sources directory
*
*****************************************************************************
*/
/** Sources directory cluster */
#define VDISK_SOURCES_CLUSTER 4
/** Sources directory sector */
#define VDISK_SOURCES_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_SOURCES_CLUSTER )
/** Sources directory LBA */
#define VDISK_SOURCES_LBA ( VDISK_VBR_LBA + VDISK_SOURCES_SECTOR )
/*****************************************************************************
*
* Fonts directory
*
*****************************************************************************
*/
/** Fonts directory cluster */
#define VDISK_FONTS_CLUSTER 5
/** Fonts directory sector */
#define VDISK_FONTS_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_FONTS_CLUSTER )
/** Fonts directory LBA */
#define VDISK_FONTS_LBA ( VDISK_VBR_LBA + VDISK_FONTS_SECTOR )
/*****************************************************************************
*
* Resources directory
*
*****************************************************************************
*/
/** Resources directory cluster */
#define VDISK_RESOURCES_CLUSTER 6
/** Resources directory sector */
#define VDISK_RESOURCES_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_RESOURCES_CLUSTER )
/** Resources directory LBA */
#define VDISK_RESOURCES_LBA ( VDISK_VBR_LBA + VDISK_RESOURCES_SECTOR )
/*****************************************************************************
*
* EFI directory
*
*****************************************************************************
*/
/** EFI directory cluster */
#define VDISK_EFI_CLUSTER 7
/** EFI directory sector */
#define VDISK_EFI_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_EFI_CLUSTER )
/** EFI directory LBA */
#define VDISK_EFI_LBA ( VDISK_VBR_LBA + VDISK_EFI_SECTOR )
/*****************************************************************************
*
* Microsoft directory
*
*****************************************************************************
*/
/** Microsoft directory cluster */
#define VDISK_MICROSOFT_CLUSTER 8
/** Microsoft directory sector */
#define VDISK_MICROSOFT_SECTOR VDISK_CLUSTER_SECTOR ( VDISK_MICROSOFT_CLUSTER )
/** Microsoft directory LBA */
#define VDISK_MICROSOFT_LBA ( VDISK_VBR_LBA + VDISK_MICROSOFT_SECTOR )
/*****************************************************************************
*
* Files
*
*****************************************************************************
*/
/** Maximum virtual filename length (excluding NUL) */
#define VDISK_NAME_LEN 31
/** A virtual file */
struct vdisk_file {
/** Filename */
char name[ VDISK_NAME_LEN + 1 /* NUL */ ];
/** Opaque token */
void *opaque;
/** Length (excluding any zero-padding) */
size_t len;
/** Length (including any zero-padding) */
size_t xlen;
/** Read data
*
* @v file Virtual file
* @v data Data buffer
* @v offset Starting offset
* @v len Length
*/
void ( * read ) ( struct vdisk_file *file, void *data, size_t offset,
size_t len );
/** Patch data (optional)
*
* @v file Virtual file
* @v data Data buffer
* @v offset Starting offset
* @v len Length
*/
void ( * patch ) ( struct vdisk_file *file, void *data, size_t offset,
size_t len );
};
extern struct vdisk_file vdisk_files[VDISK_MAX_FILES];
extern void vdisk_read ( uint64_t lba, unsigned int count, void *data );
extern struct vdisk_file *
vdisk_add_file ( const char *name, void *opaque, size_t len,
void ( * read ) ( struct vdisk_file *file, void *data,
size_t offset, size_t len ) );
extern void
vdisk_patch_file ( struct vdisk_file *file,
void ( * patch ) ( struct vdisk_file *file, void *data,
size_t offset, size_t len ) );
#endif /* _VDISK_H */
/*
* Quick and dirty wrapper around iPXE's unmodified vsprintf.c
*
*/
#include <stdint.h>
#include <string.h>
#include "wimboot.h"
#define FILE_LICENCE(x)
#include "ipxe/vsprintf.c"
#ifndef _WCHAR_H
#define _WCHAR_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Wide characters
*
*/
#include <stdint.h>
typedef void mbstate_t;
/**
* Convert wide character to multibyte sequence
*
* @v buf Buffer
* @v wc Wide character
* @v ps Shift state
* @ret len Number of characters written
*
* This is a stub implementation, sufficient to handle basic ASCII
* characters.
*/
static inline size_t wcrtomb ( char *buf, wchar_t wc,
mbstate_t *ps __attribute__ (( unused )) ) {
*buf = wc;
return 1;
}
extern int wcscasecmp ( const wchar_t *str1, const wchar_t *str2 );
extern size_t wcslen ( const wchar_t *str );
extern wchar_t * wcschr ( const wchar_t *str, wchar_t c );
extern char *strchr(const char *str, char c);
#endif /* _WCHAR_H */
#ifndef _WCTYPE_H
#define _WCTYPE_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Wide character types
*
* We don't actually care about wide characters. Internationalisation
* is a user interface concern, and has absolutely no place in the
* boot process. However, UEFI uses wide characters and so we have to
* at least be able to handle the ASCII subset of UCS-2.
*
*/
#include <ctype.h>
static inline int iswlower ( wint_t c ) {
return islower ( c );
}
static inline int iswupper ( wint_t c ) {
return isupper ( c );
}
static inline int towupper ( wint_t c ) {
return toupper ( c );
}
static inline int iswspace ( wint_t c ) {
return isspace ( c );
}
#endif /* _WCTYPE_H */
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM images
*
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <assert.h>
#include "wimboot.h"
#include "vdisk.h"
#include "lzx.h"
#include "wim.h"
/** WIM chunk buffer */
static struct wim_chunk_buffer wim_chunk_buffer;
/**
* Get WIM header
*
* @v file Virtual file
* @v header WIM header to fill in
* @ret rc Return status code
*/
int wim_header ( struct vdisk_file *file, struct wim_header *header ) {
/* Sanity check */
if ( sizeof ( *header ) > file->len ) {
DBG ( "WIM file too short (%#zx bytes)\n", file->len );
return -1;
}
/* Read WIM header */
file->read ( file, header, 0, sizeof ( *header ) );
return 0;
}
/**
* Get compressed chunk offset
*
* @v file Virtual file
* @v resource Resource
* @v chunk Chunk number
* @v offset Offset to fill in
* @ret rc Return status code
*/
static int wim_chunk_offset ( struct vdisk_file *file,
struct wim_resource_header *resource,
unsigned int chunk, size_t *offset ) {
size_t zlen = ( resource->zlen__flags & WIM_RESHDR_ZLEN_MASK );
unsigned int chunks;
size_t offset_offset;
size_t offset_len;
size_t chunks_len;
union {
uint32_t offset_32;
uint64_t offset_64;
} u;
/* Special case: zero-length files have no chunks */
if ( ! resource->len ) {
*offset = 0;
return 0;
}
/* Calculate chunk parameters */
chunks = ( ( resource->len + WIM_CHUNK_LEN - 1 ) / WIM_CHUNK_LEN );
offset_len = ( ( resource->len > 0xffffffffULL ) ?
sizeof ( u.offset_64 ) : sizeof ( u.offset_32 ) );
chunks_len = ( ( chunks - 1 ) * offset_len );
/* Sanity check */
if ( chunks_len > zlen ) {
DBG ( "Resource too short for %d chunks\n", chunks );
return -1;
}
/* Special case: chunk 0 has no offset field */
if ( ! chunk ) {
*offset = chunks_len;
return 0;
}
/* Treat out-of-range chunks as being at the end of the
* resource, to allow for length calculation on the final
* chunk.
*/
if ( chunk >= chunks ) {
*offset = zlen;
return 0;
}
/* Otherwise, read the chunk offset */
offset_offset = ( ( chunk - 1 ) * offset_len );
file->read ( file, &u, ( resource->offset + offset_offset ),
offset_len );
*offset = ( chunks_len + ( ( offset_len == sizeof ( u.offset_64 ) ) ?
u.offset_64 : u.offset_32 ) );
if ( *offset > zlen ) {
DBG ( "Chunk %d offset lies outside resource\n", chunk );
return -1;
}
return 0;
}
/**
* Read chunk from a compressed resource
*
* @v file Virtual file
* @v header WIM header
* @v resource Resource
* @v chunk Chunk number
* @v buf Chunk buffer
* @ret rc Return status code
*/
static int wim_chunk ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *resource,
unsigned int chunk, struct wim_chunk_buffer *buf ) {
ssize_t ( * decompress ) ( const void *data, size_t len, void *buf );
unsigned int chunks;
size_t offset;
size_t next_offset;
size_t len;
size_t expected_out_len;
ssize_t out_len;
int rc;
/* Get chunk compressed data offset and length */
if ( ( rc = wim_chunk_offset ( file, resource, chunk,
&offset ) ) != 0 )
return rc;
if ( ( rc = wim_chunk_offset ( file, resource, ( chunk + 1 ),
&next_offset ) ) != 0 )
return rc;
len = ( next_offset - offset );
/* Calculate uncompressed length */
assert ( resource->len > 0 );
chunks = ( ( resource->len + WIM_CHUNK_LEN - 1 ) / WIM_CHUNK_LEN );
expected_out_len = WIM_CHUNK_LEN;
if ( chunk >= ( chunks - 1 ) )
expected_out_len -= ( -resource->len & ( WIM_CHUNK_LEN - 1 ) );
/* Read possibly-compressed data */
if ( len == expected_out_len ) {
/* Chunk did not compress; read raw data */
file->read ( file, buf->data, ( resource->offset + offset ),
len );
} else {
uint8_t zbuf[len];
/* Read compressed data into a temporary buffer */
file->read ( file, zbuf, ( resource->offset + offset ), len );
/* Identify decompressor */
if ( header->flags & WIM_HDR_LZX ) {
decompress = lzx_decompress;
} else {
DBG ( "Can't handle unknown compression scheme %#08x "
"for %#llx chunk %d at [%#llx+%#llx)\n",
header->flags, resource->offset,
chunk, ( resource->offset + offset ),
( resource->offset + offset + len ) );
return -1;
}
/* Decompress data */
out_len = decompress ( zbuf, len, NULL );
if ( out_len < 0 )
return out_len;
if ( ( ( size_t ) out_len ) != expected_out_len ) {
DBG ( "Unexpected output length %#lx (expected %#zx)\n",
out_len, expected_out_len );
return -1;
}
decompress ( zbuf, len, buf->data );
}
return 0;
}
/**
* Read from a (possibly compressed) resource
*
* @v file Virtual file
* @v header WIM header
* @v resource Resource
* @v data Data buffer
* @v offset Starting offset
* @v len Length
* @ret rc Return status code
*/
int wim_read ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *resource, void *data,
size_t offset, size_t len ) {
static struct vdisk_file *cached_file;
static size_t cached_resource_offset;
static unsigned int cached_chunk;
size_t zlen = ( resource->zlen__flags & WIM_RESHDR_ZLEN_MASK );
unsigned int chunk;
size_t skip_len;
size_t frag_len;
int rc;
/* Sanity checks */
if ( ( offset + len ) > resource->len ) {
DBG ( "Resource too short (%#llx bytes)\n", resource->len );
return -1;
}
if ( ( resource->offset + zlen ) > file->len ) {
DBG ( "Resource exceeds length of file\n" );
return -1;
}
/* If resource is uncompressed, just read the raw data */
if ( ! ( resource->zlen__flags & ( WIM_RESHDR_COMPRESSED |
WIM_RESHDR_PACKED_STREAMS ) ) ) {
file->read ( file, data, ( resource->offset + offset ), len );
return 0;
}
/* Read from each chunk overlapping the target region */
while ( len ) {
/* Calculate chunk number */
chunk = ( offset / WIM_CHUNK_LEN );
/* Read chunk, if not already cached */
if ( ( file != cached_file ) ||
( resource->offset != cached_resource_offset ) ||
( chunk != cached_chunk ) ) {
/* Read chunk */
if ( ( rc = wim_chunk ( file, header, resource, chunk,
&wim_chunk_buffer ) ) != 0 )
return rc;
/* Update cache */
cached_file = file;
cached_resource_offset = resource->offset;
cached_chunk = chunk;
}
/* Copy fragment from this chunk */
skip_len = ( offset % WIM_CHUNK_LEN );
frag_len = ( WIM_CHUNK_LEN - skip_len );
if ( frag_len > len )
frag_len = len;
memcpy ( data, ( wim_chunk_buffer.data + skip_len ), frag_len );
/* Move to next chunk */
data += frag_len;
offset += frag_len;
len -= frag_len;
}
return 0;
}
/**
* Get number of images
*
* @v file Virtual file
* @v header WIM header
* @v count Count of images to fill in
* @ret rc Return status code
*/
int wim_count ( struct vdisk_file *file, struct wim_header *header,
unsigned int *count ) {
struct wim_lookup_entry entry;
size_t offset;
int rc;
/* Count metadata entries */
for ( offset = 0 ; ( offset + sizeof ( entry ) ) <= header->lookup.len ;
offset += sizeof ( entry ) ) {
/* Read entry */
if ( ( rc = wim_read ( file, header, &header->lookup, &entry,
offset, sizeof ( entry ) ) ) != 0 )
return rc;
/* Check for metadata entries */
if ( entry.resource.zlen__flags & WIM_RESHDR_METADATA ) {
(*count)++;
DBG2 ( "...found image %d metadata at +%#zx\n",
*count, offset );
}
}
return 0;
}
/**
* Get WIM image metadata
*
* @v file Virtual file
* @v header WIM header
* @v index Image index, or 0 to use boot image
* @v meta Metadata to fill in
* @ret rc Return status code
*/
int wim_metadata ( struct vdisk_file *file, struct wim_header *header,
unsigned int index, struct wim_resource_header *meta ) {
struct wim_lookup_entry entry;
size_t offset;
unsigned int found = 0;
int rc;
/* If no image index is specified, just use the boot metadata */
if ( index == 0 ) {
memcpy ( meta, &header->boot, sizeof ( *meta ) );
return 0;
}
/* Look for metadata entry */
for ( offset = 0 ; ( offset + sizeof ( entry ) ) <= header->lookup.len ;
offset += sizeof ( entry ) ) {
/* Read entry */
if ( ( rc = wim_read ( file, header, &header->lookup, &entry,
offset, sizeof ( entry ) ) ) != 0 )
return rc;
/* Look for our target entry */
if ( entry.resource.zlen__flags & WIM_RESHDR_METADATA ) {
found++;
DBG2 ( "...found image %d metadata at +%#zx\n",
found, offset );
if ( found == index ) {
memcpy ( meta, &entry.resource,
sizeof ( *meta ) );
return 0;
}
}
}
/* Fail if index was not found */
DBG ( "Cannot find WIM image index %d in %s\n", index, file->name );
return -1;
}
/**
* Get directory entry
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v name Name
* @v offset Directory offset (will be updated)
* @v direntry Directory entry to fill in
* @ret rc Return status code
*/
static int wim_direntry ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta,
const wchar_t *name, size_t *offset,
struct wim_directory_entry *direntry ) {
wchar_t name_buf[ wcslen ( name ) + 1 /* NUL */ ];
int rc;
/* Search directory */
for ( ; ; *offset += direntry->len ) {
/* Read length field */
if ( ( rc = wim_read ( file, header, meta, direntry, *offset,
sizeof ( direntry->len ) ) ) != 0 )
return rc;
/* Check for end of this directory */
if ( ! direntry->len ) {
DBG ( "...directory entry \"%ls\" not found\n", name );
return -1;
}
/* Read fixed-length portion of directory entry */
if ( ( rc = wim_read ( file, header, meta, direntry, *offset,
sizeof ( *direntry ) ) ) != 0 )
return rc;
/* Check name length */
if ( direntry->name_len > sizeof ( name_buf ) )
continue;
/* Read name */
if ( ( rc = wim_read ( file, header, meta, &name_buf,
( *offset + sizeof ( *direntry ) ),
sizeof ( name_buf ) ) ) != 0 )
return rc;
/* Check name */
if ( wcscasecmp ( name, name_buf ) != 0 )
continue;
DBG2 ( "...found entry \"%ls\"\n", name );
return 0;
}
}
/**
* Get directory entry for a path
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v path Path to file/directory
* @v offset Directory entry offset to fill in
* @v direntry Directory entry to fill in
* @ret rc Return status code
*/
int wim_path ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
size_t *offset, struct wim_directory_entry *direntry ) {
wchar_t path_copy[ wcslen ( path ) + 1 /* WNUL */ ];
struct wim_security_header security;
wchar_t *name;
wchar_t *next;
int rc;
/* Read security data header */
if ( ( rc = wim_read ( file, header, meta, &security, 0,
sizeof ( security ) ) ) != 0 )
return rc;
/* Get root directory offset */
direntry->subdir = ( ( security.len + sizeof ( uint64_t ) - 1 ) &
~( sizeof ( uint64_t ) - 1 ) );
/* Find directory entry */
name = memcpy ( path_copy, path, sizeof ( path_copy ) );
do {
next = wcschr ( name, L'\\' );
if ( next )
*next = L'\0';
*offset = direntry->subdir;
if ( ( rc = wim_direntry ( file, header, meta, name, offset,
direntry ) ) != 0 )
return rc;
name = ( next + 1 );
} while ( next );
return 0;
}
/**
* Get file resource
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v path Path to file
* @v resource File resource to fill in
* @ret rc Return status code
*/
int wim_file ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
struct wim_resource_header *resource ) {
struct wim_directory_entry direntry;
struct wim_lookup_entry entry;
size_t offset;
int rc;
/* Find directory entry */
if ( ( rc = wim_path ( file, header, meta, path, &offset,
&direntry ) ) != 0 )
return rc;
/* File matching file entry */
for ( offset = 0 ; ( offset + sizeof ( entry ) ) <= header->lookup.len ;
offset += sizeof ( entry ) ) {
/* Read entry */
if ( ( rc = wim_read ( file, header, &header->lookup, &entry,
offset, sizeof ( entry ) ) ) != 0 )
return rc;
/* Look for our target entry */
if ( memcmp ( &entry.hash, &direntry.hash,
sizeof ( entry.hash ) ) == 0 ) {
DBG ( "...found file \"%ls\"\n", path );
memcpy ( resource, &entry.resource,
sizeof ( *resource ) );
return 0;
}
}
DBG ( "Cannot find file %ls\n", path );
return -1;
}
/**
* Get length of a directory
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v offset Directory offset
* @v len Directory length to fill in (excluding terminator)
* @ret rc Return status code
*/
int wim_dir_len ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, size_t offset,
size_t *len ) {
struct wim_directory_entry direntry;
int rc;
/* Search directory */
for ( *len = 0 ; ; *len += direntry.len ) {
/* Read length field */
if ( ( rc = wim_read ( file, header, meta, &direntry,
( offset + *len ),
sizeof ( direntry.len ) ) ) != 0 )
return rc;
/* Check for end of this directory */
if ( ! direntry.len )
return 0;
}
}
#ifndef _WIM_H
#define _WIM_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM images
*
* The file format is documented in the document "Windows Imaging File
* Format (WIM)", available from
*
* http://www.microsoft.com/en-us/download/details.aspx?id=13096
*
* The wimlib source code is also a useful reference.
*
*/
#include <stdint.h>
/** A WIM resource header */
struct wim_resource_header {
/** Compressed length and flags */
uint64_t zlen__flags;
/** Offset */
uint64_t offset;
/** Uncompressed length */
uint64_t len;
} __attribute__ (( packed ));
/** WIM resource header length mask */
#define WIM_RESHDR_ZLEN_MASK 0x00ffffffffffffffULL
/** WIM resource header flags */
enum wim_resource_header_flags {
/** Resource contains metadata */
WIM_RESHDR_METADATA = ( 0x02ULL << 56 ),
/** Resource is compressed */
WIM_RESHDR_COMPRESSED = ( 0x04ULL << 56 ),
/** Resource is compressed using packed streams */
WIM_RESHDR_PACKED_STREAMS = ( 0x10ULL << 56 ),
};
/** A WIM header */
struct wim_header {
/** Signature */
uint8_t signature[8];
/** Header length */
uint32_t header_len;
/** Verson */
uint32_t version;
/** Flags */
uint32_t flags;
/** Chunk length */
uint32_t chunk_len;
/** GUID */
uint8_t guid[16];
/** Part number */
uint16_t part;
/** Total number of parts */
uint16_t parts;
/** Number of images */
uint32_t images;
/** Lookup table */
struct wim_resource_header lookup;
/** XML data */
struct wim_resource_header xml;
/** Boot metadata */
struct wim_resource_header boot;
/** Boot index */
uint32_t boot_index;
/** Integrity table */
struct wim_resource_header integrity;
/** Reserved */
uint8_t reserved[60];
} __attribute__ (( packed ));;
/** WIM header flags */
enum wim_header_flags {
/** WIM uses Xpress compresson */
WIM_HDR_XPRESS = 0x00020000,
/** WIM uses LZX compression */
WIM_HDR_LZX = 0x00040000,
};
/** A WIM file hash */
struct wim_hash {
/** SHA-1 hash */
uint8_t sha1[20];
} __attribute__ (( packed ));
/** A WIM lookup table entry */
struct wim_lookup_entry {
/** Resource header */
struct wim_resource_header resource;
/** Part number */
uint16_t part;
/** Reference count */
uint32_t refcnt;
/** Hash */
struct wim_hash hash;
} __attribute__ (( packed ));
/** WIM chunk length */
#define WIM_CHUNK_LEN 32768
/** A WIM chunk buffer */
struct wim_chunk_buffer {
/** Data */
uint8_t data[WIM_CHUNK_LEN];
};
/** Security data */
struct wim_security_header {
/** Length */
uint32_t len;
/** Number of entries */
uint32_t count;
} __attribute__ (( packed ));
/** Directory entry */
struct wim_directory_entry {
/** Length */
uint64_t len;
/** Attributes */
uint32_t attributes;
/** Security ID */
uint32_t security;
/** Subdirectory offset */
uint64_t subdir;
/** Reserved */
uint8_t reserved1[16];
/** Creation time */
uint64_t created;
/** Last access time */
uint64_t accessed;
/** Last written time */
uint64_t written;
/** Hash */
struct wim_hash hash;
/** Reserved */
uint8_t reserved2[12];
/** Streams */
uint16_t streams;
/** Short name length */
uint16_t short_name_len;
/** Name length */
uint16_t name_len;
} __attribute__ (( packed ));
/** Normal file */
#define WIM_ATTR_NORMAL 0x00000080UL
/** No security information exists for this file */
#define WIM_NO_SECURITY 0xffffffffUL
/** Windows complains if the time fields are left at zero */
#define WIM_MAGIC_TIME 0x1a7b83d2ad93000ULL
extern int wim_header ( struct vdisk_file *file, struct wim_header *header );
extern int wim_count ( struct vdisk_file *file, struct wim_header *header,
unsigned int *count );
extern int wim_metadata ( struct vdisk_file *file, struct wim_header *header,
unsigned int index, struct wim_resource_header *meta);
extern int wim_read ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *resource, void *data,
size_t offset, size_t len );
extern int wim_path ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
size_t *offset, struct wim_directory_entry *direntry );
extern int wim_file ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
struct wim_resource_header *resource );
extern int wim_dir_len ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, size_t offset,
size_t *len );
#endif /* _WIM_H */
#ifndef _WIMBOOT_H
#define _WIMBOOT_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM boot loader
*
*/
/** Debug switch */
#ifndef DEBUG
#define DEBUG 1
#endif
/** Base segment address
*
* We place everything at 2000:0000, since this region is used by the
* Microsoft first-stage loaders (e.g. pxeboot.n12, etfsboot.com).
*/
#define BASE_SEG 0x2000
/** Base linear address */
#define BASE_ADDRESS ( BASE_SEG << 4 )
/** 64 bit long mode code segment */
#define LM_CS 0x10
/** 32 bit protected mode flat code segment */
#define FLAT_CS 0x20
/** 32 bit protected mode flat data segment */
#define FLAT_DS 0x30
/** 16 bit real mode code segment */
#define REAL_CS 0x50
/** 16 bit real mode data segment */
#define REAL_DS 0x60
#ifndef ASSEMBLY
#include <stdint.h>
#include <bootapp.h>
#include <cmdline.h>
/** Construct wide-character version of a string constant */
#define L( x ) _L ( x )
#define _L( x ) L ## x
/** Page size */
#define PAGE_SIZE 4096
/**
* Calculate start page number
*
* @v address Address
* @ret page Start page number
*/
static inline unsigned int page_start ( const void *address ) {
return ( ( ( intptr_t ) address ) / PAGE_SIZE );
}
/**
* Calculate end page number
*
* @v address Address
* @ret page End page number
*/
static inline unsigned int page_end ( const void *address ) {
return ( ( ( ( intptr_t ) address ) + PAGE_SIZE - 1 ) / PAGE_SIZE );
}
/**
* Calculate page length
*
* @v start Start address
* @v end End address
* @ret num_pages Number of pages
*/
static inline unsigned int page_len ( const void *start, const void *end ) {
return ( page_end ( end ) - page_start ( start ) );
}
/**
* Bochs magic breakpoint
*
*/
static inline void bochsbp ( void ) {
__asm__ __volatile__ ( "xchgw %bx, %bx" );
}
/** Debugging output */
#define DBG(...) do { \
if ( ( DEBUG & 1 ) && ( ! cmdline_quiet ) ) { \
printf ( __VA_ARGS__ ); \
} \
} while ( 0 )
/** Verbose debugging output */
#define DBG2(...) do { \
if ( ( DEBUG & 2 ) && ( ! cmdline_quiet ) ) { \
printf ( __VA_ARGS__ ); \
} \
} while ( 0 )
/* Branch prediction macros */
#define likely( x ) __builtin_expect ( !! (x), 1 )
#define unlikely( x ) __builtin_expect ( (x), 0 )
/* Mark parameter as unused */
#define __unused __attribute__ (( unused ))
#if __x86_64__
static inline void call_real ( struct bootapp_callback_params *params ) {
/* Not available in 64-bit mode */
( void ) params;
}
static inline void call_interrupt ( struct bootapp_callback_params *params ) {
/* Not available in 64-bit mode */
( void ) params;
}
static inline void reboot ( void ) {
/* Not available in 64-bit mode */
}
#else
extern void call_real ( struct bootapp_callback_params *params );
extern void call_interrupt ( struct bootapp_callback_params *params );
extern void __attribute__ (( noreturn )) reboot ( void );
#endif
extern void __attribute__ (( noreturn, format ( printf, 1, 2 ) ))
die ( const char *fmt, ... );
extern unsigned long __stack_chk_guard;
extern void init_cookie ( void );
#endif /* ASSEMBLY */
#endif /* _WIMBOOT_H */
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM virtual files
*
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <wchar.h>
#include "wimboot.h"
#include "vdisk.h"
#include "wim.h"
#include "wimfile.h"
/** A WIM virtual file */
struct wim_file {
/** Underlying virtual file */
struct vdisk_file *file;
/** WIM header */
struct wim_header header;
/** Resource */
struct wim_resource_header resource;
};
/** Maximum number of WIM virtual files */
#define WIM_MAX_FILES 8
/** WIM virtual files */
static struct wim_file wim_files[WIM_MAX_FILES];
/**
* Read from WIM virtual file
*
* @v file Virtual file
* @v data Data buffer
* @v offset Offset
* @v len Length
*/
static void wim_read_file ( struct vdisk_file *file, void *data,
size_t offset, size_t len ) {
struct wim_file *wfile = file->opaque;
int rc;
/* Read from resource */
if ( ( rc = wim_read ( wfile->file, &wfile->header, &wfile->resource,
data, offset, len ) ) != 0 ) {
die ( "Could not read from WIM virtual file\n" );
}
}
/**
* Add WIM virtual file
*
* @v file Underlying virtual file
* @v index Image index, or 0 to use boot image
* @v path Path to file within WIM
* @v wname New virtual file name
* @ret file Virtual file, or NULL if not found
*/
struct vdisk_file * wim_add_file ( struct vdisk_file *file, unsigned int index,
const wchar_t *path, const wchar_t *wname ) {
static unsigned int wim_file_idx = 0;
struct wim_resource_header meta;
struct wim_file *wfile;
char name[ VDISK_NAME_LEN + 1 /* NUL */ ];
unsigned int i;
int rc;
/* Sanity check */
if ( wim_file_idx >= WIM_MAX_FILES )
die ( "Too many WIM files\n" );
wfile = &wim_files[wim_file_idx];
/* Construct ASCII file name */
snprintf ( name, sizeof ( name ), "%ls", wname );
/* Skip files already added explicitly */
for ( i = 0 ; i < VDISK_MAX_FILES ; i++ ) {
if ( strcasecmp ( name, vdisk_files[i].name ) == 0 )
return NULL;
}
/* Get WIM header */
if ( ( rc = wim_header ( file, &wfile->header ) ) != 0 )
return NULL;
/* Get image metadata */
if ( ( rc = wim_metadata ( file, &wfile->header, index, &meta ) ) != 0 )
return NULL;
/* Get file resource */
if ( ( rc = wim_file ( file, &wfile->header, &meta, path,
&wfile->resource ) ) != 0 )
return NULL;
/* Add virtual file */
wim_file_idx++;
wfile->file = file;
return vdisk_add_file ( name, wfile, wfile->resource.len,
wim_read_file );
}
/**
* Add WIM virtual files
*
* @v file Underlying virtual file
* @v index Image index, or 0 to use boot image
* @v paths List of paths to files within WIM
*/
void wim_add_files ( struct vdisk_file *file, unsigned int index,
const wchar_t **paths ) {
const wchar_t **path;
const wchar_t *wname;
const wchar_t *tmp;
/* Add any existent files within the list */
for ( path = paths ; *path ; path++ ) {
/* Construct file name */
wname = *path;
for ( tmp = wname ; *tmp ; tmp++ ) {
if ( *tmp == L'\\' )
wname = ( tmp + 1 );
}
/* Add virtual file, if existent */
wim_add_file ( file, index, *path, wname );
}
}
#ifndef _WIMFILE_H
#define _WIMFILE_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM virtual files
*
*/
#include <wchar.h>
struct vdisk_file;
extern struct vdisk_file * wim_add_file ( struct vdisk_file *file,
unsigned int index,
const wchar_t *path,
const wchar_t *wname );
extern void wim_add_files ( struct vdisk_file *file, unsigned int index,
const wchar_t **paths );
#endif /* _WIMFILE_H */
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM dynamic patching
*
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include "wimboot.h"
#include "cmdline.h"
#include "vdisk.h"
#include "sha1.h"
#include "wim.h"
#include "wimpatch.h"
/** Directory into which files are injected */
#define WIM_INJECT_DIR "\\Windows\\System32"
struct wim_patch;
/** A region of a patched WIM file */
struct wim_patch_region {
/** Name */
const char *name;
/** Opaque token */
void *opaque;
/** Starting offset of region */
size_t offset;
/** Length of region */
size_t len;
/** Patch region
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
int ( * patch ) ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset, size_t len );
};
/** Regions of a patched WIM directory containing injected files */
struct wim_patch_dir_regions {
/** Subdirectory offset within parent entry */
struct wim_patch_region subdir;
/** Copy of original directory entries */
struct wim_patch_region copy;
/** Injected file directory entries */
struct wim_patch_region file[VDISK_MAX_FILES];
} __attribute__ (( packed ));
/** Regions of a patched WIM file */
union wim_patch_regions {
/** Structured list of regions */
struct {
/** WIM header */
struct wim_patch_region header;
/** Injected file contents */
struct wim_patch_region file[VDISK_MAX_FILES];
/** Injected lookup table */
struct {
/** Uncompressed copy of original lookup table */
struct wim_patch_region copy;
/** Injected boot image metadata lookup table entry */
struct wim_patch_region boot;
/** Injected file lookup table entries */
struct wim_patch_region file[VDISK_MAX_FILES];
} __attribute__ (( packed )) lookup;
/** Injected boot image metadata */
struct {
/** Uncompressed copy of original metadata */
struct wim_patch_region copy;
/** Patched directory containing injected files */
struct wim_patch_dir_regions dir;
} __attribute__ (( packed )) boot;
} __attribute__ (( packed ));
/** Unstructured list of regions */
struct wim_patch_region region[0];
};
/** An injected directory entry */
struct wim_patch_dir_entry {
/** Directory entry */
struct wim_directory_entry dir;
/** Name */
wchar_t name[ VDISK_NAME_LEN + 1 /* wNUL */ ];
} __attribute__ (( packed ));
/** A directory containing injected files */
struct wim_patch_dir {
/** Name */
const char *name;
/** Offset to parent directory entry */
size_t parent;
/** Offset to original directory entries */
size_t offset;
/** Length of original directory entries (excluding terminator) */
size_t len;
/** Offset to modified directory entries */
size_t subdir;
};
/** A patched WIM file */
struct wim_patch {
/** Virtual file */
struct vdisk_file *file;
/** Patched WIM header */
struct wim_header header;
/** Original lookup table */
struct wim_resource_header lookup;
/** Original boot image metadata */
struct wim_resource_header boot;
/** Original boot index */
uint32_t boot_index;
/** Directory containing injected files */
struct wim_patch_dir dir;
/** Patched regions */
union wim_patch_regions regions;
};
/**
* Align WIM offset to nearest qword
*
* @v len Length
* @ret len Aligned length
*/
static size_t wim_align ( size_t len ) {
return ( ( len + 0x07 ) & ~0x07 );
}
/**
* Calculate WIM hash
*
* @v vfile Virtual file
* @v hash Hash to fill in
*/
static void wim_hash ( struct vdisk_file *vfile, struct wim_hash *hash ) {
uint8_t ctx[SHA1_CTX_SIZE];
uint8_t buf[512];
size_t offset;
size_t len;
/* Calculate SHA-1 digest */
sha1_init ( ctx );
for ( offset = 0 ; offset < vfile->len ; offset += len ) {
/* Read block */
len = ( vfile->len - offset );
if ( len > sizeof ( buf ) )
len = sizeof ( buf );
vfile->read ( vfile, buf, offset, len );
/* Update digest */
sha1_update ( ctx, buf, len );
}
sha1_final ( ctx, hash->sha1 );
}
/**
* Determine whether or not to inject file
*
* @v vfile Virtual file
* @ret inject Inject this file
*/
static int wim_inject_file ( struct vdisk_file *vfile ) {
size_t name_len;
const char *ext;
/* Ignore non-existent files */
if ( ! vfile->read )
return 0;
/* Ignore wimboot itself */
if ( strcasecmp ( vfile->name, "wimboot" ) == 0 )
return 0;
/* Ignore bootmgr files */
if ( strcasecmp ( vfile->name, "bootmgr" ) == 0 )
return 0;
if ( strcasecmp ( vfile->name, "bootmgr.exe" ) == 0 )
return 0;
/* Ignore BCD files */
if ( strcasecmp ( vfile->name, "BCD" ) == 0 )
return 0;
/* Locate file extension */
name_len = strlen ( vfile->name );
ext = ( ( name_len > 4 ) ? ( vfile->name + name_len - 4 ) : "" );
/* Ignore .wim files */
if ( strcasecmp ( ext, ".wim" ) == 0 )
return 0;
/* Ignore .sdi files */
if ( strcasecmp ( ext, ".sdi" ) == 0 )
return 0;
/* Ignore .efi files */
if ( strcasecmp ( ext, ".efi" ) == 0 )
return 0;
/* Ignore .ttf files */
if ( strcasecmp ( ext, ".ttf" ) == 0 )
return 0;
return 1;
}
/**
* Patch WIM header
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_header ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct wim_header *header = &patch->header;
/* Sanity checks */
assert ( offset < sizeof ( *header ) );
assert ( len <= ( sizeof ( *header ) - offset ) );
/* Copy patched header */
if ( patch->lookup.offset != patch->header.lookup.offset ) {
DBG2 ( "...patched WIM %s lookup table %#llx->%#llx\n",
region->name, patch->lookup.offset,
patch->header.lookup.offset );
}
if ( patch->boot.offset != patch->header.boot.offset ) {
DBG2 ( "...patched WIM %s boot metadata %#llx->%#llx\n",
region->name, patch->boot.offset,
patch->header.boot.offset );
}
if ( patch->boot_index != patch->header.boot_index ) {
DBG2 ( "...patched WIM %s boot index %d->%d\n", region->name,
patch->boot_index, patch->header.boot_index );
}
memcpy ( data, ( ( ( void * ) &patch->header ) + offset ), len );
return 0;
}
/**
* Patch injected file content
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_file ( struct wim_patch *patch __unused,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct vdisk_file *vfile = region->opaque;
/* Read from file */
vfile->read ( vfile, data, offset, len );
return 0;
}
/**
* Patch uncompressed copy of original lookup table
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_lookup_copy ( struct wim_patch *patch,
struct wim_patch_region *region __unused,
void *data, size_t offset, size_t len ) {
int rc;
/* Read original lookup table */
if ( ( rc = wim_read ( patch->file, &patch->header, &patch->lookup,
data, offset, len ) ) != 0 )
return rc;
return 0;
}
/**
* Patch injected boot image metadata lookup table entry
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_lookup_boot ( struct wim_patch *patch,
struct wim_patch_region *region __unused,
void *data, size_t offset, size_t len ) {
struct wim_lookup_entry entry;
/* Sanity checks */
assert ( offset < sizeof ( entry ) );
assert ( len <= ( sizeof ( entry ) - offset ) );
/* Construct lookup table entry */
memset ( &entry, 0, sizeof ( entry ) );
memcpy ( &entry.resource, &patch->header.boot,
sizeof ( entry.resource ) );
/* Copy lookup table entry */
memcpy ( data, ( ( ( void * ) &entry ) + offset ), len );
return 0;
}
/**
* Patch injected file lookup table entry
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_lookup_file ( struct wim_patch *patch __unused,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct wim_patch_region *rfile = region->opaque;
struct vdisk_file *vfile = rfile->opaque;
struct wim_lookup_entry entry;
/* Sanity checks */
assert ( offset < sizeof ( entry ) );
assert ( len <= ( sizeof ( entry ) - offset ) );
/* Construct lookup table entry */
memset ( &entry, 0, sizeof ( entry ) );
entry.resource.offset = rfile->offset;
entry.resource.len = vfile->len;
entry.resource.zlen__flags = entry.resource.len;
entry.refcnt = 1;
wim_hash ( vfile, &entry.hash );
/* Copy lookup table entry */
memcpy ( data, ( ( ( void * ) &entry ) + offset ), len );
DBG2 ( "...patched WIM %s %s\n", region->name, vfile->name );
return 0;
}
/**
* Patch uncompressed copy of original boot metadata
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_boot_copy ( struct wim_patch *patch,
struct wim_patch_region *region __unused,
void *data, size_t offset, size_t len ) {
int rc;
/* Read original boot metadata */
if ( ( rc = wim_read ( patch->file, &patch->header, &patch->boot,
data, offset, len ) ) != 0 )
return rc;
return 0;
}
/**
* Patch subdirectory offset within parent directory entry
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_dir_subdir ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct wim_patch_dir *dir = region->opaque;
uint64_t subdir = dir->subdir;
/* Sanity checks */
assert ( offset < sizeof ( subdir ) );
assert ( len <= ( sizeof ( subdir ) - offset ) );
/* Copy subdirectory offset */
memcpy ( data, ( ( ( void * ) &subdir ) + offset ), len );
DBG2 ( "...patched WIM %s %s %#llx\n", region->name, dir->name,
( patch->header.boot.offset + subdir ) );
return 0;
}
/**
* Patch copy of original directory entries
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_dir_copy ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct wim_patch_dir *dir = region->opaque;
int rc;
/* Read portion of original boot metadata */
if ( ( rc = wim_read ( patch->file, &patch->header, &patch->boot,
data, ( dir->offset + offset ), len ) ) != 0 )
return rc;
return 0;
}
/**
* Patch injected directory entries
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_dir_file ( struct wim_patch *patch __unused,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
struct wim_patch_region *rfile = region->opaque;
struct vdisk_file *vfile = rfile->opaque;
struct wim_patch_dir_entry entry;
size_t name_len = strlen ( vfile->name );
unsigned int i;
/* Sanity checks */
assert ( offset < sizeof ( entry ) );
assert ( len <= ( sizeof ( entry ) - offset ) );
/* Construct directory entry */
memset ( &entry, 0, sizeof ( entry ) );
entry.dir.len = wim_align ( sizeof ( entry ) );
entry.dir.attributes = WIM_ATTR_NORMAL;
entry.dir.security = WIM_NO_SECURITY;
entry.dir.created = WIM_MAGIC_TIME;
entry.dir.accessed = WIM_MAGIC_TIME;
entry.dir.written = WIM_MAGIC_TIME;
wim_hash ( vfile, &entry.dir.hash );
entry.dir.name_len = ( name_len * sizeof ( entry.name[0] ) );
for ( i = 0 ; i < name_len ; i++ )
entry.name[i] = vfile->name[i];
/* Copy directory entry */
memcpy ( data, ( ( ( void * ) &entry ) + offset ), len );
DBG2 ( "...patched WIM %s %s\n", region->name, vfile->name );
return 0;
}
/**
* Patch WIM region
*
* @v patch WIM patch
* @v region Patch region
* @v data Data buffer
* @v offset Relative offset
* @v len Length
* @ret rc Return status code
*/
static int wim_patch_region ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset, size_t len ) {
size_t skip;
int rc;
/* Skip unused regions */
if ( ! region->patch )
return 0;
/* Skip any data before this region */
skip = ( ( region->offset > offset ) ?
( region->offset - offset ) : 0 );
if ( skip >= len )
return 0;
data += skip;
offset += skip;
len -= skip;
/* Convert to relative offset within this region */
offset -= region->offset;
/* Skip any data after this region */
if ( offset >= region->len )
return 0;
if ( len > ( region->len - offset ) )
len = ( region->len - offset );
/* Patch this region */
if ( ( rc = region->patch ( patch, region, data, offset, len ) ) != 0 )
return rc;
DBG2 ( "...patched WIM %s at [%#zx,%#zx)\n", region->name,
( region->offset + offset ), ( region->offset + offset + len ) );
return 0;
}
/**
* Construct patched WIM region
*
* @v region Patched region to fill in
* @v name Name
* @v opaque Opaque data
* @v offset Offset
* @v len Length
* @v patch Patch method
* @ret offset Next offset
*/
static inline __attribute__ (( always_inline )) size_t
wim_construct_region ( struct wim_patch_region *region, const char *name,
void *opaque, size_t offset, size_t len,
int ( * patch ) ( struct wim_patch *patch,
struct wim_patch_region *region,
void *data, size_t offset,
size_t len ) ) {
DBG ( "...patching WIM %s at [%#zx,%#zx)\n",
name, offset, ( offset + len ) );
region->name = name;
region->opaque = opaque;
region->offset = offset;
region->len = len;
region->patch = patch;
return ( offset + len );
}
/**
* Construct patch WIM directory regions
*
* @v patch WIM patch
* @v dir Patched directory
* @v offset Offset
* @v regions Patched directory regions to fill in
* @ret offset Next offset
*/
static size_t wim_construct_dir ( struct wim_patch *patch,
struct wim_patch_dir *dir, size_t offset,
struct wim_patch_dir_regions *regions ) {
struct wim_patch_dir_entry *entry;
struct wim_patch_region *rfile;
size_t boot_offset = patch->header.boot.offset;
unsigned int i;
DBG ( "...patching WIM directory at %#zx from [%#zx,%#zx)\n",
( boot_offset + dir->parent ), ( boot_offset + dir->offset ),
( boot_offset + dir->offset + dir->len ) );
/* Align directory entries */
offset = wim_align ( offset );
dir->subdir = ( offset - patch->header.boot.offset );
/* Construct injected file directory entries */
for ( i = 0 ; i < VDISK_MAX_FILES ; i++ ) {
rfile = &patch->regions.file[i];
if ( ! rfile->patch )
continue;
offset = wim_construct_region ( &regions->file[i], "dir.file",
rfile, offset,
sizeof ( *entry ),
wim_patch_dir_file );
offset = wim_align ( offset );
}
/* Construct copy of original directory entries */
offset = wim_construct_region ( &regions->copy, dir->name, dir, offset,
dir->len, wim_patch_dir_copy );
/* Allow space for directory terminator */
offset += sizeof ( entry->dir.len );
/* Construct subdirectory offset within parent directory entry */
wim_construct_region ( &regions->subdir, "dir.subdir", dir,
( boot_offset + dir->parent +
offsetof ( typeof ( entry->dir ), subdir ) ),
sizeof ( entry->dir.subdir ),
wim_patch_dir_subdir );
return offset;
}
/**
* Construct WIM patch
*
* @v file Virtual file
* @v boot_index New boot index (or zero)
* @v inject Inject files into WIM
* @v patch Patch to fill in
* @ret rc Return status code
*/
static int wim_construct_patch ( struct vdisk_file *file,
unsigned int boot_index, int inject,
struct wim_patch *patch ) {
union wim_patch_regions *regions = &patch->regions;
struct wim_patch_region *rfile;
struct wim_resource_header *lookup;
struct wim_resource_header *boot;
struct wim_directory_entry direntry;
struct wim_lookup_entry *entry;
struct vdisk_file *vfile;
size_t offset;
unsigned int injected = 0;
unsigned int i;
int rc;
/* Initialise patch */
memset ( patch, 0, sizeof ( *patch ) );
patch->file = file;
DBG ( "...patching WIM %s\n", file->name );
/* Reset file length */
file->xlen = file->len;
offset = file->len;
/* Read WIM header */
if ( ( rc = wim_header ( file, &patch->header ) ) != 0 )
return rc;
lookup = &patch->header.lookup;
boot = &patch->header.boot;
/* Patch header within original image body */
wim_construct_region ( &regions->header, "header", NULL, 0,
sizeof ( patch->header ), wim_patch_header );
/* Record original lookup table */
memcpy ( &patch->lookup, lookup, sizeof ( patch->lookup ) );
/* Record original metadata for selected boot image (which may
* not be the originally selected boot image).
*/
if ( ( rc = wim_metadata ( file, &patch->header, boot_index,
&patch->boot ) ) != 0 )
return rc;
/* Record original boot index */
patch->boot_index = patch->header.boot_index;
/* Update boot index in patched header, if applicable */
if ( boot_index )
patch->header.boot_index = boot_index;
/* Do nothing more if injection is disabled */
if ( ! inject )
return 0;
/* Construct injected files */
for ( i = 0 ; i < VDISK_MAX_FILES ; i++ ) {
vfile = &vdisk_files[i];
if ( ! wim_inject_file ( vfile ) )
continue;
offset = wim_construct_region ( &regions->file[i], vfile->name,
vfile, offset, vfile->len,
wim_patch_file );
injected++;
}
/* Do nothing more if no files are injected */
if ( injected == 0 )
return 0;
/* Calculate boot index for injected image */
if ( ( rc = wim_count ( file, &patch->header, &boot_index ) ) != 0 )
return rc;
patch->header.images = ( boot_index + 1 );
patch->header.boot_index = patch->header.images;
/* Construct injected lookup table */
lookup->offset = offset = wim_align ( offset );
offset = wim_construct_region ( &regions->lookup.copy, "lookup.copy",
NULL, offset, patch->lookup.len,
wim_patch_lookup_copy );
offset = wim_construct_region ( &regions->lookup.boot, "lookup.boot",
NULL, offset, sizeof ( *entry ),
wim_patch_lookup_boot );
for ( i = 0 ; i < VDISK_MAX_FILES ; i++ ) {
rfile = &regions->file[i];
if ( ! rfile->patch )
continue;
offset = wim_construct_region ( &regions->lookup.file[i],
"lookup.file", rfile,
offset, sizeof ( *entry ),
wim_patch_lookup_file );
}
lookup->offset = regions->lookup.copy.offset;
lookup->len = ( offset - lookup->offset );
lookup->zlen__flags = lookup->len;
/* Locate directory containing injected files */
patch->dir.name = WIM_INJECT_DIR;
if ( ( rc = wim_path ( file, &patch->header, &patch->boot,
L(WIM_INJECT_DIR), &patch->dir.parent,
&direntry ) ) != 0 )
return rc;
patch->dir.offset = direntry.subdir;
if ( ( rc = wim_dir_len ( file, &patch->header, &patch->boot,
patch->dir.offset, &patch->dir.len ) ) != 0 )
return rc;
/* Construct injected boot image metadata */
boot->offset = offset = wim_align ( offset );
offset = wim_construct_region ( &regions->boot.copy, "boot.copy",
NULL, offset, patch->boot.len,
wim_patch_boot_copy );
offset = wim_construct_dir ( patch, &patch->dir, offset,
&regions->boot.dir );
boot->len = ( offset - boot->offset );
boot->zlen__flags = ( boot->len | WIM_RESHDR_METADATA );
/* Record patched length */
file->xlen = offset;
DBG ( "...patching WIM length %#zx->%#zx\n", file->len, file->xlen );
return 0;
}
/**
* Patch WIM file
*
* @v file Virtual file
* @v data Data buffer
* @v offset Offset
* @v len Length
*/
void patch_wim ( struct vdisk_file *file, void *data, size_t offset,
size_t len ) {
static struct wim_patch cached_patch;
struct wim_patch *patch = &cached_patch;
struct wim_patch_region *region;
unsigned int boot_index;
unsigned int i;
int inject;
int rc;
/* Do nothing unless patching is required */
boot_index = cmdline_index;
inject = ( ! cmdline_rawwim );
if ( ( boot_index == 0 ) && ( ! inject ) )
return;
/* Update cached patch if required */
if ( file != patch->file ) {
if ( ( rc = wim_construct_patch ( file, boot_index, inject,
patch ) ) != 0 ) {
die ( "Could not patch WIM %s\n", file->name );
}
}
patch = &cached_patch;
/* Patch regions */
for ( i = 0 ; i < ( sizeof ( patch->regions ) /
sizeof ( patch->regions.region[0] ) ) ; i++ ) {
region = &patch->regions.region[i];
if ( ( rc = wim_patch_region ( patch, region, data, offset,
len ) ) != 0 ) {
die ( "Could not patch WIM %s %s at [%#zx,%#zx)\n",
file->name, region->name, offset,
( offset + len ) );
}
}
}
#ifndef _WIMPATCH_H
#define _WIMPATCH_H
/*
* Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* WIM dynamic patching
*
*/
#include <stdint.h>
struct vdisk_file;
extern void patch_wim ( struct vdisk_file *file, void *data, size_t offset,
size_t len );
#endif /* _WIMPATCH_H */
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "wimboot.h"
#include "huffman.h"
#include "xca.h"
/**
* Decompress XCA-compressed data
*
* @v data Compressed data
* @v len Length of compressed data
* @v buf Decompression buffer, or NULL
* @ret out_len Length of decompressed data, or negative error
*/
ssize_t xca_decompress ( const void *data, size_t len, void *buf ) {
const void *src = data;
const void *end = ( src + len );
uint8_t *out = buf;
size_t out_len = 0;
size_t out_len_threshold = 0;
const struct xca_huf_len *lengths;
struct xca xca;
uint32_t accum = 0;
int extra_bits = 0;
unsigned int huf;
struct huffman_symbols *sym;
unsigned int raw;
unsigned int match_len;
unsigned int match_offset_bits;
unsigned int match_offset;
const uint8_t *copy;
int rc;
/* Process data stream */
while ( src < end ) {
/* (Re)initialise decompressor if applicable */
if ( out_len >= out_len_threshold ) {
/* Construct symbol lengths */
lengths = src;
src += sizeof ( *lengths );
if ( src > end ) {
DBG ( "XCA too short to hold Huffman lengths "
"table at input offset %#zx\n",
( src - data ) );
return -1;
}
for ( raw = 0 ; raw < XCA_CODES ; raw++ )
xca.lengths[raw] = xca_huf_len ( lengths, raw );
/* Construct Huffman alphabet */
if ( ( rc = huffman_alphabet ( &xca.alphabet,
xca.lengths,
XCA_CODES ) ) != 0 )
return rc;
/* Initialise state */
accum = XCA_GET16 ( src );
accum <<= 16;
accum |= XCA_GET16 ( src );
extra_bits = 16;
/* Determine next threshold */
out_len_threshold = ( out_len + XCA_BLOCK_SIZE );
}
/* Determine symbol */
huf = ( accum >> ( 32 - HUFFMAN_BITS ) );
sym = huffman_sym ( &xca.alphabet, huf );
raw = huffman_raw ( sym, huf );
accum <<= huffman_len ( sym );
extra_bits -= huffman_len ( sym );
if ( extra_bits < 0 ) {
accum |= ( XCA_GET16 ( src ) << ( -extra_bits ) );
extra_bits += 16;
}
/* Process symbol */
if ( raw < XCA_END_MARKER ) {
/* Literal symbol - add to output stream */
if ( buf )
*(out++) = raw;
out_len++;
} else if ( ( raw == XCA_END_MARKER ) &&
( src >= ( end - 1 ) ) ) {
/* End marker symbol */
return out_len;
} else {
/* LZ77 match symbol */
raw -= XCA_END_MARKER;
match_offset_bits = ( raw >> 4 );
match_len = ( raw & 0x0f );
if ( match_len == 0x0f ) {
match_len = XCA_GET8 ( src );
if ( match_len == 0xff ) {
match_len = XCA_GET16 ( src );
} else {
match_len += 0x0f;
}
}
match_len += 3;
if ( match_offset_bits ) {
match_offset =
( ( accum >> ( 32 - match_offset_bits ))
+ ( 1 << match_offset_bits ) );
} else {
match_offset = 1;
}
accum <<= match_offset_bits;
extra_bits -= match_offset_bits;
if ( extra_bits < 0 ) {
accum |= ( XCA_GET16 ( src ) << (-extra_bits) );
extra_bits += 16;
}
/* Copy data */
out_len += match_len;
if ( buf ) {
copy = ( out - match_offset );
while ( match_len-- )
*(out++) = *(copy++);
}
}
}
DBG ( "XCA input overrun at output length %#zx\n", out_len );
return -1;
}
#ifndef _XCA_H
#define _XCA_H
/*
* Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* @file
*
* Xpress Compression Algorithm (MS-XCA) decompression
*
*/
#include <stdint.h>
#include "huffman.h"
/** Number of XCA codes */
#define XCA_CODES 512
/** XCA decompressor */
struct xca {
/** Huffman alphabet */
struct huffman_alphabet alphabet;
/** Raw symbols
*
* Must immediately follow the Huffman alphabet.
*/
huffman_raw_symbol_t raw[XCA_CODES];
/** Code lengths */
uint8_t lengths[XCA_CODES];
};
/** XCA symbol Huffman lengths table */
struct xca_huf_len {
/** Lengths of each symbol */
uint8_t nibbles[ XCA_CODES / 2 ];
} __attribute__ (( packed ));
/**
* Extract Huffman-coded length of a raw symbol
*
* @v lengths Huffman lengths table
* @v symbol Raw symbol
* @ret len Huffman-coded length
*/
static inline unsigned int xca_huf_len ( const struct xca_huf_len *lengths,
unsigned int symbol ) {
return ( ( ( lengths->nibbles[ symbol / 2 ] ) >>
( 4 * ( symbol % 2 ) ) ) & 0x0f );
}
/** Get word from source data stream */
#define XCA_GET16( src ) ( { \
const uint16_t *src16 = src; \
src += sizeof ( *src16 ); \
*src16; } )
/** Get byte from source data stream */
#define XCA_GET8( src ) ( { \
const uint8_t *src8 = src; \
src += sizeof ( *src8 ); \
*src8; } )
/** XCA source data stream end marker */
#define XCA_END_MARKER 256
/** XCA block size */
#define XCA_BLOCK_SIZE ( 64 * 1024 )
extern ssize_t xca_decompress ( const void *data, size_t len, void *buf );
#endif /* _XCA_H */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment