mirror of
https://github.com/chipsec/chipsec
synced 2026-06-08 13:31:00 +00:00
395 lines
11 KiB
C
395 lines
11 KiB
C
/** @file
|
|
|
|
Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
CHIPSEC uses modified version of EfiCompressor from: https://github.com/theopolis/uefi-firmware-parser
|
|
|
|
Modified for uefi_firmware_parser:
|
|
This includes minor API changes for Tiano and EFI decompressor, as well as LZMA.
|
|
|
|
**/
|
|
|
|
#include <Python.h>
|
|
|
|
#include "CompressionTypes.h"
|
|
|
|
#include "Tiano/Decompress.h"
|
|
#include "Tiano/Compress.h"
|
|
#include "LZMA/LzmaDecompress.h"
|
|
#include "LZMA/LzmaCompress.h"
|
|
|
|
//#include "EfiFile.h"
|
|
|
|
#define EFI_COMPRESSION 1 //defined as PI_STD, section type= 0x01
|
|
#define TIANO_COMPRESSION 2 //not defined, section type= 0x01
|
|
#define LZMA_COMPRESSION 3 //not defined, section type= 0x02
|
|
|
|
#define MAX_DSTSZ 40000000 //40MB -- Max destination buffer size allowed.
|
|
//I don't think there is an image to decompress bigger than this. In any case, feel free to change.
|
|
|
|
EFI_STATUS
|
|
Extract (
|
|
IN VOID *Source,
|
|
IN UINT32 SrcSize,
|
|
OUT VOID **Destination,
|
|
OUT UINT32 *DstSize,
|
|
IN UINTN Algorithm
|
|
)
|
|
{
|
|
VOID *Scratch;
|
|
UINT32 ScratchSize;
|
|
EFI_STATUS Status;
|
|
|
|
GETINFO_FUNCTION GetInfoFunction;
|
|
DECOMPRESS_FUNCTION DecompressFunction;
|
|
|
|
GetInfoFunction = NULL;
|
|
DecompressFunction = NULL;
|
|
Scratch = NULL;
|
|
ScratchSize = 0;
|
|
Status = EFI_SUCCESS;
|
|
|
|
switch (Algorithm) {
|
|
case 0:
|
|
*Destination = (VOID *)malloc(SrcSize);
|
|
if (*Destination != NULL) {
|
|
memcpy(*Destination, Source, SrcSize);
|
|
} else {
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
break;
|
|
case 1:
|
|
GetInfoFunction = EfiGetInfo;
|
|
DecompressFunction = EfiDecompress;
|
|
break;
|
|
case 2:
|
|
GetInfoFunction = TianoGetInfo;
|
|
DecompressFunction = TianoDecompress;
|
|
break;
|
|
case 3:
|
|
GetInfoFunction = LzmaGetInfo;
|
|
DecompressFunction = LzmaDecompress;
|
|
break;
|
|
default:
|
|
Status = EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (GetInfoFunction != NULL) {
|
|
Status = GetInfoFunction(Source, SrcSize, DstSize, &ScratchSize);
|
|
if (Status == EFI_SUCCESS) {
|
|
if (ScratchSize > 0) {
|
|
Scratch = (VOID *)malloc(ScratchSize);
|
|
}
|
|
if(*DstSize <= MAX_DSTSZ){
|
|
*Destination = (VOID *)malloc(*DstSize);
|
|
}
|
|
if (((ScratchSize > 0 && Scratch != NULL) || ScratchSize == 0) && *Destination != NULL) {
|
|
Status = DecompressFunction(Source, SrcSize, *Destination, *DstSize, Scratch, ScratchSize);
|
|
} else {
|
|
free(*Destination);
|
|
free(Scratch);
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
EFI_STATUS
|
|
ParseObject(
|
|
PyObject *SrcData,
|
|
UINT8 *SrcBuf,
|
|
UINT32 MaxSize
|
|
)
|
|
{
|
|
UINT32 ObjLen;
|
|
UINT8 *TmpBuf;
|
|
Py_ssize_t SegNum;
|
|
Py_ssize_t Index;
|
|
|
|
ObjLen = 0;
|
|
SegNum = SrcData->ob_type->tp_as_buffer->bf_getsegcount((PyObject *)SrcData, NULL);
|
|
TmpBuf = SrcBuf;
|
|
for (Index = 0; Index < SegNum; ++Index) {
|
|
VOID *BufSeg;
|
|
Py_ssize_t Len;
|
|
|
|
Len = SrcData->ob_type->tp_as_buffer->bf_getreadbuffer((PyObject *)SrcData, Index, &BufSeg);
|
|
if (Len < 0) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (ObjLen + Len > MaxSize) {
|
|
return EFI_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
memcpy(TmpBuf, BufSeg, Len);
|
|
TmpBuf += Len;
|
|
ObjLen += Len;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
void
|
|
errorHandling(
|
|
VOID* SrcBuf,
|
|
VOID* DstBuf
|
|
)
|
|
{
|
|
if (SrcBuf != NULL) {
|
|
free(SrcBuf);
|
|
}
|
|
|
|
if (DstBuf != NULL) {
|
|
free(DstBuf);
|
|
}
|
|
}
|
|
|
|
/*
|
|
UefiDecompress(data_buffer, size, huffman_type)
|
|
*/
|
|
STATIC
|
|
PyObject*
|
|
UefiDecompress(
|
|
PyObject *Self,
|
|
PyObject *Args,
|
|
UINT8 type
|
|
)
|
|
{
|
|
PyObject *SrcData;
|
|
UINT32 SrcDataSize;
|
|
UINT32 DstDataSize;
|
|
UINT32 Status;
|
|
UINT8 *SrcBuf;
|
|
UINT8 *DstBuf;
|
|
|
|
DstDataSize = 0;
|
|
DstBuf = NULL;
|
|
|
|
Status = PyArg_ParseTuple(Args, "Oi", &SrcData, &SrcDataSize);
|
|
if (Status == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SrcData->ob_type->tp_as_buffer == NULL
|
|
|| SrcData->ob_type->tp_as_buffer->bf_getreadbuffer == NULL
|
|
|| SrcData->ob_type->tp_as_buffer->bf_getsegcount == NULL) {
|
|
PyErr_SetString(PyExc_Exception, "First argument is not a buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Because some Python objects which support "buffer" protocol have more than one
|
|
// memory segment, we have to copy them into a contiguous memory.
|
|
SrcBuf = PyMem_Malloc(SrcDataSize);
|
|
if (SrcBuf == NULL) {
|
|
PyErr_SetString(PyExc_Exception, "Not enough memory\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
Status = ParseObject(SrcData, SrcBuf, SrcDataSize);
|
|
if (Status != EFI_SUCCESS) {
|
|
PyErr_SetString(PyExc_Exception, "Buffer segment is not available, or incorrect length\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
Status = Extract((VOID *)SrcBuf, SrcDataSize, (VOID **)&DstBuf, &DstDataSize, type);
|
|
if (Status != EFI_SUCCESS) {
|
|
PyErr_SetString(PyExc_Exception, "Failed to decompress\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
return PyBuffer_FromMemory(DstBuf, (Py_ssize_t)DstDataSize);
|
|
}
|
|
|
|
/*
|
|
UefiCompress(data_buffer, size, huffman_type)
|
|
*/
|
|
STATIC
|
|
PyObject*
|
|
UefiCompress(
|
|
PyObject *Self,
|
|
PyObject *Args,
|
|
UINT8 type
|
|
)
|
|
{
|
|
PyObject *SrcData;
|
|
UINT32 SrcDataSize;
|
|
UINT32 DstDataSize;
|
|
UINT32 Status;
|
|
UINT8 *SrcBuf;
|
|
UINT8 *DstBuf;
|
|
|
|
// Pick the compress function based on compression type
|
|
COMPRESS_FUNCTION CompressFunction;
|
|
|
|
DstDataSize = 0;
|
|
DstBuf = NULL;
|
|
CompressFunction = NULL;
|
|
|
|
Status = PyArg_ParseTuple(Args, "Oi", &SrcData, &SrcDataSize);
|
|
if (Status == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (SrcData->ob_type->tp_as_buffer == NULL
|
|
|| SrcData->ob_type->tp_as_buffer->bf_getreadbuffer == NULL
|
|
|| SrcData->ob_type->tp_as_buffer->bf_getsegcount == NULL) {
|
|
PyErr_SetString(PyExc_Exception, "First argument is not a buffer\n");
|
|
return NULL;
|
|
}
|
|
|
|
// Because some Python objects which support "buffer" protocol have more than one
|
|
// memory segment, we have to copy them into a contiguous memory.
|
|
SrcBuf = PyMem_Malloc(SrcDataSize);
|
|
if (SrcBuf == NULL) {
|
|
PyErr_SetString(PyExc_Exception, "Not enough memory\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
Status = ParseObject(SrcData, SrcBuf, SrcDataSize);
|
|
if (Status != EFI_SUCCESS) {
|
|
PyErr_SetString(PyExc_Exception, "Buffer segment is not available, or incorrect length\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
if (type == 3) {
|
|
CompressFunction = (COMPRESS_FUNCTION) LzmaCompress;
|
|
} else {
|
|
CompressFunction = (COMPRESS_FUNCTION) ((type == EFI_COMPRESSION) ? EfiCompress : TianoCompress);
|
|
}
|
|
Status = CompressFunction(SrcBuf, SrcDataSize, DstBuf, &DstDataSize);
|
|
if (Status == EFI_BUFFER_TOO_SMALL) {
|
|
// The first call to compress fills in the expected destination size.
|
|
DstBuf = malloc (DstDataSize);
|
|
if (!DstBuf) {
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
// The second call to compress compresses.
|
|
Status = CompressFunction(SrcBuf, SrcDataSize, DstBuf, &DstDataSize);
|
|
}
|
|
|
|
if (Status != EFI_SUCCESS) {
|
|
PyErr_SetString(PyExc_Exception, "Failed to compress\n");
|
|
errorHandling(SrcBuf, DstBuf);
|
|
return NULL;
|
|
}
|
|
|
|
return PyBuffer_FromMemory(DstBuf, (Py_ssize_t)DstDataSize);
|
|
}
|
|
|
|
/**
|
|
|
|
The following functions are semi-cyclic, they call a Python-abstraction that calls
|
|
replica version of the following two entry points. Each uses a cased short to determine
|
|
the huffman-decode implementation.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_EfiDecompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "EFI"-type compression, or PI_STD (4-bit symbol tables). */
|
|
return UefiDecompress(Self, Args, EFI_COMPRESSION);
|
|
}
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_TianoDecompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "Tiano"-type compression (5-bit symbol tables). */
|
|
return UefiDecompress(Self, Args, TIANO_COMPRESSION);
|
|
}
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_LzmaDecompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "Tiano"-type compression (5-bit symbol tables). */
|
|
return UefiDecompress(Self, Args, LZMA_COMPRESSION);
|
|
}
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_EfiCompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "EFI"-type compression, or PI_STD (4-bit symbol tables). */
|
|
return UefiCompress(Self, Args, EFI_COMPRESSION);
|
|
}
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_TianoCompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "Tiano"-type compression (5-bit symbol tables). */
|
|
return UefiCompress(Self, Args, TIANO_COMPRESSION);
|
|
}
|
|
|
|
STATIC
|
|
PyObject*
|
|
Py_LzmaCompress(
|
|
PyObject *Self,
|
|
PyObject *Args
|
|
)
|
|
{
|
|
/* Use the "Tiano"-type compression (5-bit symbol tables). */
|
|
return UefiCompress(Self, Args, LZMA_COMPRESSION);
|
|
}
|
|
|
|
#define EFI_DECOMPRESS_DOCS "EfiDecompress(): Decompress data using the EDKII standard algorithm.\n"
|
|
#define TIANO_DECOMPRESS_DOCS "TianoDecompress(): Decompress data using 5-bit Huffman encoding.\n"
|
|
#define LZMA_DECOMPRESS_DOCS "LzmaDecompress(): Decompress using 7-z LZMA alogrithm.\n"
|
|
#define EFI_COMPRESS_DOCS "EfiCompress(): Compress data using the EDKII standard algorithm.\n"
|
|
#define TIANO_COMPRESS_DOCS "TianoCompress(): Compress data using 5-bit Huffman encoding.\n"
|
|
#define LZMA_COMPRESS_DOCS "LzmaCompress(): Compress using 7-z LZMA alogrithm.\n"
|
|
|
|
|
|
STATIC PyMethodDef EfiCompressor_Funcs[] = {
|
|
{"EfiDecompress", (PyCFunction)Py_EfiDecompress, METH_VARARGS, EFI_DECOMPRESS_DOCS},
|
|
{"TianoDecompress", (PyCFunction)Py_TianoDecompress, METH_VARARGS, TIANO_DECOMPRESS_DOCS},
|
|
{"LzmaDecompress", (PyCFunction)Py_LzmaDecompress, METH_VARARGS, LZMA_DECOMPRESS_DOCS},
|
|
{"EfiCompress", (PyCFunction)Py_EfiCompress, METH_VARARGS, EFI_COMPRESS_DOCS},
|
|
{"TianoCompress", (PyCFunction)Py_TianoCompress, METH_VARARGS, TIANO_COMPRESS_DOCS},
|
|
{"LzmaCompress", (PyCFunction)Py_LzmaCompress, METH_VARARGS, LZMA_COMPRESS_DOCS},
|
|
|
|
{NULL, NULL, 0, NULL}
|
|
};
|
|
|
|
PyMODINIT_FUNC
|
|
initefi_compressor(VOID) {
|
|
Py_InitModule3("efi_compressor", EfiCompressor_Funcs, "Various EFI Compression Algorithms Extension Module");
|
|
}
|
|
|
|
|