"runner/llamarunner/runner.go" did not exist on "17b386a891af182650f93d528ff78f2fded9efc6"
Commit 93996cf7 authored by longpanda's avatar longpanda
Browse files

1. Optimization for WIMBOOT mode.

2. Add WIMBOOT for UEFI mode.
parent ca62128f
#ifndef _LZNT1_H
#define _LZNT1_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
*
* LZNT1 decompression
*
*/
#include <stdint.h>
/** Extract LZNT1 block length */
#define LZNT1_BLOCK_LEN( header ) ( ( (header) & 0x0fff ) + 1 )
/** Determine if LZNT1 block is compressed */
#define LZNT1_BLOCK_COMPRESSED( header ) ( (header) & 0x8000 )
/** Extract LZNT1 compressed value length */
#define LZNT1_VALUE_LEN( tuple, split ) \
( ( (tuple) & ( ( 1 << (split) ) - 1 ) ) + 3 )
/** Extract LZNT1 compressed value offset */
#define LZNT1_VALUE_OFFSET( tuple, split ) ( ( (tuple) >> split ) + 1 )
extern ssize_t lznt1_decompress ( const void *data, size_t len, void *buf );
#endif /* _LZNT1_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
*
* LZX decompression
*
* This algorithm is derived jointly from the document "[MS-PATCH]:
* LZX DELTA Compression and Decompression", available from
*
* http://msdn.microsoft.com/en-us/library/cc483133.aspx
*
* and from the file lzx-decompress.c in the wimlib source code.
*
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include "wimboot.h"
#include "huffman.h"
#include "lzx.h"
/** Base positions, indexed by position slot */
static unsigned int lzx_position_base[LZX_POSITION_SLOTS];
/**
* Attempt to accumulate bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to accumulate
* @v norm_value Accumulated value (normalised to 16 bits)
*
* Note that there may not be sufficient accumulated bits in the
* bitstream; callers must check that sufficient bits are available
* before using the value.
*/
static int lzx_accumulate ( struct lzx *lzx, unsigned int bits ) {
const uint16_t *src16;
/* Accumulate more bits if required */
if ( ( lzx->bits < bits ) &&
( lzx->input.offset < lzx->input.len ) ) {
src16 = ( ( void * ) lzx->input.data + lzx->input.offset );
lzx->input.offset += sizeof ( *src16 );
lzx->accumulator |= ( *src16 << ( 16 - lzx->bits ) );
lzx->bits += 16;
}
return ( lzx->accumulator >> 16 );
}
/**
* Consume accumulated bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to consume
* @ret rc Return status code
*/
static int lzx_consume ( struct lzx *lzx, unsigned int bits ) {
/* Fail if insufficient bits are available */
if ( lzx->bits < bits ) {
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
lzx->input.offset, lzx->input.len, lzx->output.offset );
return -1;
}
/* Consume bits */
lzx->accumulator <<= bits;
lzx->bits -= bits;
return 0;
}
/**
* Get bits from LZX bitstream
*
* @v lzx Decompressor
* @v bits Number of bits to fetch
* @ret value Value, or negative error
*/
static int lzx_getbits ( struct lzx *lzx, unsigned int bits ) {
int norm_value;
int rc;
/* Accumulate more bits if required */
norm_value = lzx_accumulate ( lzx, bits );
/* Consume bits */
if ( ( rc = lzx_consume ( lzx, bits ) ) != 0 )
return rc;
return ( norm_value >> ( 16 - bits ) );
}
/**
* Align LZX bitstream for byte access
*
* @v lzx Decompressor
* @v bits Minimum number of padding bits
* @ret rc Return status code
*/
static int lzx_align ( struct lzx *lzx, unsigned int bits ) {
int pad;
/* Get padding bits */
pad = lzx_getbits ( lzx, bits );
if ( pad < 0 )
return pad;
/* Consume all accumulated bits */
lzx_consume ( lzx, lzx->bits );
return 0;
}
/**
* Get bytes from LZX bitstream
*
* @v lzx Decompressor
* @v data Data buffer, or NULL
* @v len Length of data buffer
* @ret rc Return status code
*/
static int lzx_getbytes ( struct lzx *lzx, void *data, size_t len ) {
/* Sanity check */
if ( ( lzx->input.offset + len ) > lzx->input.len ) {
DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
lzx->input.offset, lzx->input.len, lzx->output.offset );
return -1;
}
/* Copy data */
if ( data )
memcpy ( data, ( lzx->input.data + lzx->input.offset ), len );
lzx->input.offset += len;
return 0;
}
/**
* Decode LZX Huffman-coded symbol
*
* @v lzx Decompressor
* @v alphabet Huffman alphabet
* @ret raw Raw symbol, or negative error
*/
static int lzx_decode ( struct lzx *lzx, struct huffman_alphabet *alphabet ) {
struct huffman_symbols *sym;
int huf;
int rc;
/* Accumulate sufficient bits */
huf = lzx_accumulate ( lzx, HUFFMAN_BITS );
if ( huf < 0 )
return huf;
/* Decode symbol */
sym = huffman_sym ( alphabet, huf );
/* Consume bits */
if ( ( rc = lzx_consume ( lzx, huffman_len ( sym ) ) ) != 0 )
return rc;
return huffman_raw ( sym, huf );
}
/**
* Generate Huffman alphabet from raw length table
*
* @v lzx Decompressor
* @v count Number of symbols
* @v bits Length of each length (in bits)
* @v lengths Lengths table to fill in
* @v alphabet Huffman alphabet to fill in
* @ret rc Return status code
*/
static int lzx_raw_alphabet ( struct lzx *lzx, unsigned int count,
unsigned int bits, uint8_t *lengths,
struct huffman_alphabet *alphabet ) {
unsigned int i;
int len;
int rc;
/* Read lengths */
for ( i = 0 ; i < count ; i++ ) {
len = lzx_getbits ( lzx, bits );
if ( len < 0 )
return len;
lengths[i] = len;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( alphabet, lengths, count ) ) != 0 )
return rc;
return 0;
}
/**
* Generate pretree
*
* @v lzx Decompressor
* @v count Number of symbols
* @v lengths Lengths table to fill in
* @ret rc Return status code
*/
static int lzx_pretree ( struct lzx *lzx, unsigned int count,
uint8_t *lengths ) {
unsigned int i;
unsigned int length;
int dup = 0;
int code;
int rc;
/* Generate pretree alphabet */
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_PRETREE_CODES,
LZX_PRETREE_BITS, lzx->pretree_lengths,
&lzx->pretree ) ) != 0 )
return rc;
/* Read lengths */
for ( i = 0 ; i < count ; i++ ) {
if ( dup ) {
/* Duplicate previous length */
lengths[i] = lengths[ i - 1 ];
dup--;
} else {
/* Get next code */
code = lzx_decode ( lzx, &lzx->pretree );
if ( code < 0 )
return code;
/* Interpret code */
if ( code <= 16 ) {
length = ( ( lengths[i] - code + 17 ) % 17 );
} else if ( code == 17 ) {
length = 0;
dup = lzx_getbits ( lzx, 4 );
if ( dup < 0 )
return dup;
dup += 3;
} else if ( code == 18 ) {
length = 0;
dup = lzx_getbits ( lzx, 5 );
if ( dup < 0 )
return dup;
dup += 19;
} else if ( code == 19 ) {
length = 0;
dup = lzx_getbits ( lzx, 1 );
if ( dup < 0 )
return dup;
dup += 3;
code = lzx_decode ( lzx, &lzx->pretree );
if ( code < 0 )
return code;
length = ( ( lengths[i] - code + 17 ) % 17 );
} else {
DBG ( "Unrecognised pretree code %d\n", code );
return -1;
}
lengths[i] = length;
}
}
/* Sanity check */
if ( dup ) {
DBG ( "Pretree duplicate overrun\n" );
return -1;
}
return 0;
}
/**
* Generate aligned offset Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_alignoffset_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate aligned offset alphabet */
if ( ( rc = lzx_raw_alphabet ( lzx, LZX_ALIGNOFFSET_CODES,
LZX_ALIGNOFFSET_BITS,
lzx->alignoffset_lengths,
&lzx->alignoffset ) ) != 0 )
return rc;
return 0;
}
/**
* Generate main Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_main_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate literal symbols pretree */
if ( ( rc = lzx_pretree ( lzx, LZX_MAIN_LIT_CODES,
lzx->main_lengths.literals ) ) != 0 ) {
DBG ( "Could not construct main literal pretree\n" );
return rc;
}
/* Generate remaining symbols pretree */
if ( ( rc = lzx_pretree ( lzx, ( LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ),
lzx->main_lengths.remainder ) ) != 0 ) {
DBG ( "Could not construct main remainder pretree\n" );
return rc;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( &lzx->main, lzx->main_lengths.literals,
LZX_MAIN_CODES ) ) != 0 ) {
DBG ( "Could not generate main alphabet\n" );
return rc;
}
return 0;
}
/**
* Generate length Huffman alphabet
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_length_alphabet ( struct lzx *lzx ) {
int rc;
/* Generate pretree */
if ( ( rc = lzx_pretree ( lzx, LZX_LENGTH_CODES,
lzx->length_lengths ) ) != 0 ) {
DBG ( "Could not generate length pretree\n" );
return rc;
}
/* Generate Huffman alphabet */
if ( ( rc = huffman_alphabet ( &lzx->length, lzx->length_lengths,
LZX_LENGTH_CODES ) ) != 0 ) {
DBG ( "Could not generate length alphabet\n" );
return rc;
}
return 0;
}
/**
* Process LZX block header
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_block_header ( struct lzx *lzx ) {
size_t block_len;
int block_type;
int default_len;
int len_high;
int len_low;
int rc;
/* Get block type */
block_type = lzx_getbits ( lzx, LZX_BLOCK_TYPE_BITS );
if ( block_type < 0 )
return block_type;
lzx->block_type = block_type;
/* Check block length */
default_len = lzx_getbits ( lzx, 1 );
if ( default_len < 0 )
return default_len;
if ( default_len ) {
block_len = LZX_DEFAULT_BLOCK_LEN;
} else {
len_high = lzx_getbits ( lzx, 8 );
if ( len_high < 0 )
return len_high;
len_low = lzx_getbits ( lzx, 8 );
if ( len_low < 0 )
return len_low;
block_len = ( ( len_high << 8 ) | len_low );
}
lzx->output.threshold = ( lzx->output.offset + block_len );
/* Handle block type */
switch ( block_type ) {
case LZX_BLOCK_ALIGNOFFSET :
/* Generated aligned offset alphabet */
if ( ( rc = lzx_alignoffset_alphabet ( lzx ) ) != 0 )
return rc;
/* Fall through */
case LZX_BLOCK_VERBATIM :
/* Generate main alphabet */
if ( ( rc = lzx_main_alphabet ( lzx ) ) != 0 )
return rc;
/* Generate lengths alphabet */
if ( ( rc = lzx_length_alphabet ( lzx ) ) != 0 )
return rc;
break;
case LZX_BLOCK_UNCOMPRESSED :
/* Align input stream */
if ( ( rc = lzx_align ( lzx, 1 ) ) != 0 )
return rc;
/* Read new repeated offsets */
if ( ( rc = lzx_getbytes ( lzx, &lzx->repeated_offset,
sizeof ( lzx->repeated_offset )))!=0)
return rc;
break;
default:
DBG ( "Unrecognised block type %d\n", block_type );
return -1;
}
return 0;
}
/**
* Process uncompressed data
*
* @v lzx Decompressor
* @ret rc Return status code
*/
static int lzx_uncompressed ( struct lzx *lzx ) {
void *data;
size_t len;
int rc;
/* Copy bytes */
data = ( lzx->output.data ?
( lzx->output.data + lzx->output.offset ) : NULL );
len = ( lzx->output.threshold - lzx->output.offset );
if ( ( rc = lzx_getbytes ( lzx, data, len ) ) != 0 )
return rc;
/* Align input stream */
if ( len % 2 )
lzx->input.offset++;
return 0;
}
/**
* Process an LZX token
*
* @v lzx Decompressor
* @ret rc Return status code
*
* Variable names are chosen to match the LZX specification
* pseudo-code.
*/
static int lzx_token ( struct lzx *lzx ) {
unsigned int length_header;
unsigned int position_slot;
unsigned int offset_bits;
unsigned int i;
size_t match_offset;
size_t match_length;
int verbatim_bits;
int aligned_bits;
int main;
int length;
uint8_t *copy;
/* Get main symelse*/
main = lzx_decode ( lzx, &lzx->main );
if ( main < 0 )
return main;
/* Check for literals */
if ( main < LZX_MAIN_LIT_CODES ) {
if ( lzx->output.data )
lzx->output.data[lzx->output.offset] = main;
lzx->output.offset++;
return 0;
}
main -= LZX_MAIN_LIT_CODES;
/* Calculate the match length */
length_header = ( main & 7 );
if ( length_header == 7 ) {
length = lzx_decode ( lzx, &lzx->length );
if ( length < 0 )
return length;
} else {
length = 0;
}
match_length = ( length_header + 2 + length );
/* Calculate the position slot */
position_slot = ( main >> 3 );
if ( position_slot < LZX_REPEATED_OFFSETS ) {
/* Repeated offset */
match_offset = lzx->repeated_offset[position_slot];
lzx->repeated_offset[position_slot] = lzx->repeated_offset[0];
lzx->repeated_offset[0] = match_offset;
} else {
/* Non-repeated offset */
offset_bits = lzx_footer_bits ( position_slot );
if ( ( lzx->block_type == LZX_BLOCK_ALIGNOFFSET ) &&
( offset_bits >= 3 ) ) {
verbatim_bits = lzx_getbits ( lzx, ( offset_bits - 3 ));
if ( verbatim_bits < 0 )
return verbatim_bits;
verbatim_bits <<= 3;
aligned_bits = lzx_decode ( lzx, &lzx->alignoffset );
if ( aligned_bits < 0 )
return aligned_bits;
} else {
verbatim_bits = lzx_getbits ( lzx, offset_bits );
if ( verbatim_bits < 0 )
return verbatim_bits;
aligned_bits = 0;
}
match_offset = ( lzx_position_base[position_slot] +
verbatim_bits + aligned_bits - 2 );
/* Update repeated offset list */
for ( i = ( LZX_REPEATED_OFFSETS - 1 ) ; i > 0 ; i-- )
lzx->repeated_offset[i] = lzx->repeated_offset[ i - 1 ];
lzx->repeated_offset[0] = match_offset;
}
/* Copy data */
if ( match_offset > lzx->output.offset ) {
DBG ( "LZX match underrun out %#zx offset %#zx len %#zx\n",
lzx->output.offset, match_offset, match_length );
return -1;
}
if ( lzx->output.data ) {
copy = &lzx->output.data[lzx->output.offset];
for ( i = 0 ; i < match_length ; i++ )
copy[i] = copy[ i - match_offset ];
}
lzx->output.offset += match_length;
return 0;
}
/**
* Translate E8 jump addresses
*
* @v lzx Decompressor
*/
static void lzx_translate_jumps ( struct lzx *lzx ) {
size_t offset;
int32_t *target;
/* Sanity check */
if ( lzx->output.offset < 10 )
return;
/* Scan for jump instructions */
for ( offset = 0 ; offset < ( lzx->output.offset - 10 ) ; offset++ ) {
/* Check for jump instruction */
if ( lzx->output.data[offset] != 0xe8 )
continue;
/* Translate jump target */
target = ( ( int32_t * ) &lzx->output.data[ offset + 1 ] );
if ( *target >= 0 ) {
if ( *target < LZX_WIM_MAGIC_FILESIZE )
*target -= offset;
} else {
if ( *target >= -( ( int32_t ) offset ) )
*target += LZX_WIM_MAGIC_FILESIZE;
}
offset += sizeof ( *target );
}
}
/**
* Decompress LZX-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 lzx_decompress ( const void *data, size_t len, void *buf ) {
struct lzx lzx;
unsigned int i;
int rc;
/* Sanity check */
if ( len % 2 ) {
DBG ( "LZX cannot handle odd-length input data\n" );
return -1;
}
/* Initialise global state, if required */
if ( ! lzx_position_base[ LZX_POSITION_SLOTS - 1 ] ) {
for ( i = 1 ; i < LZX_POSITION_SLOTS ; i++ ) {
lzx_position_base[i] =
( lzx_position_base[i-1] +
( 1 << lzx_footer_bits ( i - 1 ) ) );
}
}
/* Initialise decompressor */
memset ( &lzx, 0, sizeof ( lzx ) );
lzx.input.data = data;
lzx.input.len = len;
lzx.output.data = buf;
for ( i = 0 ; i < LZX_REPEATED_OFFSETS ; i++ )
lzx.repeated_offset[i] = 1;
/* Process blocks */
while ( lzx.input.offset < lzx.input.len ) {
/* Process block header */
if ( ( rc = lzx_block_header ( &lzx ) ) != 0 )
return rc;
/* Process block contents */
if ( lzx.block_type == LZX_BLOCK_UNCOMPRESSED ) {
/* Copy uncompressed data */
if ( ( rc = lzx_uncompressed ( &lzx ) ) != 0 )
return rc;
} else {
/* Process token stream */
while ( lzx.output.offset < lzx.output.threshold ) {
if ( ( rc = lzx_token ( &lzx ) ) != 0 )
return rc;
}
}
}
/* Postprocess to undo E8 jump compression */
if ( lzx.output.data )
lzx_translate_jumps ( &lzx );
return lzx.output.offset;
}
#ifndef _LZX_H
#define _LZX_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
*
* LZX decompression
*
*/
#include <stdint.h>
#include "huffman.h"
/** Number of aligned offset codes */
#define LZX_ALIGNOFFSET_CODES 8
/** Aligned offset code length (in bits) */
#define LZX_ALIGNOFFSET_BITS 3
/** Number of pretree codes */
#define LZX_PRETREE_CODES 20
/** Pretree code length (in bits) */
#define LZX_PRETREE_BITS 4
/** Number of literal main codes */
#define LZX_MAIN_LIT_CODES 256
/** Number of position slots */
#define LZX_POSITION_SLOTS 30
/** Number of main codes */
#define LZX_MAIN_CODES ( LZX_MAIN_LIT_CODES + ( 8 * LZX_POSITION_SLOTS ) )
/** Number of length codes */
#define LZX_LENGTH_CODES 249
/** Block type length (in bits) */
#define LZX_BLOCK_TYPE_BITS 3
/** Default block length */
#define LZX_DEFAULT_BLOCK_LEN 32768
/** Number of repeated offsets */
#define LZX_REPEATED_OFFSETS 3
/** Don't ask */
#define LZX_WIM_MAGIC_FILESIZE 12000000
/** Block types */
enum lzx_block_type {
/** Verbatim block */
LZX_BLOCK_VERBATIM = 1,
/** Aligned offset block */
LZX_BLOCK_ALIGNOFFSET = 2,
/** Uncompressed block */
LZX_BLOCK_UNCOMPRESSED = 3,
};
/** An LZX input stream */
struct lzx_input_stream {
/** Data */
const uint8_t *data;
/** Length */
size_t len;
/** Offset within stream */
size_t offset;
};
/** An LZX output stream */
struct lzx_output_stream {
/** Data, or NULL */
uint8_t *data;
/** Offset within stream */
size_t offset;
/** End of current block within stream */
size_t threshold;
};
/** LZX decompressor */
struct lzx {
/** Input stream */
struct lzx_input_stream input;
/** Output stream */
struct lzx_output_stream output;
/** Accumulator */
uint32_t accumulator;
/** Number of bits in accumulator */
unsigned int bits;
/** Block type */
enum lzx_block_type block_type;
/** Repeated offsets */
unsigned int repeated_offset[LZX_REPEATED_OFFSETS];
/** Aligned offset Huffman alphabet */
struct huffman_alphabet alignoffset;
/** Aligned offset raw symbols
*
* Must immediately follow the aligned offset Huffman
* alphabet.
*/
huffman_raw_symbol_t alignoffset_raw[LZX_ALIGNOFFSET_CODES];
/** Aligned offset code lengths */
uint8_t alignoffset_lengths[LZX_ALIGNOFFSET_CODES];
/** Pretree Huffman alphabet */
struct huffman_alphabet pretree;
/** Pretree raw symbols
*
* Must immediately follow the pretree Huffman alphabet.
*/
huffman_raw_symbol_t pretree_raw[LZX_PRETREE_CODES];
/** Preetree code lengths */
uint8_t pretree_lengths[LZX_PRETREE_CODES];
/** Main Huffman alphabet */
struct huffman_alphabet main;
/** Main raw symbols
*
* Must immediately follow the main Huffman alphabet.
*/
huffman_raw_symbol_t main_raw[LZX_MAIN_CODES];
/** Main code lengths */
struct {
/** Literals */
uint8_t literals[LZX_MAIN_LIT_CODES];
/** Remaining symbols */
uint8_t remainder[ LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ];
} __attribute__ (( packed )) main_lengths;
/** Length Huffman alphabet */
struct huffman_alphabet length;
/** Length raw symbols
*
* Must immediately follow the length Huffman alphabet.
*/
huffman_raw_symbol_t length_raw[LZX_LENGTH_CODES];
/** Length code lengths */
uint8_t length_lengths[LZX_LENGTH_CODES];
};
/**
* Calculate number of footer bits for a given position slot
*
* @v position_slot Position slot
* @ret footer_bits Number of footer bits
*/
static inline unsigned int lzx_footer_bits ( unsigned int position_slot ) {
if ( position_slot < 2 ) {
return 0;
} else if ( position_slot < 38 ) {
return ( ( position_slot / 2 ) - 1 );
} else {
return 17;
}
}
extern ssize_t lzx_decompress ( const void *data, size_t len, void *buf );
#endif /* _LZX_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
*
* Main entry point
*
*/
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "wimboot.h"
#include "peloader.h"
#include "int13.h"
#include "vdisk.h"
#include "cpio.h"
#include "lznt1.h"
#include "xca.h"
#include "cmdline.h"
#include "wimpatch.h"
#include "wimfile.h"
#include "pause.h"
#include "paging.h"
#include "memmap.h"
/** Start of our image (defined by linker) */
extern char _start[];
/** End of our image (defined by linker) */
extern char _end[];
/** Command line */
char *cmdline;
/** initrd */
void *initrd;
/** Length of initrd */
size_t initrd_len;
/** bootmgr.exe path within WIM */
static const wchar_t bootmgr_path[] = L"\\Windows\\Boot\\PXE\\bootmgr.exe";
/** Other paths within WIM */
static const wchar_t *wim_paths[] = {
L"\\Windows\\Boot\\DVD\\PCAT\\boot.sdi",
L"\\Windows\\Boot\\DVD\\PCAT\\BCD",
L"\\Windows\\Boot\\Fonts\\segmono_boot.ttf",
L"\\Windows\\Boot\\Fonts\\segoen_slboot.ttf",
L"\\Windows\\Boot\\Fonts\\segoe_slboot.ttf",
L"\\Windows\\Boot\\Fonts\\wgl4_boot.ttf",
L"\\sms\\boot\\boot.sdi",
NULL
};
/** bootmgr.exe file */
static struct vdisk_file *bootmgr;
/** WIM image file */
static struct vdisk_file *bootwim;
/** Minimal length of embedded bootmgr.exe */
#define BOOTMGR_MIN_LEN 16384
/** 1MB memory threshold */
#define ADDR_1MB 0x00100000
/** 2GB memory threshold */
#define ADDR_2GB 0x80000000
/** Memory regions */
enum {
WIMBOOT_REGION = 0,
PE_REGION,
INITRD_REGION,
NUM_REGIONS
};
/**
* Wrap interrupt callback
*
* @v params Parameters
*/
static void call_interrupt_wrapper ( struct bootapp_callback_params *params ) {
struct paging_state state;
uint16_t *attributes;
/* Handle/modify/pass-through interrupt as required */
if ( params->vector.interrupt == 0x13 ) {
/* Enable paging */
enable_paging ( &state );
/* Intercept INT 13 calls for the emulated drive */
emulate_int13 ( params );
/* Disable paging */
disable_paging ( &state );
} else if ( ( params->vector.interrupt == 0x10 ) &&
( params->ax == 0x4f01 ) &&
( ! cmdline_gui ) ) {
/* Mark all VESA video modes as unsupported */
attributes = REAL_PTR ( params->es, params->di );
call_interrupt ( params );
*attributes &= ~0x0001;
} else {
/* Pass through interrupt */
call_interrupt ( params );
}
}
/** Real-mode callback functions */
static struct bootapp_callback_functions callback_fns = {
.call_interrupt = call_interrupt_wrapper,
.call_real = call_real,
};
/** Real-mode callbacks */
static struct bootapp_callback callback = {
.fns = &callback_fns,
};
/** Boot application descriptor set */
static struct {
/** Boot application descriptor */
struct bootapp_descriptor bootapp;
/** Boot application memory descriptor */
struct bootapp_memory_descriptor memory;
/** Boot application memory descriptor regions */
struct bootapp_memory_region regions[NUM_REGIONS];
/** Boot application entry descriptor */
struct bootapp_entry_descriptor entry;
struct bootapp_entry_wtf1_descriptor wtf1;
struct bootapp_entry_wtf2_descriptor wtf2;
struct bootapp_entry_wtf3_descriptor wtf3;
struct bootapp_entry_wtf3_descriptor wtf3_copy;
/** Boot application callback descriptor */
struct bootapp_callback_descriptor callback;
/** Boot application pointless descriptor */
struct bootapp_pointless_descriptor pointless;
} __attribute__ (( packed )) bootapps = {
.bootapp = {
.signature = BOOTAPP_SIGNATURE,
.version = BOOTAPP_VERSION,
.len = sizeof ( bootapps ),
.arch = BOOTAPP_ARCH_I386,
.memory = offsetof ( typeof ( bootapps ), memory ),
.entry = offsetof ( typeof ( bootapps ), entry ),
.xxx = offsetof ( typeof ( bootapps ), wtf3_copy ),
.callback = offsetof ( typeof ( bootapps ), callback ),
.pointless = offsetof ( typeof ( bootapps ), pointless ),
},
.memory = {
.version = BOOTAPP_MEMORY_VERSION,
.len = sizeof ( bootapps.memory ),
.num_regions = NUM_REGIONS,
.region_len = sizeof ( bootapps.regions[0] ),
.reserved_len = sizeof ( bootapps.regions[0].reserved ),
},
.entry = {
.signature = BOOTAPP_ENTRY_SIGNATURE,
.flags = BOOTAPP_ENTRY_FLAGS,
},
.wtf1 = {
.flags = 0x11000001,
.len = sizeof ( bootapps.wtf1 ),
.extra_len = ( sizeof ( bootapps.wtf2 ) +
sizeof ( bootapps.wtf3 ) ),
},
.wtf3 = {
.flags = 0x00000006,
.len = sizeof ( bootapps.wtf3 ),
.boot_partition_offset = ( VDISK_VBR_LBA * VDISK_SECTOR_SIZE ),
.xxx = 0x01,
.mbr_signature = VDISK_MBR_SIGNATURE,
},
.wtf3_copy = {
.flags = 0x00000006,
.len = sizeof ( bootapps.wtf3 ),
.boot_partition_offset = ( VDISK_VBR_LBA * VDISK_SECTOR_SIZE ),
.xxx = 0x01,
.mbr_signature = VDISK_MBR_SIGNATURE,
},
.callback = {
.callback = &callback,
},
.pointless = {
.version = BOOTAPP_POINTLESS_VERSION,
},
};
/**
* Test if a paragraph is empty
*
* @v pgh Paragraph
* @ret is_empty Paragraph is empty (all zeroes)
*/
static int is_empty_pgh ( const void *pgh ) {
const uint32_t *dwords = pgh;
return ( ( dwords[0] | dwords[1] | dwords[2] | dwords[3] ) == 0 );
}
/**
* Read from file
*
* @v file Virtual file
* @v data Data buffer
* @v offset Offset
* @v len Length
*/
static void read_file ( struct vdisk_file *file, void *data, size_t offset,
size_t len ) {
memcpy ( data, ( file->opaque + offset ), len );
}
/**
* Add embedded bootmgr.exe extracted from bootmgr
*
* @v data File data
* @v len Length
* @ret file Virtual file, or NULL
*
* bootmgr.exe is awkward to obtain, since it is not available as a
* standalone file on the installation media, or on an installed
* system, or in a Windows PE image as created by WAIK or WADK. It
* can be extracted from a typical boot.wim image using ImageX, but
* this requires installation of the WAIK/WADK/wimlib.
*
* A compressed version of bootmgr.exe is contained within bootmgr,
* which is trivial to obtain.
*/
static struct vdisk_file * add_bootmgr ( const void *data, size_t len ) {
const uint8_t *compressed;
size_t offset;
size_t compressed_len;
ssize_t ( * decompress ) ( const void *data, size_t len, void *buf );
ssize_t decompressed_len;
size_t padded_len;
/* Look for an embedded compressed bootmgr.exe on an
* eight-byte boundary.
*/
for ( offset = BOOTMGR_MIN_LEN ; offset < ( len - BOOTMGR_MIN_LEN ) ;
offset += 0x08 ) {
/* Initialise checks */
decompress = NULL;
compressed = ( data + offset );
compressed_len = ( len - offset );
/* Check for an embedded LZNT1-compressed bootmgr.exe.
* Since there is no way for LZNT1 to compress the
* initial "MZ" bytes of bootmgr.exe, we look for this
* signature starting three bytes after a paragraph
* boundary, with a preceding tag byte indicating that
* these two bytes would indeed be uncompressed.
*/
if ( ( ( offset & 0x0f ) == 0x00 ) &&
( ( compressed[0x02] & 0x03 ) == 0x00 ) &&
( compressed[0x03] == 'M' ) &&
( compressed[0x04] == 'Z' ) ) {
DBG ( "...checking for LZNT1-compressed bootmgr.exe at "
"+%#zx\n", offset );
decompress = lznt1_decompress;
}
/* Check for an embedded XCA-compressed bootmgr.exe.
* The bytes 0x00, 'M', and 'Z' will always be
* present, and so the corresponding symbols must have
* a non-zero Huffman length. The embedded image
* tends to have a large block of zeroes immediately
* beforehand, which we check for. It's implausible
* that the compressed data could contain substantial
* runs of zeroes, so we check for that too, in order
* to eliminate some common false positive matches.
*/
if ( ( ( compressed[0x00] & 0x0f ) != 0x00 ) &&
( ( compressed[0x26] & 0xf0 ) != 0x00 ) &&
( ( compressed[0x2d] & 0x0f ) != 0x00 ) &&
( is_empty_pgh ( compressed - 0x10 ) ) &&
( ! is_empty_pgh ( ( compressed + 0x400 ) ) ) &&
( ! is_empty_pgh ( ( compressed + 0x800 ) ) ) &&
( ! is_empty_pgh ( ( compressed + 0xc00 ) ) ) ) {
DBG ( "...checking for XCA-compressed bootmgr.exe at "
"+%#zx\n", offset );
decompress = xca_decompress;
}
/* If we have not found a possible bootmgr.exe, skip
* to the next offset.
*/
if ( ! decompress )
continue;
/* Find length of decompressed image */
decompressed_len = decompress ( compressed, compressed_len,
NULL );
if ( decompressed_len < 0 ) {
/* May be a false positive signature match */
continue;
}
/* Prepend decompressed image to initrd */
DBG ( "...extracting embedded bootmgr.exe\n" );
padded_len = ( ( decompressed_len + PAGE_SIZE - 1 ) &
~( PAGE_SIZE - 1 ) );
initrd -= padded_len;
initrd_len += padded_len;
decompress ( compressed, compressed_len, initrd );
/* Add decompressed image */
return vdisk_add_file ( "bootmgr.exe", initrd,
decompressed_len, read_file );
}
DBG ( "...no embedded bootmgr.exe found\n" );
return NULL;
}
/**
* File handler
*
* @v name File name
* @v data File data
* @v len Length
* @ret rc Return status code
*/
static int add_file ( const char *name, void *data, size_t len ) {
struct vdisk_file *file;
/* Store file */
file = vdisk_add_file ( name, data, len, read_file );
/* Check for special-case files */
if ( strcasecmp ( name, "bootmgr.exe" ) == 0 ) {
DBG ( "...found bootmgr.exe\n" );
bootmgr = file;
} else if ( strcasecmp ( name, "bootmgr" ) == 0 ) {
DBG ( "...found bootmgr\n" );
if ( ( ! bootmgr ) &&
( bootmgr = add_bootmgr ( data, len ) ) ) {
DBG ( "...extracted bootmgr.exe\n" );
}
} else if ( strcasecmp ( ( name + strlen ( name ) - 4 ),
".wim" ) == 0 ) {
DBG ( "...found WIM file %s\n", name );
bootwim = file;
}
return 0;
}
/**
* Relocate data between 1MB and 2GB if possible
*
* @v data Start of data
* @v len Length of data
* @ret start Start address
*/
static void * relocate_memory_low ( void *data, size_t len ) {
struct e820_entry *e820 = NULL;
uint64_t end;
intptr_t start;
/* Read system memory map */
while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {
/* Find highest compatible placement within this region */
end = ( e820->start + e820->len );
start = ( ( end > ADDR_2GB ) ? ADDR_2GB : end );
if ( start < len )
continue;
start -= len;
start &= ~( PAGE_SIZE - 1 );
if ( start < e820->start )
continue;
if ( start < ADDR_1MB )
continue;
/* Relocate to this region */
memmove ( ( void * ) start, data, len );
return ( ( void * ) start );
}
/* Leave at original location */
return data;
}
/**
* Main entry point
*
*/
int main ( void ) {
size_t padded_len;
void *raw_pe;
struct loaded_pe pe;
struct paging_state state;
uint64_t initrd_phys;
/* Initialise stack cookie */
init_cookie();
/* Print welcome banner */
printf ( "\n\nBooting wim file...... (This may take a few minutes, please wait)\n\n");
//printf ( "\n\nwimboot " VERSION " -- Windows Imaging Format "
// "bootloader -- https://ipxe.org/wimboot\n\n" );
/* Process command line */
process_cmdline ( cmdline );
/* Initialise paging */
init_paging();
/* Enable paging */
enable_paging ( &state );
/* Relocate initrd below 2GB if possible, to avoid collisions */
DBG ( "Found initrd at [%p,%p)\n", initrd, ( initrd + initrd_len ) );
initrd = relocate_memory_low ( initrd, initrd_len );
DBG ( "Placing initrd at [%p,%p)\n", initrd, ( initrd + initrd_len ) );
/* Extract files from initrd */
if ( cpio_extract ( initrd, initrd_len, add_file ) != 0 )
die ( "FATAL: could not extract initrd files\n" );
/* Process WIM image */
if ( bootwim ) {
vdisk_patch_file ( bootwim, patch_wim );
if ( ( ! bootmgr ) &&
( bootmgr = wim_add_file ( bootwim, cmdline_index,
bootmgr_path,
L"bootmgr.exe" ) ) ) {
DBG ( "...extracted bootmgr.exe\n" );
}
wim_add_files ( bootwim, cmdline_index, wim_paths );
}
/* Add INT 13 drive */
callback.drive = initialise_int13();
/* Read bootmgr.exe into memory */
if ( ! bootmgr )
die ( "FATAL: no bootmgr.exe\n" );
if ( bootmgr->read == read_file ) {
raw_pe = bootmgr->opaque;
} else {
padded_len = ( ( bootmgr->len + PAGE_SIZE - 1 ) &
~( PAGE_SIZE -1 ) );
raw_pe = ( initrd - padded_len );
bootmgr->read ( bootmgr, raw_pe, 0, bootmgr->len );
}
/* Load bootmgr.exe into memory */
if ( load_pe ( raw_pe, bootmgr->len, &pe ) != 0 )
die ( "FATAL: Could not load bootmgr.exe\n" );
/* Relocate initrd above 4GB if possible, to free up 32-bit memory */
initrd_phys = relocate_memory_high ( initrd, initrd_len );
DBG ( "Placing initrd at physical [%#llx,%#llx)\n",
initrd_phys, ( initrd_phys + initrd_len ) );
/* Complete boot application descriptor set */
bootapps.bootapp.pe_base = pe.base;
bootapps.bootapp.pe_len = pe.len;
bootapps.regions[WIMBOOT_REGION].start_page = page_start ( _start );
bootapps.regions[WIMBOOT_REGION].num_pages = page_len ( _start, _end );
bootapps.regions[PE_REGION].start_page = page_start ( pe.base );
bootapps.regions[PE_REGION].num_pages =
page_len ( pe.base, ( pe.base + pe.len ) );
bootapps.regions[INITRD_REGION].start_page =
( initrd_phys / PAGE_SIZE );
bootapps.regions[INITRD_REGION].num_pages =
page_len ( initrd, initrd + initrd_len );
/* Omit initrd region descriptor if located above 4GB */
if ( initrd_phys >= ADDR_4GB )
bootapps.memory.num_regions--;
/* Disable paging */
disable_paging ( &state );
/* Jump to PE image */
DBG ( "Entering bootmgr.exe with parameters at %p\n", &bootapps );
if ( cmdline_pause )
pause();
pe.entry ( &bootapps.bootapp );
die ( "FATAL: bootmgr.exe returned\n" );
}
/*
* Copyright (C) 2021 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
*
* Memory map
*
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "wimboot.h"
#include "memmap.h"
/** Buffer for INT 15,e820 calls */
static struct e820_entry e820_buf __attribute__ (( section ( ".bss16" ) ));
/** Continuation value for next INT 15,e820 call */
static uint32_t e820_ebx;
/**
* Get system memory map entry
*
* @v prev Previous system memory map entry, or NULL at start
* @v next Next system memory map entry, or NULL at end
*/
struct e820_entry * memmap_next ( struct e820_entry *prev ) {
struct bootapp_callback_params params;
/* Reset buffer and continuation value if restarting */
if ( ! prev ) {
memset ( &e820_buf, 0, sizeof ( e820_buf ) );
e820_ebx = 0;
} else if ( e820_ebx == 0 ) {
/* Reach the end */
return NULL;
}
/* Read system memory map */
memset ( &params, 0, sizeof ( params ) );
do {
/* Call INT 15,e820 */
params.vector.interrupt = 0x15;
params.eax = 0xe820;
params.ebx = e820_ebx;
params.ecx = sizeof ( e820_buf );
params.edx = E820_SMAP;
params.es = BASE_SEG;
params.edi = ( ( ( void * ) &e820_buf ) -
( ( void * ) BASE_ADDRESS ) );
call_interrupt ( &params );
/* Record continuation value */
e820_ebx = params.ebx;
/* Check result */
if ( params.eflags & CF ) {
DBG ( "INT 15,e820 failed: error %02x\n", params.ah );
break;
}
if ( params.eax != E820_SMAP ) {
DBG ( "INT 15,e820 invalid SMAP signature %08x\n",
params.eax );
break;
}
DBG2 ( "INT 15,e820 region [%llx,%llx) type %d\n",
e820_buf.start, ( e820_buf.start + e820_buf.len ),
e820_buf.type );
/* Skip non-RAM regions */
if ( e820_buf.type != E820_TYPE_RAM )
continue;
if ( params.ecx > offsetof ( typeof ( e820_buf ), attrs ) ) {
if ( ! ( e820_buf.attrs & E820_ATTR_ENABLED ) )
continue;
if ( e820_buf.attrs & E820_ATTR_NONVOLATILE )
continue;
}
/* Return this region */
return &e820_buf;
} while ( e820_ebx != 0 );
return NULL;
}
#ifndef _MEMMAP_H
#define _MEMMAP_H
/*
* Copyright (C) 2021 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
*
* Memory map
*
*/
#include <stdint.h>
/** Magic value for INT 15,e820 calls */
#define E820_SMAP 0x534d4150
/** An INT 15,e820 memory map entry */
struct e820_entry {
/** Start of region */
uint64_t start;
/** Length of region */
uint64_t len;
/** Type of region */
uint32_t type;
/** Extended attributes (optional) */
uint32_t attrs;
} __attribute__ (( packed ));
/** Normal RAM */
#define E820_TYPE_RAM 1
/** Region is enabled (if extended attributes are present) */
#define E820_ATTR_ENABLED 0x00000001UL
/** Region is non-volatile memory (if extended attributes are present) */
#define E820_ATTR_NONVOLATILE 0x00000002UL
extern struct e820_entry * memmap_next ( struct e820_entry *prev );
#endif /* _MEMMAP_H */
/*
* Copyright (C) 2021 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
*
* Paging
*
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "wimboot.h"
#include "memmap.h"
#include "paging.h"
/** Virtual address used as a 2MB window during relocation */
#define COPY_WINDOW 0x200000
/** Paging is available */
int paging;
/** Page directory pointer table */
static uint64_t pdpt[4] __attribute__ (( aligned ( PAGE_SIZE ) ));
/** Page directories */
static uint64_t pd[2048] __attribute__ (( aligned ( PAGE_SIZE ) ));
/**
* Check that paging can be supported
*
* @ret supported Paging can be supported on this CPU
*/
static int paging_supported ( void ) {
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
/* Get CPU features */
__asm__ ( "cpuid"
: "=a" ( eax ), "=b" ( ebx ), "=c" ( ecx ), "=d" ( edx )
: "0" ( CPUID_FEATURES ) );
return ( edx & CPUID_FEATURE_EDX_PAE );
}
/**
* Map 2MB page directory entry containing address
*
* @v vaddr Virtual address
* @v paddr Physical address
*/
static void map_page ( uint32_t vaddr, uint64_t paddr ) {
char *byte = ( ( char * ) ( intptr_t ) vaddr );
unsigned int index;
/* Sanity checks */
assert ( ( vaddr & ( PAGE_SIZE_2MB - 1 ) ) == 0 );
assert ( ( paddr & ( PAGE_SIZE_2MB - 1 ) ) == 0 );
/* Populate page directory entry */
index = ( vaddr / PAGE_SIZE_2MB );
pd[index] = ( paddr | PG_P | PG_RW | PG_US | PG_PS );
/* Invalidate TLB */
__asm__ __volatile__ ( "invlpg %0" : : "m" ( *byte ) );
}
/**
* Initialise paging
*
*/
void init_paging ( void ) {
uint32_t addr;
unsigned int i;
/* Do nothing if paging is disabled */
if ( cmdline_linear ) {
DBG ( "Paging disabled\n" );
return;
}
/* Check for PAE */
if ( ! paging_supported() ) {
DBG ( "Paging not possible on this CPU\n" );
return;
}
/* Initialise page directory entries */
addr = 0;
do {
map_page ( addr, addr );
addr += PAGE_SIZE_2MB;
} while ( addr );
/* Initialise page directory pointer table */
for ( i = 0 ; i < ( sizeof ( pdpt ) / sizeof ( pdpt[0] ) ) ; i++ ) {
addr = ( ( intptr_t ) &pd[ i * PAGE_SIZE / sizeof ( pd[0] ) ] );
pdpt[i] = ( addr | PG_P );
}
/* Mark paging as available */
paging = 1;
}
/**
* Enable paging
*
* @v state Saved paging state to fill in
*/
void enable_paging ( struct paging_state *state ) {
unsigned long cr0;
unsigned long cr3;
unsigned long cr4;
/* Do nothing if paging is unavailable */
if ( ! paging )
return;
/* Save paging state */
__asm__ __volatile__ ( "mov %%cr0, %0\n\t"
"mov %%cr3, %1\n\t"
"mov %%cr4, %2\n\t"
: "=r" ( cr0 ), "=r" ( cr3 ), "=r" ( cr4 ) );
state->cr0 = cr0;
state->cr3 = cr3;
state->cr4 = cr4;
/* Disable any existing paging */
__asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 & ~CR0_PG ) );
/* Enable PAE */
__asm__ __volatile__ ( "mov %0, %%cr4" : : "r" ( cr4 | CR4_PAE ) );
/* Load page directory pointer table */
__asm__ __volatile__ ( "mov %0, %%cr3" : : "r" ( pdpt ) );
/* Enable paging */
__asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 | CR0_PG ) );
}
/**
* Disable paging
*
* @v state Previously saved paging state
*/
void disable_paging ( struct paging_state *state ) {
unsigned long cr0 = state->cr0;
unsigned long cr3 = state->cr3;
unsigned long cr4 = state->cr4;
/* Do nothing if paging is unavailable */
if ( ! paging )
return;
/* Disable paging */
__asm__ __volatile__ ( "mov %0, %%cr0" : : "r" ( cr0 & ~CR0_PG ) );
/* Restore saved paging state */
__asm__ __volatile__ ( "mov %2, %%cr4\n\t"
"mov %1, %%cr3\n\t"
"mov %0, %%cr0\n\t"
: : "r" ( cr0 ), "r" ( cr3 ), "r" ( cr4 ) );
}
/**
* Relocate data out of 32-bit address space, if possible
*
* @v data Start of data
* @v len Length of data
* @ret start Physical start address
*/
uint64_t relocate_memory_high ( void *data, size_t len ) {
intptr_t end = ( ( ( intptr_t ) data ) + len );
struct e820_entry *e820 = NULL;
uint64_t start;
uint64_t dest;
size_t offset;
size_t frag_len;
/* Do nothing if paging is unavailable */
if ( ! paging )
return ( ( intptr_t ) data );
/* Read system memory map */
while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {
/* Find highest compatible placement within this region */
start = ( e820->start + e820->len );
if ( start < ADDR_4GB )
continue;
start = ( ( ( start - end ) & ~( PAGE_SIZE_2MB - 1 ) ) + end );
start -= len;
if ( start < e820->start )
continue;
if ( start < ADDR_4GB )
continue;
/* Relocate to this region */
dest = start;
while ( len ) {
/* Calculate length within this 2MB page */
offset = ( ( ( intptr_t ) data ) &
( PAGE_SIZE_2MB - 1 ) );
frag_len = ( PAGE_SIZE_2MB - offset );
if ( frag_len > len )
frag_len = len;
/* Map copy window to destination */
map_page ( COPY_WINDOW,
( dest & ~( PAGE_SIZE_2MB - 1 ) ) );
/* Copy data through copy window */
memcpy ( ( ( ( void * ) COPY_WINDOW ) + offset ),
data, frag_len );
/* Map original page to destination */
map_page ( ( ( ( intptr_t ) data ) - offset ),
( dest & ~( PAGE_SIZE_2MB - 1 ) ) );
/* Move to next 2MB page */
data += frag_len;
dest += frag_len;
len -= frag_len;
}
/* Remap copy window */
map_page ( COPY_WINDOW, COPY_WINDOW );
return start;
}
/* Leave at original location */
return ( ( intptr_t ) data );
}
#ifndef _PAGING_H
#define _PAGING_H
/*
* Copyright (C) 2021 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
*
* Paging
*
*/
#include <stddef.h>
/** Get CPU features */
#define CPUID_FEATURES 0x00000001
/** CPU supports PAE */
#define CPUID_FEATURE_EDX_PAE 0x00000040
/* CR0: paging */
#define CR0_PG 0x80000000
/* CR4: physical address extensions */
#define CR4_PAE 0x00000020
/* Page: present */
#define PG_P 0x01
/* Page: read/write */
#define PG_RW 0x02
/* Page: user/supervisor */
#define PG_US 0x04
/* Page: page size */
#define PG_PS 0x80
/** 2MB page size */
#define PAGE_SIZE_2MB 0x200000
/** 32-bit address space size */
#define ADDR_4GB 0x100000000ULL
/** Saved paging state */
struct paging_state {
/** Control register 0 */
unsigned long cr0;
/** Control register 3 */
unsigned long cr3;
/** Control register 4 */
unsigned long cr4;
};
extern int paging;
extern void init_paging ( void );
extern void enable_paging ( struct paging_state *state );
extern void disable_paging ( struct paging_state *state );
extern uint64_t relocate_memory_high ( void *start, size_t len );
#endif /* _PAGING_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
*
* Diagnostic pause
*
*/
#include <stdio.h>
#include "wimboot.h"
#include "cmdline.h"
#include "pause.h"
/**
* Pause before booting
*
*/
void pause ( void ) {
/* Wait for keypress, prompting unless inhibited */
if ( cmdline_pause_quiet ) {
getchar();
} else {
printf ( "Press any key to continue booting..." );
getchar();
printf ( "\n" );
}
}
#ifndef _PAUSE_H
#define _PAUSE_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
*
* Diagnostic pause
*
*/
extern void pause ( void );
#endif /* _PAUSE_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
*
* PE image loader
*
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include "wimboot.h"
#include "peloader.h"
/**
* Load PE image into memory
*
* @v data PE image
* @v len Length of PE image
* @v pe Loaded PE structure to fill in
* @ret rc Return status code
*/
int load_pe ( const void *data, size_t len, struct loaded_pe *pe ) {
const struct mz_header *mzhdr;
size_t pehdr_offset;
const struct pe_header *pehdr;
size_t opthdr_offset;
const struct pe_optional_header *opthdr;
size_t section_offset;
const struct coff_section *section;
char name[ sizeof ( section->name ) + 1 /* NUL */ ];
unsigned int i;
void *section_base;
size_t filesz;
size_t memsz;
void *end;
void *raw_base;
DBG2 ( "Loading PE executable...\n" );
/* Parse PE header */
mzhdr = data;
if ( mzhdr->magic != MZ_HEADER_MAGIC ) {
DBG ( "Bad MZ magic %04x\n", mzhdr->magic );
return -1;
}
pehdr_offset = mzhdr->lfanew;
if ( pehdr_offset > len ) {
DBG ( "PE header outside file\n" );
return -1;
}
pehdr = ( data + pehdr_offset );
if ( pehdr->magic != PE_HEADER_MAGIC ) {
DBG ( "Bad PE magic %08x\n", pehdr->magic );
return -1;
}
opthdr_offset = ( pehdr_offset + sizeof ( *pehdr ) );
opthdr = ( data + opthdr_offset );
pe->base = ( ( void * ) ( intptr_t ) ( opthdr->base ) );
section_offset = ( opthdr_offset + pehdr->coff.opthdr_len );
section = ( data + section_offset );
/* Load header into memory */
DBG2 ( "...headers to %p+%#x\n", pe->base, opthdr->header_len );
memcpy ( pe->base, data, opthdr->header_len );
end = ( pe->base + opthdr->header_len );
/* Load each section into memory */
for ( i = 0 ; i < pehdr->coff.num_sections ; i++, section++ ) {
memset ( name, 0, sizeof ( name ) );
memcpy ( name, section->name, sizeof ( section->name ) );
section_base = ( pe->base + section->virtual );
filesz = section->raw_len;
memsz = section->misc.virtual_len;
DBG2 ( "...from %#05x to %p+%#zx/%#zx (%s)\n",
section->raw, section_base, filesz, memsz, name );
memset ( section_base, 0, memsz );
memcpy ( section_base, ( data + section->raw ), filesz );
if ( end < ( section_base + memsz ) )
end = ( section_base + memsz );
}
pe->len = ( ( ( end - pe->base ) + opthdr->section_align - 1 )
& ~( opthdr->section_align - 1 ) );
/* Load copy of raw image into memory immediately after loaded
* sections. This seems to be used for verification of X.509
* signatures.
*/
raw_base = ( pe->base + pe->len );
memcpy ( raw_base, data, len );
pe->len += len;
DBG2 ( "...raw copy to %p+%#zx\n", raw_base, len );
/* Extract entry point */
pe->entry = ( pe->base + opthdr->entry );
DBG2 ( "...entry point %p\n", pe->entry );
return 0;
}
#ifndef _PELOADER_H
#define _PELOADER_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
*
* PE image loader
*
*/
#include <stdint.h>
#include "wimboot.h"
/** DOS MZ header */
struct mz_header {
/** Magic number */
uint16_t magic;
/** Bytes on last page of file */
uint16_t cblp;
/** Pages in file */
uint16_t cp;
/** Relocations */
uint16_t crlc;
/** Size of header in paragraphs */
uint16_t cparhdr;
/** Minimum extra paragraphs needed */
uint16_t minalloc;
/** Maximum extra paragraphs needed */
uint16_t maxalloc;
/** Initial (relative) SS value */
uint16_t ss;
/** Initial SP value */
uint16_t sp;
/** Checksum */
uint16_t csum;
/** Initial IP value */
uint16_t ip;
/** Initial (relative) CS value */
uint16_t cs;
/** File address of relocation table */
uint16_t lfarlc;
/** Overlay number */
uint16_t ovno;
/** Reserved words */
uint16_t res[4];
/** OEM identifier (for oeminfo) */
uint16_t oemid;
/** OEM information; oemid specific */
uint16_t oeminfo;
/** Reserved words */
uint16_t res2[10];
/** File address of new exe header */
uint32_t lfanew;
} __attribute__ (( packed ));
/** MZ header magic */
#define MZ_HEADER_MAGIC 0x5a4d
/** COFF file header */
struct coff_header {
/** Magic number */
uint16_t magic;
/** Number of sections */
uint16_t num_sections;
/** Timestamp (seconds since the Epoch) */
uint32_t timestamp;
/** Offset to symbol table */
uint32_t symtab;
/** Number of symbol table entries */
uint32_t num_syms;
/** Length of optional header */
uint16_t opthdr_len;
/** Flags */
uint16_t flags;
} __attribute__ (( packed ));
/** COFF section */
struct coff_section {
/** Section name */
char name[8];
/** Physical address or virtual length */
union {
/** Physical address */
uint32_t physical;
/** Virtual length */
uint32_t virtual_len;
} misc;
/** Virtual address */
uint32_t virtual;
/** Length of raw data */
uint32_t raw_len;
/** Offset to raw data */
uint32_t raw;
/** Offset to relocations */
uint32_t relocations;
/** Offset to line numbers */
uint32_t line_numbers;
/** Number of relocations */
uint16_t num_relocations;
/** Number of line numbers */
uint16_t num_line_numbers;
/** Flags */
uint32_t flags;
} __attribute__ (( packed ));
/** PE file header */
struct pe_header {
/** Magic number */
uint32_t magic;
/** COFF header */
struct coff_header coff;
} __attribute__ (( packed ));
/** PE header magic */
#define PE_HEADER_MAGIC 0x00004550
/** PE optional header */
struct pe_optional_header {
/** Magic number */
uint16_t magic;
/** Major linker version */
uint8_t linker_major;
/** Minor linker version */
uint8_t linker_minor;
/** Length of code */
uint32_t text_len;
/** Length of initialised data */
uint32_t data_len;
/** Length of uninitialised data */
uint32_t bss_len;
/** Entry point */
uint32_t entry;
/** Base of code */
uint32_t text;
/** Base of data */
uint32_t data;
/** Image base address */
uint32_t base;
/** Section alignment */
uint32_t section_align;
/** File alignment */
uint32_t file_align;
/** Major operating system version */
uint16_t os_major;
/** Minor operating system version */
uint16_t os_minor;
/** Major image version */
uint16_t image_major;
/** Minor image version */
uint16_t image_minor;
/** Major subsystem version */
uint16_t subsystem_major;
/** Minor subsystem version */
uint16_t subsystem_minor;
/** Win32 version */
uint32_t win32_version;
/** Size of image */
uint32_t len;
/** Size of headers */
uint32_t header_len;
/* Plus extra fields that we don't care about */
} __attribute__ (( packed ));
/** A loaded PE image */
struct loaded_pe {
/** Base address */
void *base;
/** Length */
size_t len;
/** Entry point */
void ( * entry ) ( struct bootapp_descriptor *bootapp );
};
extern int load_pe ( const void *data, size_t len, struct loaded_pe *pe );
#endif /* _PELOADER_H */
#ifndef _ROTATE_H
#define _ROTATE_H
/** @file
*
* Bit operations
*/
#include <stdint.h>
static inline __attribute__ (( always_inline )) uint8_t
rol8 ( uint8_t data, unsigned int rotation ) {
return ( ( data << rotation ) | ( data >> ( 8 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint8_t
ror8 ( uint8_t data, unsigned int rotation ) {
return ( ( data >> rotation ) | ( data << ( 8 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint16_t
rol16 ( uint16_t data, unsigned int rotation ) {
return ( ( data << rotation ) | ( data >> ( 16 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint16_t
ror16 ( uint16_t data, unsigned int rotation ) {
return ( ( data >> rotation ) | ( data << ( 16 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint32_t
rol32 ( uint32_t data, unsigned int rotation ) {
return ( ( data << rotation ) | ( data >> ( 32 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint32_t
ror32 ( uint32_t data, unsigned int rotation ) {
return ( ( data >> rotation ) | ( data << ( 32 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint64_t
rol64 ( uint64_t data, unsigned int rotation ) {
return ( ( data << rotation ) | ( data >> ( 64 - rotation ) ) );
}
static inline __attribute__ (( always_inline )) uint64_t
ror64 ( uint64_t data, unsigned int rotation ) {
return ( ( data >> rotation ) | ( data << ( 64 - rotation ) ) );
}
#endif /* _ROTATE_H */
SECTIONS {
/* Align sections to keep PE tools happy */
alignment = 16;
/* Virtual addresses start at 0x20000 */
. = 0x20000;
_start = .;
/* bzImage prefix */
_prefix_pos = 0;
.prefix : AT ( _prefix_pos ) {
_prefix = .;
*(.prefix)
*(.prefix.*)
. = ALIGN ( alignment );
_eprefix = .;
}
_prefix_len = ABSOLUTE ( _eprefix ) - ABSOLUTE ( _prefix );
/* Real-mode uninitialised data section */
.bss16 ( NOLOAD ) : {
_bss16 = .;
*(.stack16)
*(.stack16.*)
*(.bss16)
*(.bss16.*)
. = ALIGN ( alignment );
_ebss16 = .;
}
_bss16_len = ABSOLUTE ( _ebss16 ) - ABSOLUTE ( _bss16 );
/* Payload section */
_payload_pos = ( _prefix_pos + _prefix_len );
.payload : AT ( _payload_pos ) {
_payload = .;
/* Portions that must be accessible in 16-bit modes */
_text16 = .;
*(.text16)
*(.text16.*)
_etext16 = .;
_data16 = .;
*(.rodata16)
*(.rodata16.*)
*(.data16)
*(.data16.*)
_edata16 = .;
/* Portions that need not be accessible in 16-bit modes */
_text = .;
*(.text)
*(.text.*)
_etext = .;
_data = .;
*(.rodata)
*(.rodata.*)
*(.data)
*(.data.*)
. = ALIGN ( alignment );
_edata = .;
_epayload = .;
}
_text16_len = ABSOLUTE ( _etext16 ) - ABSOLUTE ( _text16 );
_data16_len = ABSOLUTE ( _edata16 ) - ABSOLUTE ( _data16 );
_text_len = ABSOLUTE ( _etext ) - ABSOLUTE ( _text );
_data_len = ABSOLUTE ( _edata ) - ABSOLUTE ( _data );
_payload_len = ABSOLUTE ( _epayload ) - ABSOLUTE ( _payload );
/* bootmgr.exe hardcodes the address 0x30000 for use as a
* buffer accessible by real-mode code. We can't fit our
* .text, .data, and .bss below this region, so explicitly
* place the .bss higher in memory.
*/
_forbidden_start = 0x30000;
_forbidden_end = 0x40000;
/* Uninitialised data section */
.bss ( NOLOAD ) : {
_bss = .;
ASSERT ( ABSOLUTE ( . ) <= ABSOLUTE ( _forbidden_start ),
"Binary is too large" );
. = ABSOLUTE ( _forbidden_end );
*(.bss)
*(.bss.*)
*(COMMON)
*(.stack)
*(.stack.*)
. = ALIGN ( alignment );
_ebss = .;
}
_bss_len = ABSOLUTE ( _ebss ) - ABSOLUTE ( _bss );
/* Relocations section */
_reloc_pos = ( _payload_pos + _payload_len );
_reloc = .;
_end = .;
_text_total_len = ( _text_len + _text16_len );
_data_total_len = ( _data_len + _data16_len );
_bss_total_len = ( _bss_len + _bss16_len );
/* Symbols required by i386.x86_64 objects */
__i386__start = _start;
__i386__end = _end;
/DISCARD/ : {
*(.comment)
*(.comment.*)
*(.note)
*(.note.*)
*(.eh_frame)
*(.eh_frame.*)
*(.rel)
*(.rel.*)
}
}
/*
* 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 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.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/
/**
* @file
*
* SHA-1 algorithm
*
*/
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <byteswap.h>
#include <assert.h>
#include "rotate.h"
#include "sha1.h"
/** SHA-1 variables */
struct sha1_variables {
/* This layout matches that of struct sha1_digest_data,
* allowing for efficient endianness-conversion,
*/
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
uint32_t e;
uint32_t w[80];
} __attribute__ (( packed ));
/**
* f(a,b,c,d) for steps 0 to 19
*
* @v v SHA-1 variables
* @ret f f(a,b,c,d)
*/
static uint32_t sha1_f_0_19 ( struct sha1_variables *v ) {
return ( ( v->b & v->c ) | ( (~v->b) & v->d ) );
}
/**
* f(a,b,c,d) for steps 20 to 39 and 60 to 79
*
* @v v SHA-1 variables
* @ret f f(a,b,c,d)
*/
static uint32_t sha1_f_20_39_60_79 ( struct sha1_variables *v ) {
return ( v->b ^ v->c ^ v->d );
}
/**
* f(a,b,c,d) for steps 40 to 59
*
* @v v SHA-1 variables
* @ret f f(a,b,c,d)
*/
static uint32_t sha1_f_40_59 ( struct sha1_variables *v ) {
return ( ( v->b & v->c ) | ( v->b & v->d ) | ( v->c & v->d ) );
}
/** An SHA-1 step function */
struct sha1_step {
/**
* Calculate f(a,b,c,d)
*
* @v v SHA-1 variables
* @ret f f(a,b,c,d)
*/
uint32_t ( * f ) ( struct sha1_variables *v );
/** Constant k */
uint32_t k;
};
/** SHA-1 steps */
static struct sha1_step sha1_steps[4] = {
/** 0 to 19 */
{ .f = sha1_f_0_19, .k = 0x5a827999 },
/** 20 to 39 */
{ .f = sha1_f_20_39_60_79, .k = 0x6ed9eba1 },
/** 40 to 59 */
{ .f = sha1_f_40_59, .k = 0x8f1bbcdc },
/** 60 to 79 */
{ .f = sha1_f_20_39_60_79, .k = 0xca62c1d6 },
};
/**
* Initialise SHA-1 algorithm
*
* @v ctx SHA-1 context
*/
void sha1_init ( void *ctx ) {
struct sha1_context *context = ctx;
context->ddd.dd.digest.h[0] = cpu_to_be32 ( 0x67452301 );
context->ddd.dd.digest.h[1] = cpu_to_be32 ( 0xefcdab89 );
context->ddd.dd.digest.h[2] = cpu_to_be32 ( 0x98badcfe );
context->ddd.dd.digest.h[3] = cpu_to_be32 ( 0x10325476 );
context->ddd.dd.digest.h[4] = cpu_to_be32 ( 0xc3d2e1f0 );
context->len = 0;
}
/**
* Calculate SHA-1 digest of accumulated data
*
* @v context SHA-1 context
*/
static void sha1_digest ( struct sha1_context *context ) {
union {
union sha1_digest_data_dwords ddd;
struct sha1_variables v;
} u;
uint32_t *a = &u.v.a;
uint32_t *b = &u.v.b;
uint32_t *c = &u.v.c;
uint32_t *d = &u.v.d;
uint32_t *e = &u.v.e;
uint32_t *w = u.v.w;
uint32_t f;
uint32_t k;
uint32_t temp;
struct sha1_step *step;
unsigned int i;
/* Convert h[0..4] to host-endian, and initialise a, b, c, d,
* e, and w[0..15]
*/
for ( i = 0 ; i < ( sizeof ( u.ddd.dword ) /
sizeof ( u.ddd.dword[0] ) ) ; i++ ) {
be32_to_cpus ( &context->ddd.dword[i] );
u.ddd.dword[i] = context->ddd.dword[i];
}
/* Initialise w[16..79] */
for ( i = 16 ; i < 80 ; i++ )
w[i] = rol32 ( ( w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16] ), 1 );
/* Main loop */
for ( i = 0 ; i < 80 ; i++ ) {
step = &sha1_steps[ i / 20 ];
f = step->f ( &u.v );
k = step->k;
temp = ( rol32 ( *a, 5 ) + f + *e + k + w[i] );
*e = *d;
*d = *c;
*c = rol32 ( *b, 30 );
*b = *a;
*a = temp;
}
/* Add chunk to hash and convert back to big-endian */
for ( i = 0 ; i < 5 ; i++ ) {
context->ddd.dd.digest.h[i] =
cpu_to_be32 ( context->ddd.dd.digest.h[i] +
u.ddd.dd.digest.h[i] );
}
}
/**
* Accumulate data with SHA-1 algorithm
*
* @v ctx SHA-1 context
* @v data Data
* @v len Length of data
*/
void sha1_update ( void *ctx, const void *data, size_t len ) {
struct sha1_context *context = ctx;
const uint8_t *byte = data;
size_t offset;
/* Accumulate data a byte at a time, performing the digest
* whenever we fill the data buffer
*/
while ( len-- ) {
offset = ( context->len % sizeof ( context->ddd.dd.data ) );
context->ddd.dd.data.byte[offset] = *(byte++);
context->len++;
if ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 )
sha1_digest ( context );
}
}
/**
* Generate SHA-1 digest
*
* @v ctx SHA-1 context
* @v out Output buffer
*/
void sha1_final ( void *ctx, void *out ) {
struct sha1_context *context = ctx;
uint64_t len_bits;
uint8_t pad;
/* Record length before pre-processing */
len_bits = cpu_to_be64 ( ( ( uint64_t ) context->len ) * 8 );
/* Pad with a single "1" bit followed by as many "0" bits as required */
pad = 0x80;
do {
sha1_update ( ctx, &pad, sizeof ( pad ) );
pad = 0x00;
} while ( ( context->len % sizeof ( context->ddd.dd.data ) ) !=
offsetof ( typeof ( context->ddd.dd.data ), final.len ) );
/* Append length (in bits) */
sha1_update ( ctx, &len_bits, sizeof ( len_bits ) );
assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 );
/* Copy out final digest */
memcpy ( out, &context->ddd.dd.digest,
sizeof ( context->ddd.dd.digest ) );
}
#ifndef _SHA1_H
#define _SHA1_H
/** @file
*
* SHA-1 algorithm
*
*/
#include <stdint.h>
/** An SHA-1 digest */
struct sha1_digest {
/** Hash output */
uint32_t h[5];
};
/** An SHA-1 data block */
union sha1_block {
/** Raw bytes */
uint8_t byte[64];
/** Raw dwords */
uint32_t dword[16];
/** Final block structure */
struct {
/** Padding */
uint8_t pad[56];
/** Length in bits */
uint64_t len;
} final;
};
/** SHA-1 digest and data block
*
* The order of fields within this structure is designed to minimise
* code size.
*/
struct sha1_digest_data {
/** Digest of data already processed */
struct sha1_digest digest;
/** Accumulated data */
union sha1_block data;
} __attribute__ (( packed ));
/** SHA-1 digest and data block */
union sha1_digest_data_dwords {
/** Digest and data block */
struct sha1_digest_data dd;
/** Raw dwords */
uint32_t dword[ sizeof ( struct sha1_digest_data ) /
sizeof ( uint32_t ) ];
};
/** An SHA-1 context */
struct sha1_context {
/** Amount of accumulated data */
size_t len;
/** Digest and accumulated data */
union sha1_digest_data_dwords ddd;
} __attribute__ (( packed ));
/** SHA-1 context size */
#define SHA1_CTX_SIZE sizeof ( struct sha1_context )
/** SHA-1 digest size */
#define SHA1_DIGEST_SIZE sizeof ( struct sha1_digest )
extern void sha1_init ( void *ctx );
extern void sha1_update ( void *ctx, const void *data, size_t len );
extern void sha1_final ( void *ctx, void *out );
#endif /* _SHA1_H */
#ifndef _STDARG_H
#define _STDARG_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 arguments
*
*/
typedef __builtin_va_list va_list;
#define va_start( ap, last ) __builtin_va_start ( ap, last )
#define va_arg( ap, type ) __builtin_va_arg ( ap, type )
#define va_end( ap ) __builtin_va_end ( ap )
#define va_copy( dest, src ) __builtin_va_copy ( dest, src )
#endif /* _STDARG_H */
#ifndef _STDDEF_H
#define _STDDEF_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 definitions
*
*/
#include <stdint.h>
#define NULL ( ( void * ) 0 )
#define offsetof( type, member ) ( ( size_t ) &( ( type * ) NULL )->member )
#define container_of( ptr, type, member ) ( { \
const typeof ( ( ( type * ) NULL )->member ) *__mptr = (ptr); \
( type * ) ( ( void * ) __mptr - offsetof ( type, member ) ); } )
#endif /* _STDDEF_H */
#ifndef _STDINT_H
#define _STDINT_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 integer types
*
*/
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
typedef unsigned long intptr_t;
typedef __SIZE_TYPE__ size_t;
typedef signed long ssize_t;
typedef __WCHAR_TYPE__ wchar_t;
typedef __WINT_TYPE__ wint_t;
#endif /* _STDINT_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 <stdio.h>
#include <string.h>
#include "bootapp.h"
#include "wimboot.h"
#include "efi.h"
/**
* Print character to console
*
* @v character Character to print
*/
int putchar ( int character ) {
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout;
struct bootapp_callback_params params;
wchar_t wbuf[2];
/* Convert LF to CR,LF */
if ( character == '\n' )
putchar ( '\r' );
/* Print character to bochs debug port */
__asm__ __volatile__ ( "outb %b0, $0xe9"
: : "a" ( character ) );
/* Print character to EFI/BIOS console as applicable */
if ( efi_systab ) {
conout = efi_systab->ConOut;
wbuf[0] = character;
wbuf[1] = 0;
conout->OutputString ( conout, wbuf );
} else {
memset ( &params, 0, sizeof ( params ) );
params.vector.interrupt = 0x10;
params.eax = ( 0x0e00 | character );
params.ebx = 0x0007;
call_interrupt ( &params );
}
return 0;
}
/**
* Get character from console
*
* @ret character Character
*/
int getchar ( void ) {
EFI_BOOT_SERVICES *bs;
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin;
EFI_INPUT_KEY key;
UINTN index;
struct bootapp_callback_params params;
int character;
/* Get character */
if ( efi_systab ) {
bs = efi_systab->BootServices;
conin = efi_systab->ConIn;
bs->WaitForEvent ( 1, &conin->WaitForKey, &index );
conin->ReadKeyStroke ( conin, &key );
character = key.UnicodeChar;
} else {
memset ( &params, 0, sizeof ( params ) );
params.vector.interrupt = 0x16;
call_interrupt ( &params );
character = params.al;
}
return character;
}
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