You've already forked slimbootloader
mirror of
https://github.com/Dasharo/slimbootloader.git
synced 2026-03-06 15:26:20 -08:00
0e1098d7b2
This patch added DMA memory type into memory allocation pool for payloads. This DMA memory buffer with PcdDmaBufferSize is located at address aligned at PcdDmaBufferAlignment after Payload reserved memory. Memory type EfiRuntimeServicesData is used to indicate DMA memory type. Stage1B calculates the DMA memory location using fixed PCDs so that platform can set up DMA protection as early as possible after memory is ready. In Stage1B or Stage2 platform code should use platform VTd information to setup PMR to protect all low memory except for the DMA buffer range. DMA memory will be added into memory pool at the entry point of the payload. Before transfering to OS, the DMA memory protection can be disabled, and the DMA memory pool can be reclaimed for OS usage. Currently only boot media device will utilize the DMA buffer range for block access operations. So it should only be required by payloads. GFX, when enabled, will also use DMA. It will be targeted to the system stolen memory which is not protected by PMR. Signed-off-by: Maurice Ma <maurice.ma@intel.com>
1332 lines
59 KiB
Python
Executable File
1332 lines
59 KiB
Python
Executable File
#!/usr/bin/env python
|
|
## @ BuildLoader.py
|
|
# Build bootloader main script
|
|
#
|
|
# Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.<BR>
|
|
# SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
##
|
|
# Import Modules
|
|
#
|
|
import os
|
|
import sys
|
|
|
|
tool_dir = os.path.join(os.path.dirname (os.path.realpath(__file__)), 'BootloaderCorePkg', 'Tools')
|
|
sys.dont_write_bytecode = True
|
|
sys.path.append (tool_dir)
|
|
|
|
import re
|
|
import imp
|
|
import errno
|
|
import shutil
|
|
import argparse
|
|
import subprocess
|
|
import multiprocessing
|
|
from ctypes import *
|
|
from BuildUtility import *
|
|
|
|
|
|
def rebuild_basetools ():
|
|
exe_list = 'GenFfs GenFv GenFw GenSec Lz4Compress LzmaCompress'.split()
|
|
ret = 0
|
|
sblsource = os.environ['SBL_SOURCE']
|
|
|
|
if os.name == 'posix':
|
|
if not check_files_exist (exe_list, os.path.join(sblsource, 'BaseTools', 'Source', 'C', 'bin')):
|
|
ret = run_process (['make', '-C', 'BaseTools'])
|
|
|
|
elif os.name == 'nt':
|
|
|
|
if not check_files_exist (exe_list, os.path.join(sblsource, 'BaseTools', 'Bin', 'Win32'), '.exe'):
|
|
print ("Could not find pre-built BaseTools binaries, try to rebuild BaseTools ...")
|
|
ret = run_process (['BaseTools\\toolsetup.bat', 'forcerebuild'])
|
|
|
|
if ret:
|
|
print ("Build BaseTools failed, please check required build environment and utilities !")
|
|
sys.exit(1)
|
|
|
|
def prep_env ():
|
|
# check python version first
|
|
version = check_for_python ()
|
|
os.environ['PYTHON_COMMAND'] = '"' + sys.executable + '"'
|
|
print_tool_version_info(os.environ['PYTHON_COMMAND'], version.strip())
|
|
|
|
sblsource = os.path.dirname(os.path.realpath(__file__))
|
|
os.chdir(sblsource)
|
|
if os.name == 'posix':
|
|
toolchain = 'GCC49'
|
|
gcc_ver = run_process (['gcc', '-dumpversion'], capture_out = True)
|
|
gcc_ver = gcc_ver.strip()
|
|
if int(gcc_ver.split('.')[0]) > 4:
|
|
toolchain = 'GCC5'
|
|
os.environ['PATH'] = os.environ['PATH'] + ':' + os.path.join(sblsource, 'BaseTools/BinWrappers/PosixLike')
|
|
toolchain_ver = gcc_ver
|
|
elif os.name == 'nt':
|
|
os.environ['PATH'] = os.environ['PATH'] + ';' + os.path.join(sblsource, 'BaseTools\\Bin\\Win32')
|
|
os.environ['PATH'] = os.environ['PATH'] + ';' + os.path.join(sblsource, 'BaseTools\\BinWrappers\\WindowsLike')
|
|
os.environ['PYTHONPATH'] = os.path.join(sblsource, 'BaseTools', 'Source', 'Python')
|
|
|
|
toolchain, toolchain_prefix, toolchain_path, toolchain_ver = get_visual_studio_info ()
|
|
if toolchain:
|
|
os.environ[toolchain_prefix] = toolchain_path
|
|
else:
|
|
print("Could not find supported Visual Studio version !")
|
|
sys.exit(1)
|
|
if 'NASM_PREFIX' not in os.environ:
|
|
os.environ['NASM_PREFIX'] = "C:\\Nasm\\"
|
|
if 'OPENSSL_PATH' not in os.environ:
|
|
os.environ['OPENSSL_PATH'] = "C:\\Openssl\\"
|
|
if 'IASL_PREFIX' not in os.environ:
|
|
os.environ['IASL_PREFIX'] = "C:\\ASL\\"
|
|
else:
|
|
print("Unsupported operating system !")
|
|
sys.exit(1)
|
|
|
|
print_tool_version_info(toolchain, toolchain_ver)
|
|
|
|
check_for_openssl()
|
|
check_for_nasm()
|
|
check_for_git()
|
|
|
|
# Update Environment vars
|
|
os.environ['SBL_SOURCE'] = sblsource
|
|
os.environ['EDK_TOOLS_PATH'] = os.path.join(sblsource, 'BaseTools')
|
|
os.environ['BASE_TOOLS_PATH'] = os.path.join(sblsource, 'BaseTools')
|
|
if 'WORKSPACE' not in os.environ:
|
|
os.environ['WORKSPACE'] = sblsource
|
|
os.environ['CONF_PATH'] = os.path.join(os.environ['WORKSPACE'], 'Conf')
|
|
os.environ['TOOL_CHAIN'] = toolchain
|
|
|
|
def get_board_config_file (check_dir, board_cfgs):
|
|
platform_dir = os.path.join (check_dir, 'Platform')
|
|
if not os.path.isdir (platform_dir):
|
|
if os.path.basename(check_dir) == 'Platform':
|
|
platform_dir = check_dir
|
|
else:
|
|
return
|
|
|
|
board_pkgs = os.listdir(platform_dir)
|
|
for pkg in board_pkgs:
|
|
cfgfile = os.path.join(platform_dir, pkg, 'BoardConfig.py')
|
|
if os.path.exists(cfgfile):
|
|
board_cfgs.append(cfgfile)
|
|
|
|
class BaseBoard(object):
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
# NOTE: Variables starting with '_' will not be exported to Platform.dsc
|
|
|
|
# Define default private key (PEM format) used to sign configuration data, firmware capsule and IAS image
|
|
key_dir = os.path.join('BootloaderCorePkg', 'Tools', 'Keys')
|
|
# Allow master key to be able to sign everything by default
|
|
self._MASTER_KEY_USAGE = HASH_USAGE['PUBKEY_CFG_DATA'] | HASH_USAGE['PUBKEY_FWU'] | HASH_USAGE['PUBKEY_OS'] | \
|
|
HASH_USAGE['PUBKEY_CONT_DEF']
|
|
self._MASTER_PRIVATE_KEY = os.path.join(key_dir, 'TestSigningPrivateKey.pem')
|
|
self._CFGDATA_PRIVATE_KEY = os.path.join(key_dir, 'TestSigningPrivateKey.pem')
|
|
self._CONTAINER_PRIVATE_KEY = os.path.join(key_dir, 'TestSigningPrivateKey.pem')
|
|
self.LOGO_FILE = 'Platform/CommonBoardPkg/Logo/Logo.bmp'
|
|
|
|
self._RSA_SIGN_TYPE = 'RSA2048'
|
|
self._SIGN_HASH = 'SHA2_256'
|
|
self.SIGN_HASH_TYPE = HASH_TYPE_VALUE[self._SIGN_HASH]
|
|
self.VERINFO_IMAGE_ID = 'SB_???? '
|
|
self.VERINFO_PROJ_ID = 1
|
|
self.VERINFO_CORE_MAJOR_VER = 0
|
|
self.VERINFO_CORE_MINOR_VER = 5
|
|
|
|
self.VERINFO_PROJ_MAJOR_VER = 0
|
|
self.VERINFO_PROJ_MINOR_VER = 1
|
|
self.VERINFO_SVN = 1
|
|
self.VERINFO_BUILD_DATE = '01/01/2018'
|
|
|
|
self.LOWEST_SUPPORTED_FW_VER = 1
|
|
|
|
self.FLASH_BLOCK_SIZE = 0x1000
|
|
self.FLASH_LAYOUT_START = 0x100000000
|
|
self.FLASH_BASE = 0
|
|
self.FLASH_SIZE = 0
|
|
|
|
self.PCI_EXPRESS_BASE = 0xE0000000
|
|
self.ACPI_PM_TIMER_BASE = 0x0408
|
|
self.USB_KB_POLLING_TIMEOUT = 1
|
|
|
|
self.VERIFIED_BOOT_HASH_MASK = 0x00000000
|
|
self.BOOT_MEDIA_SUPPORT_MASK = 0xFFFFFFFF
|
|
self.FILE_SYSTEM_SUPPORT_MASK = 0x00000003
|
|
self.DEBUG_OUTPUT_DEVICE_MASK = 0x00000003
|
|
self.DEBUG_PORT_NUMBER = 0x00000002
|
|
self.CONSOLE_IN_DEVICE_MASK = 0x00000001
|
|
self.CONSOLE_OUT_DEVICE_MASK = 0x00000001
|
|
|
|
self.HAVE_VBT_BIN = 0
|
|
self.HAVE_FIT_TABLE = 0
|
|
self.HAVE_VERIFIED_BOOT = 0
|
|
self.HAVE_MEASURED_BOOT = 0
|
|
self.HAVE_FSP_BIN = 1
|
|
self.HAVE_ACPI_TABLE = 1
|
|
self.HAVE_PSD_TABLE = 0
|
|
self.HAVE_SEED_LIST = 0
|
|
|
|
self.FIT_ENTRY_MAX_NUM = 10
|
|
|
|
self.ENABLE_PCI_ENUM = 1
|
|
self.ENABLE_SMP_INIT = 1
|
|
self.ENABLE_FSP_LOAD_IMAGE = 0
|
|
self.ENABLE_SPLASH = 0
|
|
self.ENABLE_FRAMEBUFFER_INIT = 0
|
|
self.ENABLE_PRE_OS_CHECKER = 0
|
|
self.ENABLE_CRYPTO_SHA_OPT = IPP_CRYPTO_OPTIMIZATION_MASK['SHA256_V8']
|
|
self.ENABLE_FWU = 0
|
|
self.ENABLE_SOURCE_DEBUG = 0
|
|
self.ENABLE_SMM_REBASE = 0
|
|
self.ENABLE_GRUB_CONFIG = 0
|
|
self.ENABLE_SMBIOS = 0
|
|
self.ENABLE_LINUX_PAYLOAD = 0
|
|
self.ENABLE_CONTAINER_BOOT = 1
|
|
self.ENABLE_CSME_UPDATE = 0
|
|
self.ENABLE_EMMC_HS400 = 1
|
|
self.ENABLE_DMA_PROTECTION = 0
|
|
|
|
self.BUILD_CSME_UPDATE_DRIVER = 0
|
|
|
|
self.CPU_MAX_LOGICAL_PROCESSOR_NUMBER = 16
|
|
|
|
self.ACM_SIZE = 0
|
|
self.ACM3_SIZE = 0
|
|
self.UCODE_SIZE = 0
|
|
self.CFGDATA_SIZE = 0
|
|
self.MRCDATA_SIZE = 0
|
|
self.VARIABLE_SIZE = 0
|
|
self.UEFI_VARIABLE_SIZE = 0
|
|
self.FWUPDATE_SIZE = 0
|
|
|
|
self.SPI_IAS1_SIZE = 0
|
|
self.SPI_IAS2_SIZE = 0
|
|
|
|
self.KM_SIZE = 0x1000 # valid only if ACM_SIZE > 0
|
|
self.BPM_SIZE = 0x1000 # valid only if ACM_SIZE > 0
|
|
self.CFG_DATABASE_SIZE = 0
|
|
|
|
self.STAGE1A_XIP = 1
|
|
self.STAGE1B_XIP = 1
|
|
self.STAGE1_STACK_BASE_OFFSET = 0x00000000
|
|
self.STAGE2_XIP = 0
|
|
self.STAGE2_LOAD_HIGH = 1
|
|
self.PAYLOAD_LOAD_HIGH = 1
|
|
self.PAYLOAD_EXE_BASE = 0x00800000
|
|
|
|
# 0: Direct access from flash
|
|
# other: Load image into memory address
|
|
self.PAYLOAD_LOAD_BASE = 0
|
|
self.FWUPDATE_LOAD_BASE = 0
|
|
|
|
# OS Loader FD/FV sizes
|
|
self.OS_LOADER_FD_SIZE = 0x00042000
|
|
self.OS_LOADER_FD_NUMBLK = self.OS_LOADER_FD_SIZE // self.FLASH_BLOCK_SIZE
|
|
|
|
self.PLD_HEAP_SIZE = 0x02000000
|
|
self.PLD_STACK_SIZE = 0x00010000
|
|
self.PLD_RSVD_MEM_SIZE = 0x00004000
|
|
|
|
# These memory sizes need to be page aligned
|
|
self.LOADER_RSVD_MEM_SIZE = 0x0038C000
|
|
self.LOADER_ACPI_NVS_MEM_SIZE = 0x00008000
|
|
self.LOADER_ACPI_RECLAIM_MEM_SIZE = 0x00068000
|
|
|
|
self.CFGDATA_REGION_TYPE = FLASH_REGION_TYPE.BIOS
|
|
|
|
self.RELEASE_MODE = 0
|
|
self.NO_OPT_MODE = 0
|
|
self.FSPDEBUG_MODE = 0
|
|
self.MIN_FSP_REVISION = 0
|
|
self.FSP_IMAGE_ID = ''
|
|
|
|
self.TOP_SWAP_SIZE = 0
|
|
self.REDUNDANT_SIZE = 0
|
|
|
|
self._PAYLOAD_NAME = ''
|
|
self._FSP_PATH_NAME = ''
|
|
|
|
self._PLATFORM_ID = None
|
|
self._MULTI_VBT_FILE = {}
|
|
self._CFGDATA_INT_FILE = []
|
|
self._CFGDATA_EXT_FILE = []
|
|
|
|
self.IPP_HASH_LIB_SUPPORTED_MASK = IPP_CRYPTO_ALG_MASK[self._SIGN_HASH]
|
|
|
|
self.HASH_STORE_SIZE = 0x200 #Hash store size to be allocated in bootloader
|
|
|
|
self.PCI_MEM64_BASE = 0
|
|
|
|
for key, value in list(kwargs.items()):
|
|
setattr(self, '%s' % key, value)
|
|
|
|
|
|
class Build(object):
|
|
|
|
def __init__(self, board):
|
|
self._toolchain = os.environ['TOOL_CHAIN']
|
|
self._workspace = os.environ['WORKSPACE']
|
|
self._board = board
|
|
self._image = "SlimBootloader.bin"
|
|
self._target = 'RELEASE' if board.RELEASE_MODE else 'NOOPT' if board.NO_OPT_MODE else 'DEBUG'
|
|
self._fsp_basename = 'FspDbg' if board.FSPDEBUG_MODE else 'FspRel'
|
|
self._fv_dir = os.path.join(self._workspace, 'Build', 'BootloaderCorePkg', '%s_%s' % (self._target, self._toolchain), 'FV')
|
|
self._key_dir = os.path.join('BootloaderCorePkg', 'Tools', 'Keys')
|
|
self._img_list = board.GetImageLayout()
|
|
self._pld_list = get_payload_list (board._PAYLOAD_NAME.split(';'))
|
|
self._comp_list = []
|
|
self._region_list = []
|
|
|
|
def update_fit_table (self):
|
|
|
|
if not self._board.HAVE_FIT_TABLE:
|
|
return
|
|
|
|
print('Updating FIT')
|
|
|
|
img_file = os.path.join (self._fv_dir, self._image)
|
|
fi = open(img_file,'rb')
|
|
rom = bytearray(fi.read())
|
|
fi.close()
|
|
|
|
# Find FIT pointer @ 0xFFFFFFC0
|
|
fit_address = c_uint32.from_buffer(rom, len(rom) + FIT_ENTRY.FIT_OFFSET)
|
|
print(' FIT Address: 0x%08X' % fit_address.value)
|
|
|
|
if self._board.ACM_SIZE > 0:
|
|
# Check FIT address alignment for 64 bytes if ACM is used
|
|
# because BIOS IBB segments base/size require 64 bytes alignment.
|
|
if fit_address.value & ~0x3F != fit_address.value:
|
|
raise Exception (' FIT address (0x%08X) is not 64-byte aligned' % fit_address.value)
|
|
|
|
# Check FIT address range
|
|
base = 0x100000000 - len(rom);
|
|
if (fit_address.value < base) or (fit_address.value > (base + len(rom))):
|
|
raise Exception(' FIT address (0x%08X) out of range' % fit_address.value)
|
|
|
|
# Check FIT signature
|
|
fit_offset = fit_address.value - base
|
|
fit_header = FIT_ENTRY.from_buffer(rom, fit_offset)
|
|
if fit_header.address != bytes_to_value (bytearray(FIT_ENTRY.FIT_SIGNATURE)):
|
|
raise Exception(' FIT signature not found')
|
|
|
|
num_fit_entries = 0
|
|
if self._board.UCODE_SIZE > 0:
|
|
ucode_base = self._board.UCODE_BASE
|
|
ucode_offset = ucode_base - base;
|
|
if (ucode_offset < 0):
|
|
raise Exception (' UCODE %x\n UCODE address (0x%08X) out of range' % (base, ucode_base))
|
|
|
|
# Collect all CPU uCode images
|
|
u_code_images = []
|
|
while ucode_offset < len(rom):
|
|
ucode_hdr = UCODE_HEADER.from_buffer(rom, ucode_offset)
|
|
if ucode_hdr.header_version == 1:
|
|
if ucode_hdr.total_size:
|
|
ucode_size = ucode_hdr.total_size
|
|
else:
|
|
ucode_size = 0x0800
|
|
u_code_images.append((ucode_offset, ucode_size))
|
|
ucode_offset += ucode_size
|
|
num_fit_entries += 1
|
|
else:
|
|
break
|
|
|
|
# Patch FIT with addresses of uCode images
|
|
for i in range(0, num_fit_entries):
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (i+1)*16)
|
|
# uCode Update
|
|
if len(u_code_images) > 0:
|
|
offset, size = u_code_images.pop(0)
|
|
fit_entry.set_values(base + offset, 0, 0x100, 0x1, 0)
|
|
print (' Patching entry %d with 0x%08X - uCode' % (i, fit_entry.address))
|
|
else:
|
|
print (' Nullifying unused uCode patch entry %d' % i)
|
|
fit_entry.type = 0x7f
|
|
|
|
if len(u_code_images) > 0:
|
|
raise Exception(' Insufficient uCode entries in FIT. Need %d more.' % len(u_code_images))
|
|
|
|
# ACM
|
|
if self._board.ACM_SIZE > 0:
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (num_fit_entries+1)*16)
|
|
fit_entry.set_values(self._board.ACM_BASE, 0, 0x100, 0x2, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - ACM' % (num_fit_entries, fit_entry.address, fit_entry.size))
|
|
num_fit_entries += 1
|
|
|
|
# ACM3 Fit entry should be in sequential order with/without BTG enabled
|
|
# Save the next FIT entry for ACM3 here and set it later below
|
|
if self._board.ACM3_SIZE > 0:
|
|
acm3_index = num_fit_entries
|
|
num_fit_entries += 1
|
|
|
|
# BIOS Module (IBB segment 0): from FIT table end to 4GB
|
|
# Record it now and update later since the FIT size is unknown yet
|
|
patch_entry = num_fit_entries
|
|
num_fit_entries += 1
|
|
|
|
# BIOS Module (IBB segment 1): from Stage1A base to FIT table start
|
|
addr = self._board.STAGE1A_BASE
|
|
module_size = (fit_address.value - addr) >> 4
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (num_fit_entries+1)*16)
|
|
fit_entry.set_values(addr, module_size, 0x100, 0x7, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - BIOS Module(Stage1A base to FIT table start)' % (num_fit_entries, fit_entry.address, fit_entry.size))
|
|
num_fit_entries += 1
|
|
|
|
# BIOS Module (IBB segment 2): full Stage1B
|
|
addr = self._board.STAGE1B_BASE
|
|
module_size = self._board.STAGE1B_SIZE >> 4
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (num_fit_entries+1)*16)
|
|
fit_entry.set_values(addr, module_size, 0x100, 0x7, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - BIOS Module(Stage1B)' % (num_fit_entries, fit_entry.address, fit_entry.size))
|
|
num_fit_entries += 1
|
|
|
|
# KM
|
|
addr = self._board.ACM_BASE + self._board.ACM_SIZE - (self._board.KM_SIZE + self._board.BPM_SIZE)
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (num_fit_entries+1)*16)
|
|
fit_entry.set_values(addr, self._board.KM_SIZE, 0x100, 0xb, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - KM' % (num_fit_entries, fit_entry.address, fit_entry.size))
|
|
num_fit_entries += 1
|
|
|
|
# BPM
|
|
addr = self._board.ACM_BASE + self._board.ACM_SIZE - self._board.BPM_SIZE
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (num_fit_entries+1)*16)
|
|
fit_entry.set_values(addr, self._board.BPM_SIZE, 0x100, 0xc, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - BPM' % (num_fit_entries, fit_entry.address, fit_entry.size))
|
|
num_fit_entries += 1
|
|
|
|
# Patch the entry 'FIT table end to 4GB' since FIT table size is known now
|
|
# The size of the FIT table end address needs to be adjusted to align with 64
|
|
# bytes so that IBB segment start address is 64 byte aligned as per required.
|
|
addr = fit_address.value + (num_fit_entries + 1) * 16
|
|
addr = (addr + 0x3F) & 0xFFFFFFC0
|
|
module_size = (0x100000000 - addr) >> 4
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (patch_entry+1)*16)
|
|
fit_entry.set_values(addr, module_size, 0x100, 0x7, 0)
|
|
print (' Patching entry %d with 0x%08X:0x%08X - BIOS Module(FIT table end to 4GB)' % (patch_entry, fit_entry.address, fit_entry.size))
|
|
|
|
else :
|
|
if self._board.ACM3_SIZE > 0:
|
|
acm3_index = num_fit_entries
|
|
num_fit_entries += 1
|
|
|
|
addr = fit_address.value + (num_fit_entries + 1) * 16
|
|
|
|
# Add ACM3 with the reserved fit entry saved
|
|
if self._board.ACM3_SIZE > 0:
|
|
fit_entry = FIT_ENTRY.from_buffer(rom, fit_offset + (acm3_index+1)*16)
|
|
fit_entry.set_values(self._board.ACM3_BASE, self._board.ACM3_SIZE, 0x100, 0x3, 0)
|
|
print(' Patching entry %d with 0x%08X:0x%08X - ACM3' % (acm3_index, fit_entry.address, fit_entry.size))
|
|
|
|
# Check FIT length
|
|
spaceleft = addr - (fit_address.value + fit_header.size)
|
|
if spaceleft > 0:
|
|
raise Exception(' Insufficient FIT entries in FIT table, need %d more entries !' % ((spaceleft + 15) // 16))
|
|
|
|
print (' FIT %d entries added' % num_fit_entries)
|
|
|
|
# Update FIT checksum
|
|
print(' Updating Checksum')
|
|
fit_header.size = num_fit_entries + 1
|
|
fit_header.type = 0x80 # Valid checksum
|
|
fit_header.version = 0x0100
|
|
fit_header.checksum = 0
|
|
fit_sum = sum(rom[fit_offset:fit_offset+fit_header.size*16])
|
|
fit_header.checksum = (0 - fit_sum) & 0xff
|
|
fit_data = rom[fit_offset:fit_offset+fit_header.size*16]
|
|
|
|
fo = open(img_file,'r+b')
|
|
|
|
fo.seek(fit_offset)
|
|
fo.write(fit_data)
|
|
|
|
if self._board.REDUNDANT_SIZE != 0:
|
|
# Update FIT table in STAGE1A_B
|
|
print('Updating FIT in STAGE1A_B')
|
|
fit_offset -= self._board.TOP_SWAP_SIZE
|
|
rom[fit_offset:fit_offset+fit_header.size*16] = fit_data
|
|
|
|
# Update components base in Fit table.
|
|
fit_data = rom[fit_offset:fit_offset+fit_header.size*16]
|
|
for i in range(0, num_fit_entries):
|
|
fit_entry = FIT_ENTRY.from_buffer(fit_data, (i+1)*16)
|
|
if (0x100000000 - fit_entry.address) > self._board.TOP_SWAP_SIZE * 2:
|
|
fit_entry.address -= self._board.REDUNDANT_SIZE
|
|
print(' Patching entry %d from 0x%08X with 0x%08X size:0x%08X ' %
|
|
(i, fit_entry.address + self._board.REDUNDANT_SIZE, fit_entry.address, fit_entry.size))
|
|
fit_header = FIT_ENTRY.from_buffer(fit_data)
|
|
fit_header.checksum = 0
|
|
fit_sum = sum(fit_data)
|
|
fit_header.checksum = (0 - fit_sum) & 0xff
|
|
fo.seek(fit_offset)
|
|
fo.write(fit_data)
|
|
|
|
fo.close()
|
|
|
|
|
|
def update_hash_table (self, img_file):
|
|
|
|
if not self._board.HAVE_VERIFIED_BOOT:
|
|
return
|
|
|
|
print('Updating HashStore %s' % os.path.basename (img_file))
|
|
|
|
fi = open(img_file,'rb')
|
|
stage1_bins = bytearray(fi.read())
|
|
fi.close()
|
|
|
|
hs_offset = stage1_bins.find (HashStoreTable.HASH_STORE_SIGNATURE)
|
|
if hs_offset < 0:
|
|
raise Exceptoin ("HashStoreTable not found in '%s'!" % os.path.basename(img_file))
|
|
|
|
comp_name, part_name = get_redundant_info (img_file)
|
|
if part_name:
|
|
part_name = '_' + part_name
|
|
|
|
hash_file_list = [
|
|
('STAGE1B%s.hash' % part_name, HASH_USAGE['STAGE_1B']),
|
|
('STAGE2.hash', HASH_USAGE['STAGE_2']),
|
|
('PAYLOAD.hash', HASH_USAGE['PAYLOAD'])
|
|
]
|
|
if self._board.ENABLE_FWU:
|
|
hash_file_list.append (('FWUPDATE.hash', HASH_USAGE['PAYLOAD_FWU']))
|
|
|
|
hash_file_list.append (('MSTKEY.hash', HASH_USAGE['PUBKEY_MASTER'] | self._board._MASTER_KEY_USAGE))
|
|
|
|
if len(hash_file_list) > HashStoreTable.HASH_STORE_MAX_IDX_NUM:
|
|
raise Exception ('Insufficant hash entries !')
|
|
|
|
hash_idx = 0
|
|
hash_store = HashStoreTable.from_buffer(stage1_bins, hs_offset)
|
|
hash_len = HASH_DIGEST_SIZE[HASH_VAL_STRING[self._board.SIGN_HASH_TYPE]]
|
|
hash_store_data_buf = bytearray()
|
|
hash_store.UsedLength = sizeof(HashStoreTable())
|
|
for hash_file, usage in hash_file_list:
|
|
# If the hash verification is not required for certain stage, skip it
|
|
if hash_file == 'PLDDYN':
|
|
hash_data = bytearray(b'\x00' * hash_len)
|
|
else:
|
|
src_path = os.path.join(self._fv_dir, hash_file)
|
|
if not os.path.exists(src_path):
|
|
raise Exception ("Hash data file '%s' not found !" % hash_file )
|
|
fh = open(src_path,'rb')
|
|
hash_data = bytearray(fh.read())
|
|
fh.close()
|
|
if hash_len != len (hash_data):
|
|
raise Exception ("Hash data file '%s' length is incorrect !" % hash_file )
|
|
|
|
# update hash data
|
|
hashstoredata = HashStoreData()
|
|
hashstoredata.Usage = usage
|
|
hashstoredata.HashAlg = self._board.SIGN_HASH_TYPE
|
|
hashstoredata.DigestLen = hash_len
|
|
|
|
hash_store.UsedLength += hash_len + sizeof(HashStoreData())
|
|
|
|
#Append hash store data entries
|
|
hash_store_data_buf = hash_store_data_buf + bytearray(hashstoredata) + hash_data
|
|
print(' Update HashStore entry %d with file %s' % (hash_idx, hash_file))
|
|
hash_idx += 1
|
|
|
|
#Update Hash store Table
|
|
fo = open(img_file,'r+b')
|
|
fo.seek(hs_offset)
|
|
fo.write(hash_store)
|
|
#Update Hash store data
|
|
fo.seek(hs_offset + sizeof(hash_store))
|
|
fo.write(hash_store_data_buf)
|
|
fo.close()
|
|
|
|
|
|
def update_component_list (self):
|
|
|
|
def process_image_list (idx, offset):
|
|
|
|
region_name, part_name = get_redundant_info (img_list[idx][0])
|
|
redundant = True if part_name == 'B' else False
|
|
|
|
flags = 0
|
|
if redundant:
|
|
flags |= FLASH_MAP.FLASH_MAP_DESC_FLAGS['BACKUP']
|
|
if region_name in ['TOP_SWAP', 'REDUNDANT', 'NON_REDUNDANT', 'NON_VOLATILE']:
|
|
flags |= FLASH_MAP.FLASH_MAP_DESC_FLAGS[region_name]
|
|
|
|
oldidx = len (comp_list)
|
|
parent_size = getattr(self._board, '%s_SIZE' % region_name, 0)
|
|
remaining_size = parent_size
|
|
for comp in img_list[idx][1]:
|
|
if comp[3] & STITCH_OPS.MODE_FILE_IGNOR:
|
|
continue
|
|
compress = FLASH_MAP.FLASH_MAP_DESC_FLAGS['COMPRESSED'] if comp[1] else 0
|
|
if comp[0] in region_name_list:
|
|
idx = region_name_list.index (comp[0])
|
|
region_size = process_image_list (idx, offset)
|
|
region_list.append ({'name':comp[0], 'offset':offset, 'size':region_size})
|
|
offset += region_size
|
|
else:
|
|
comp_list.append ({'name':comp[0], 'size':comp[2], 'flag':flags | compress})
|
|
remaining_size -= comp[2]
|
|
|
|
if remaining_size > 0:
|
|
comp_node = find_component_in_image_list (img_list[idx][0], img_list)
|
|
pos = STITCH_OPS.MODE_POS_HEAD if comp_node is None else comp_node[4]
|
|
comp = {'name':'EMPTY.bin', 'size':remaining_size, 'flag':flags}
|
|
if pos == STITCH_OPS.MODE_POS_HEAD:
|
|
comp_list.insert (oldidx, comp)
|
|
else:
|
|
comp_list.append (comp)
|
|
elif remaining_size < 0:
|
|
if parent_size == 0:
|
|
parent_size = -remaining_size
|
|
else:
|
|
raise Exception ('Insufficant space, please adjust %s_SIZE (0x%X more is requried) !' % (region_name, -remaining_size))
|
|
|
|
return parent_size
|
|
|
|
# Create compoent list and update base and offset
|
|
img_list = self._img_list
|
|
region_name_list = [img[0] for img in img_list]
|
|
comp_list = []
|
|
region_list = []
|
|
|
|
try:
|
|
master_name = self._image
|
|
master_idx = region_name_list.index(master_name)
|
|
process_image_list (master_idx, 0)
|
|
image_size = sum (comp['size'] for comp in comp_list)
|
|
image_base = self._board.FLASH_LAYOUT_START - image_size
|
|
image_offs = 0
|
|
for comp in comp_list:
|
|
comp['bname'] = get_redundant_info (comp['name'])[0]
|
|
comp['offset'] = image_offs
|
|
comp['base'] = image_base + image_offs
|
|
image_offs += comp['size']
|
|
|
|
for rgn in region_list:
|
|
rgn['base'] = image_base + rgn['offset']
|
|
|
|
except ValueError:
|
|
print("Warning: No '%s' component in image list !" % master_name)
|
|
|
|
#print_component_list (comp_list)
|
|
|
|
self._comp_list = comp_list
|
|
self._region_list = region_list
|
|
|
|
|
|
def patch_stages (self):
|
|
|
|
print('Patching STAGE1A')
|
|
extra_cmd = [
|
|
"STAGE1A:STAGE1A",
|
|
"0xFFFFFFFC, _BASE_STAGE1A_, @Patch BFV",
|
|
"_OFFS_STAGE1A_, Stage1A:__ModuleEntryPoint, @Patch Stage1A Entry",
|
|
"_OFFS_STAGE1A_+4, Stage1A:BASE, @Patch Module Base",
|
|
"<Stage1A:__gPcd_BinaryPatch_PcdVerInfoBase>, {3473A022-C3C2-4964-B309-22B3DFB0B6CA:0x1C}, @Patch VerInfo",
|
|
"<Stage1A:__gPcd_BinaryPatch_PcdFileDataBase>, {EFAC3859-B680-4232-A159-F886F2AE0B83:0x1C}, @Patch PcdBase"
|
|
]
|
|
|
|
extra_cmd.append (
|
|
"0xFFFFFFF8, {3CEA8EF3-95FC-476F-ABA5-7EC5DFA1D77B:0x1C}, @Patch FlashMap",
|
|
)
|
|
|
|
if self._board.HAVE_FIT_TABLE:
|
|
if self._board.ACM_SIZE > 0:
|
|
extra_cmd.append (
|
|
"0xFFFFFFC0, ({CD17FF5E-7731-4D16-8441-FC7A113C392F:0x1C} + 0x3F) & ~0x3F, @FIT table"
|
|
)
|
|
else:
|
|
extra_cmd.append (
|
|
"0xFFFFFFC0, {CD17FF5E-7731-4D16-8441-FC7A113C392F:0x1C}, @FIT table"
|
|
)
|
|
extra_cmd.extend ([
|
|
"<[0xFFFFFFC0]>+0, 0x5449465F, @FIT Signature Low" ,
|
|
"<[0xFFFFFFC0]>+4, 0x2020205F, @FIT Signature High",
|
|
"<[0xFFFFFFC0]>+8, [CD17FF5E-7731-4D16-8441-FC7A113C392F:0x18] & 0xFFFFFF , @FIT FFS section length",
|
|
"<[0xFFFFFFC0]>+8, {CD17FF5E-7731-4D16-8441-FC7A113C392F:0x1C} + [[0xFFFFFFC0] + 8] - [0xFFFFFFC0], @FIT table max length",
|
|
])
|
|
if self._board.HAVE_VERIFIED_BOOT:
|
|
extra_cmd.append (
|
|
"<Stage1A:__gPcd_BinaryPatch_PcdHashStoreBase>, {18EDB1DF-1DBE-4EC5-8E26-C44808B546E1:0x1C}, @Patch HashStore",
|
|
)
|
|
patch_fv(self._fv_dir, *extra_cmd)
|
|
|
|
print('Patching STAGE1B')
|
|
patch_fv(
|
|
self._fv_dir,
|
|
"STAGE1B:STAGE1B",
|
|
"_OFFS_STAGE1B_, Stage1B:__ModuleEntryPoint, @Patch Stage1B Entry",
|
|
"_OFFS_STAGE1B_+4, Stage1B:BASE, @Patch Stage1B Base",
|
|
"<Stage1B:__gPcd_BinaryPatch_PcdCfgDataIntBase>, {016E6CD0-4834-4C7E-BCFE-41DFB88A6A6D:0x1C}, @Patch Internal CfgDataBase"
|
|
)
|
|
|
|
print('Patching STAGE2')
|
|
extra_cmd = []
|
|
if self._board.HAVE_VBT_BIN:
|
|
extra_cmd.append (
|
|
"<Stage2:__gPcd_BinaryPatch_PcdGraphicsVbtAddress>, {E08CA6D5-8D02-43AE-ABB1-952CC787C933:0x1C}, @Patch VBT"
|
|
)
|
|
if self._board.HAVE_ACPI_TABLE:
|
|
extra_cmd.append (
|
|
"<Stage2:__gPcd_BinaryPatch_PcdAcpiTablesAddress>, {7E374E25-8E01-4FEE-87F2-390C23C606CD:0x1C}, @Patch ACPI",
|
|
)
|
|
if self._board.ENABLE_SPLASH:
|
|
extra_cmd.append (
|
|
"<Stage2:__gPcd_BinaryPatch_PcdSplashLogoAddress>, {5E2D3BE9-AD72-4D1D-AAD5-6B08AF921590:0x1C}, @Patch Logo",
|
|
)
|
|
patch_fv(
|
|
self._fv_dir,
|
|
"STAGE2:STAGE2",
|
|
"_OFFS_STAGE2_, Stage2:__ModuleEntryPoint, @Patch Stage2 Entry",
|
|
"_OFFS_STAGE2_+4, Stage2:BASE, @Patch Stage2 Base",
|
|
*extra_cmd
|
|
)
|
|
|
|
|
|
def create_dsc_inc_file (self, file):
|
|
lines = []
|
|
|
|
lines.append('%s\n' % AUTO_GEN_DSC_HDR)
|
|
lines.append('# Platform specific macro definitions\n')
|
|
lines.append('[Defines]\n')
|
|
|
|
for attr in sorted(vars(self._board)):
|
|
if attr.startswith('_'):
|
|
continue
|
|
value = getattr(self._board, attr)
|
|
if type(value) is not str:
|
|
if value == 0 or value == 1:
|
|
value = '0x%x' % value
|
|
else:
|
|
value = '0x%08X' % value
|
|
lines.append(' DEFINE %-24s = %s\n' % (attr, value))
|
|
|
|
if getattr(self._board, "GetDscLibrarys", None):
|
|
libsdict = self._board.GetDscLibrarys()
|
|
for arch in libsdict:
|
|
lines.append('\n# Platform specific libraries\n')
|
|
lines.append('[LibraryClasses.%s]\n' % arch)
|
|
for lib in libsdict[arch]:
|
|
lines.append(' %s\n' % lib)
|
|
lines.append('\n')
|
|
|
|
update = True
|
|
text = ''.join(lines)
|
|
if os.path.exists(file):
|
|
old_text = get_file_data (file, 'r')
|
|
if text == old_text:
|
|
update = False
|
|
|
|
if update:
|
|
open (file, 'w').write(text)
|
|
|
|
|
|
def create_platform_vars (self):
|
|
for comp in self._comp_list:
|
|
if comp['flag'] & FLASH_MAP.FLASH_MAP_DESC_FLAGS['BACKUP'] or comp['bname'] == 'EMPTY':
|
|
continue
|
|
setattr(self._board, '%s_BASE' % comp['bname'], comp['base'])
|
|
|
|
image_base = self._board.FLASH_LAYOUT_START
|
|
for idx, comp_name in enumerate(['STAGE1A', 'STAGE1B', 'STAGE2']):
|
|
if not hasattr(self._board, '%s_BASE' % comp_name):
|
|
image_base -= getattr(self._board, '%s_SIZE' % comp_name)
|
|
if idx > 0:
|
|
image_base &= ~0xFFFFF
|
|
setattr(self._board, '%s_BASE' % comp_name, image_base)
|
|
|
|
if getattr(self._board, '%s_XIP' % comp_name) or comp_name == 'STAGE1A':
|
|
setattr(self._board, '%s_FD_SIZE' % comp_name, getattr(self._board, '%s_SIZE' % comp_name))
|
|
setattr(self._board, '%s_FD_BASE' % comp_name, getattr(self._board, '%s_BASE' % comp_name))
|
|
|
|
if getattr(self._board, '%s_XIP' % comp_name):
|
|
setattr(self._board, '%s_LOAD_BASE' % comp_name, getattr(self._board, '%s_BASE' % comp_name))
|
|
else:
|
|
var_name = '%s_LOAD_BASE' % comp_name
|
|
if not hasattr(self._board, var_name):
|
|
setattr(self._board, var_name, getattr(self._board, '%s_BASE' % comp_name))
|
|
for var in ['%s_FD_SIZE', '%s_FD_BASE', '%s_LOAD_BASE']:
|
|
var_name = var % comp_name
|
|
if not hasattr(self._board, var_name):
|
|
raise Exception ('%s needs to be defined' % var_name)
|
|
|
|
fd_size = getattr(self._board, '%s_FD_SIZE' % comp_name)
|
|
setattr(self._board, '%s_FD_NUMBLK' % comp_name, fd_size // self._board.FLASH_BLOCK_SIZE)
|
|
|
|
pld_list = ['PAYLOAD']
|
|
if self._board.ENABLE_FWU:
|
|
pld_list.append ('FWUPDATE')
|
|
for pld in pld_list:
|
|
if not hasattr(self._board, '%s_LOAD_BASE' % pld):
|
|
if not hasattr(self._board, '%s_BASE' % pld):
|
|
raise Exception ('%s_BASE or %s_LOAD_BASE needs to be defined !' % (pld, pld))
|
|
setattr(self._board, '%s_LOAD_BASE' % pld, getattr(self._board, '%s_BASE' % pld))
|
|
|
|
setattr(self._board, 'FSP_T_OFFSET' , 0)
|
|
setattr(self._board, 'STAGE1A_FV_OFFSET' , getattr(self._board, 'FSP_T_OFFSET') + getattr(self._board, 'FSP_T_SIZE'))
|
|
setattr(self._board, 'STAGE1A_FV_SIZE' , getattr(self._board, 'STAGE1A_FD_SIZE') - getattr(self._board, 'FSP_T_SIZE'))
|
|
setattr(self._board, 'STAGE1B_FV_OFFSET' , 0)
|
|
setattr(self._board, 'STAGE1B_FV_SIZE' , getattr(self._board, 'STAGE1B_FD_SIZE') - getattr(self._board, 'FSP_M_SIZE'))
|
|
setattr(self._board, 'STAGE2_FV_OFFSET' , 0)
|
|
setattr(self._board, 'STAGE2_FV_SIZE' , getattr(self._board, 'STAGE2_FD_SIZE') - getattr(self._board, 'FSP_S_SIZE'))
|
|
setattr(self._board, 'FSP_S_OFFSET' , getattr(self._board, 'STAGE2_FV_OFFSET') + getattr(self._board, 'STAGE2_FV_SIZE'))
|
|
setattr(self._board, 'FSP_M_OFFSET' , getattr(self._board, 'STAGE1B_FV_OFFSET') + getattr(self._board, 'STAGE1B_FV_SIZE'))
|
|
setattr(self._board, 'FSP_T_BASE' , getattr(self._board, 'STAGE1A_FD_BASE') + getattr(self._board, 'FSP_T_OFFSET'))
|
|
setattr(self._board, 'FSP_M_BASE' , getattr(self._board, 'STAGE1B_FD_BASE') + getattr(self._board, 'FSP_M_OFFSET'))
|
|
setattr(self._board, 'FSP_S_BASE' , getattr(self._board, 'STAGE2_FD_BASE') + getattr(self._board, 'FSP_S_OFFSET'))
|
|
|
|
if getattr(self._board, 'FLASH_SIZE') == 0:
|
|
if not hasattr (self._board, 'SLIMBOOTLOADER_SIZE'):
|
|
raise Exception ('FLASH_SIZE needs to be defined !')
|
|
else:
|
|
setattr(self._board, 'FLASH_SIZE' , getattr(self._board, 'SLIMBOOTLOADER_SIZE'))
|
|
setattr(self._board, 'FLASH_BASE' , 0x100000000 - getattr(self._board, 'FLASH_SIZE'))
|
|
|
|
if getattr(self._board, 'ACM_SIZE') > 0:
|
|
acm_base = getattr(self._board, 'ACM_BASE')
|
|
if acm_base & 0x7FFF:
|
|
raise Exception ('ACM base[FSP-T+CAR:0x%x] must be 32KB aligned!' % acm_base)
|
|
|
|
if getattr(self._board, 'ACM3_SIZE') > 0:
|
|
acm3_base = getattr(self._board, 'ACM3_BASE')
|
|
if acm3_base & 0x0FFF:
|
|
raise Exception ('ACM3 base[FSP-T+CAR:0x%x] must be 4KB aligned!' % acm3_base)
|
|
|
|
# Generate Pci Enum Policy Info
|
|
pci_enum_policy_list = ['DOWNGRADE_IO32', 'DOWNGRADE_MEM64', 'DOWNGRADE_PMEM64', 'BUS_SCAN_TYPE', 'BUS_SCAN_ITEMS']
|
|
pci_enum_policy_dict = {}
|
|
for policy_list in pci_enum_policy_list:
|
|
policy_name = '_PCI_ENUM_%s' % policy_list
|
|
policy_value = None
|
|
if not hasattr(self._board, policy_name):
|
|
if policy_list == 'BUS_SCAN_TYPE':
|
|
policy_value = 0
|
|
elif policy_list == 'BUS_SCAN_ITEMS':
|
|
policy_value = '0'
|
|
else:
|
|
policy_value = 1
|
|
else:
|
|
policy_value = getattr(self._board, policy_name)
|
|
pci_enum_policy_dict[policy_list] = policy_value
|
|
self._board.PCI_ENUM_POLICY_INFO = gen_pci_enum_policy_info(pci_enum_policy_dict)
|
|
|
|
def create_redundant_components (self):
|
|
if self._board.REDUNDANT_SIZE == 0:
|
|
return
|
|
|
|
print("Generating redundant components")
|
|
|
|
shutil.copy(
|
|
os.path.join(self._fv_dir, 'STAGE1A.fd'),
|
|
os.path.join(self._fv_dir, 'STAGE1A_A.fd'))
|
|
|
|
shutil.copy(
|
|
os.path.join(self._fv_dir, 'STAGE1A.fd'),
|
|
os.path.join(self._fv_dir, 'STAGE1A_B.fd'))
|
|
|
|
# Patch flashmap to indicate boot parititon
|
|
fo = open(os.path.join(self._fv_dir, 'STAGE1A_B.fd'), 'r+b')
|
|
bins = bytearray(fo.read())
|
|
fmapoff = (bytes_to_value(bins[-8:-4]) + len(bins)) & 0xFFFFFFFF
|
|
fmaphdr = FLASH_MAP.from_buffer (bins, fmapoff)
|
|
if fmaphdr.sig != FLASH_MAP.FLASH_MAP_SIGNATURE:
|
|
raise Exception ('Failed to locate flash map in STAGE1A_B.fd !')
|
|
fmaphdr.attributes |= fmaphdr.FLASH_MAP_ATTRIBUTES['BACKUP_REGION']
|
|
fo.seek(fmapoff)
|
|
fo.write(fmaphdr)
|
|
fo.close()
|
|
|
|
# Stage 1B_B will be created during rebasing
|
|
shutil.copy(
|
|
os.path.join(self._fv_dir, 'STAGE1B.fd'),
|
|
os.path.join(self._fv_dir, 'STAGE1B_A.fd'))
|
|
|
|
stage1b_path = os.path.join(self._fv_dir, 'STAGE1B.fd')
|
|
stage1b_b_path = os.path.join(self._fv_dir, 'STAGE1B_B.fd')
|
|
|
|
if self._board.STAGE1B_XIP:
|
|
# Rebase stage1b.fd
|
|
print("Rebasing STAGE1B_B")
|
|
rebase_stage (stage1b_path, stage1b_b_path, -self._board.REDUNDANT_SIZE)
|
|
|
|
# rebase FSPM in Stage1B and update stage1B hash in key store
|
|
if self._board.HAVE_FSP_BIN:
|
|
fsp_path = os.path.join(self._fv_dir, 'Fsp.bin')
|
|
rebase_fsp (fsp_path, self._fv_dir, self._board.FSP_T_BASE, self._board.FSP_M_BASE - self._board.REDUNDANT_SIZE, self._board.FSP_S_BASE)
|
|
split_fsp (fsp_path, self._fv_dir)
|
|
|
|
# write rebased fspm to second firmware
|
|
di = open(os.path.join(self._fv_dir, 'FSP_M.bin'), 'rb').read()
|
|
fo = open(stage1b_b_path,'r+b')
|
|
fo.seek(self._board.FSP_M_OFFSET)
|
|
fo.write(di)
|
|
fo.close()
|
|
else:
|
|
shutil.copy(stage1b_path, stage1b_b_path)
|
|
|
|
|
|
def create_bootloader_image (self, layout_name):
|
|
|
|
layout_file = open(os.path.join(self._fv_dir, layout_name), 'w')
|
|
layout_file.write("BOARD_INFO = ['%s']\n" % self._board.BOARD_NAME)
|
|
|
|
rgn_name_list = [rgn['name'] for rgn in self._region_list]
|
|
|
|
for idx, (comp_name, file_list) in enumerate(self._img_list):
|
|
if (self._board.ENABLE_FWU == 0) and (comp_name == 'Stitch_FWU.bin') :
|
|
print("No firmware update payload specified, skip firmware update.")
|
|
continue
|
|
out_file = comp_name
|
|
out_path = os.path.join(self._fv_dir, out_file)
|
|
bins = bytearray()
|
|
new_list = []
|
|
for src, algo, val, mode, pos in file_list:
|
|
if mode & STITCH_OPS.MODE_FILE_IGNOR:
|
|
continue
|
|
new_list.append((src, algo, val, mode, pos))
|
|
if src == 'EMPTY':
|
|
bins.extend (b'\xff' * val)
|
|
continue
|
|
src_path = os.path.join(self._fv_dir, src)
|
|
bas_path = os.path.splitext(src_path)[0]
|
|
if not os.path.exists(src_path):
|
|
raise Exception ("Component '%s' could not be found !" % src)
|
|
|
|
if algo:
|
|
compress(src_path, algo)
|
|
src_path = bas_path + '.lz'
|
|
else:
|
|
if src == 'STAGE2.fd':
|
|
raise Exception ("STAGE2.fd must be compressed, please change BoardConfig.py file !")
|
|
if src not in rgn_name_list:
|
|
gen_hash_file (src_path, HASH_VAL_STRING[self._board.SIGN_HASH_TYPE], '', False)
|
|
|
|
if mode != STITCH_OPS.MODE_FILE_NOP:
|
|
dst_path = bas_path + '.pad'
|
|
align_pad_file(src_path, dst_path, val, mode, pos)
|
|
src_path = dst_path
|
|
else:
|
|
if val and (val != os.path.getsize(src_path)):
|
|
raise Exception ("Size of file '%s' does not match expected 0x%X !" % (src_path, val))
|
|
if 'STAGE1A' in src :
|
|
self.update_hash_table (src_path)
|
|
|
|
fi = open(src_path, 'rb')
|
|
bins.extend(bytearray(fi.read()))
|
|
fi.close()
|
|
|
|
if comp_name == self._image:
|
|
bins = b'\xff' * (self._board.SLIMBOOTLOADER_SIZE - len(bins)) + bins
|
|
|
|
fo = open(out_path,'wb')
|
|
fo.write(bins)
|
|
fo.close()
|
|
|
|
comp_file = out_file
|
|
comp_node = find_component_in_image_list (comp_name, self._img_list)
|
|
if comp_node:
|
|
space = comp_node[2] - len(bins)
|
|
if space > 0:
|
|
empty = ('EMPTY', '', space, STITCH_OPS.MODE_FILE_NOP, STITCH_OPS.MODE_POS_HEAD)
|
|
if comp_node[4] == STITCH_OPS.MODE_POS_HEAD:
|
|
new_list.insert (0, empty)
|
|
else:
|
|
new_list.append (empty)
|
|
comp_file = os.path.splitext(comp_name)[0] + '.pad'
|
|
|
|
image_base = self._board.FLASH_LAYOUT_START
|
|
comp_name = comp_name.replace ('TOP_SWAP_B.', 'TOP_SWAP_A.')
|
|
|
|
if comp_name in rgn_name_list:
|
|
idx = rgn_name_list.index(comp_name)
|
|
image_base = self._region_list[idx]['base'] + self._region_list[idx]['size']
|
|
|
|
layout_file.write("IMAGE_INFO = ['%s', 0x%X, %d]\n" % (comp_file, image_base, True))
|
|
layout_file.write("IMAGE_LIST = %s\n" % new_list)
|
|
|
|
layout_file.close()
|
|
|
|
self.update_fit_table ()
|
|
|
|
# generate flash layout file
|
|
layout_file = os.path.splitext(self._image)[0] + '.txt'
|
|
report_image_layout (self._fv_dir, layout_name, layout_file)
|
|
|
|
# copy files to staging directory for stitching
|
|
if getattr(self._board, "GetOutputImages", None):
|
|
extra_list = self._board.GetOutputImages()
|
|
else:
|
|
extra_list = []
|
|
out_file = os.path.join("Outputs", self._board.BOARD_NAME, 'Stitch_Components.zip')
|
|
copy_images_to_output (self._fv_dir, out_file, self._img_list, rgn_name_list, extra_list)
|
|
|
|
|
|
def pre_build(self):
|
|
# Check if BaseTools has been compiled
|
|
rebuild_basetools ()
|
|
|
|
# Update search path
|
|
sbl_dir = os.environ['SBL_SOURCE']
|
|
plt_dir = os.environ['PLT_SOURCE']
|
|
os.environ['PACKAGES_PATH'] = plt_dir
|
|
if plt_dir != sbl_dir:
|
|
os.environ['PACKAGES_PATH'] += os.pathsep + sbl_dir
|
|
|
|
# create conf and build folder if not exist
|
|
if not os.path.exists(os.path.join(self._workspace, 'Conf')):
|
|
os.makedirs(os.path.join(self._workspace, 'Conf'))
|
|
for name in ['target', 'tools_def', 'build_rule']:
|
|
txt_file = os.path.join(self._workspace, 'Conf/%s.txt' % name)
|
|
if not os.path.exists(txt_file):
|
|
shutil.copy (
|
|
os.path.join(sbl_dir, 'BaseTools/Conf/%s.template' % name),
|
|
os.path.join(self._workspace, 'Conf/%s.txt' % name))
|
|
|
|
if not os.path.exists(self._fv_dir):
|
|
os.makedirs(self._fv_dir)
|
|
|
|
# check if FSP binary exists
|
|
fsp_dir = os.path.join(plt_dir, 'Silicon', self._board.SILICON_PKG_NAME, "FspBin", self._board._FSP_PATH_NAME)
|
|
work_dir = plt_dir
|
|
if not os.path.exists(fsp_dir):
|
|
fsp_dir = os.path.join(sbl_dir, 'Silicon', self._board.SILICON_PKG_NAME, "FspBin", self._board._FSP_PATH_NAME)
|
|
work_dir = sbl_dir
|
|
fsp_path = os.path.join(fsp_dir, self._fsp_basename + '.bin')
|
|
|
|
check_build_component_bin = os.path.join(tool_dir, 'PrepareBuildComponentBin.py')
|
|
if os.path.exists(check_build_component_bin):
|
|
ret = subprocess.call([sys.executable, check_build_component_bin, work_dir, self._board.SILICON_PKG_NAME, '/d' if self._board.FSPDEBUG_MODE else '/r'])
|
|
if ret:
|
|
raise Exception ('Failed to prepare build component binaries !')
|
|
|
|
# create FSP size and UPD size can be known
|
|
fsp_list = ['FSP_T', 'FSP_M', 'FSP_S']
|
|
if self._board.HAVE_FSP_BIN:
|
|
split_fsp (fsp_path, self._fv_dir)
|
|
else:
|
|
# create dummy FSP files
|
|
for each in fsp_list:
|
|
open(os.path.join(self._fv_dir, each + '.bin'),'wb').close()
|
|
# generate size variables
|
|
for each in fsp_list:
|
|
fsp_bin = os.path.join(self._fv_dir, "%s.bin" % each)
|
|
if self._board.FSP_IMAGE_ID:
|
|
imageid = get_fsp_image_id (fsp_bin)
|
|
if self._board.FSP_IMAGE_ID != imageid:
|
|
raise Exception ('Expecting FSP ImageId: %s, but got %s !' % (self._board.FSP_IMAGE_ID, imageid))
|
|
revision = get_fsp_revision (fsp_bin)
|
|
if revision < self._board.MIN_FSP_REVISION:
|
|
raise Exception ('Required minimum FSP revision is 0x%08X, but current revision is 0x%08X !' %
|
|
(self._board.MIN_FSP_REVISION, revision))
|
|
setattr(self._board, '%s_SIZE' % each, get_fsp_size(fsp_bin) if self._board.HAVE_FSP_BIN else 0)
|
|
setattr(self._board, '%s_UPD_SIZE' % each, get_fsp_upd_size(fsp_bin) if self._board.HAVE_FSP_BIN else 0)
|
|
|
|
if self._board.BUILD_CSME_UPDATE_DRIVER:
|
|
if os.name != 'nt':
|
|
raise Exception ('BUILD_CSME_UPDATE_DRIVER is enabled, build only works in WINDOWS !')
|
|
|
|
# create component base/size variables
|
|
self.update_component_list ()
|
|
self.create_platform_vars ()
|
|
|
|
# generate a padding file
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'PADDING.bin'), 0)
|
|
|
|
# create flashmap file
|
|
comp_list = self._comp_list
|
|
if len(self._comp_list) == 0 and getattr(self._board, "GetFlashMapList", None):
|
|
comp_list = self._board.GetFlashMapList()
|
|
gen_flash_map_bin (os.path.join(self._fv_dir, 'FlashMap.bin'), comp_list)
|
|
|
|
if not self._board.HAVE_VERIFIED_BOOT and self._board.HAVE_MEASURED_BOOT:
|
|
raise Exception ('Verified Boot must also enabled to enable Measured Boot!')
|
|
|
|
# create hashstore file
|
|
key_hash_list = []
|
|
mst_key = None
|
|
if self._board.HAVE_VERIFIED_BOOT:
|
|
hash_store_size = sizeof(HashStoreTable) + (sizeof(HashStoreData) + HASH_DIGEST_SIZE[HASH_VAL_STRING[self._board.SIGN_HASH_TYPE]]) * HashStoreTable().HASH_STORE_MAX_IDX_NUM
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'HashStore.bin'), hash_store_size)
|
|
|
|
#Intialize with HashStoreTable
|
|
fo = open(os.path.join(self._fv_dir, 'HashStore.bin'),'r+b')
|
|
hash_store_table = HashStoreTable()
|
|
hash_store_table.TotalLength = hash_store_size
|
|
fo.write(hash_store_table)
|
|
fo.close()
|
|
|
|
# create key hash file
|
|
mst_key = self._board._MASTER_PRIVATE_KEY
|
|
if getattr(self._board, "GetKeyHashList", None):
|
|
key_hash_list = self._board.GetKeyHashList ()
|
|
else:
|
|
self._board.VERIFIED_BOOT_HASH_MASK = 0
|
|
|
|
gen_pub_key_hash_store (mst_key, key_hash_list, HASH_VAL_STRING[self._board.SIGN_HASH_TYPE],
|
|
self._key_dir, os.path.join(self._fv_dir, 'KEYHASH.bin'))
|
|
|
|
# create fit table
|
|
if self._board.HAVE_FIT_TABLE:
|
|
FitSize = sizeof(FIT_ENTRY) * (self._board.FIT_ENTRY_MAX_NUM - 1)
|
|
if self._board.ACM_SIZE > 0:
|
|
# Make sure FIT table start address and end address are 64 bytes aligned.
|
|
FitSize = ((FitSize + 0x3F) & ~0x3F) + 0x3F
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'FitTable.bin'), FitSize)
|
|
|
|
|
|
# create bootloader version info file
|
|
ver_info_name = 'VerInfo'
|
|
ver_bin_file = os.path.join(self._fv_dir, ver_info_name + '.bin')
|
|
ver_txt_file = os.path.join(os.environ['PLT_SOURCE'], 'Platform', self._board.BOARD_PKG_NAME, ver_info_name + '.txt')
|
|
|
|
keys = ['VERINFO_IMAGE_ID', 'VERINFO_BUILD_DATE', 'VERINFO_PROJ_MINOR_VER',
|
|
'VERINFO_PROJ_MAJOR_VER', 'VERINFO_CORE_MINOR_VER', 'VERINFO_CORE_MAJOR_VER',
|
|
'VERINFO_SVN', 'FSPDEBUG_MODE', 'RELEASE_MODE']
|
|
ver_dict = {}
|
|
for key in keys:
|
|
ver_dict[key] = getattr (self._board, key)
|
|
if self._board.USE_VERSION:
|
|
ver_info = get_verinfo_via_file (ver_dict, ver_txt_file)
|
|
else:
|
|
ver_info = get_verinfo_via_git (ver_dict, os.environ['PLT_SOURCE'])
|
|
gen_ver_info_txt (ver_txt_file, ver_info)
|
|
gen_file_from_object (ver_bin_file, ver_info)
|
|
|
|
# create VBT file
|
|
if self._board.HAVE_VBT_BIN:
|
|
gen_vbt_file (self._board.BOARD_PKG_NAME, self._board._MULTI_VBT_FILE, os.path.join(self._fv_dir, 'Vbt.bin'))
|
|
|
|
# create platform include dsc file
|
|
platform_dsc_path = os.path.join(sbl_dir, 'BootloaderCorePkg', 'Platform.dsc')
|
|
self.create_dsc_inc_file (platform_dsc_path)
|
|
|
|
# rebase FSP accordingly
|
|
if self._board.HAVE_FSP_BIN:
|
|
rebase_fsp(fsp_path, self._fv_dir, self._board.FSP_T_BASE, self._board.FSP_M_BASE, self._board.FSP_S_BASE)
|
|
split_fsp(os.path.join(self._fv_dir, 'Fsp.bin'), self._fv_dir)
|
|
|
|
# create master key hash
|
|
if self._board.HAVE_VERIFIED_BOOT:
|
|
mst_priv_key = self._board._MASTER_PRIVATE_KEY
|
|
mst_pub_key_file = os.path.join(self._fv_dir, "MSTKEY.bin")
|
|
gen_pub_key (mst_priv_key, mst_pub_key_file)
|
|
gen_hash_file (mst_pub_key_file, HASH_VAL_STRING[self._board.SIGN_HASH_TYPE], '', True)
|
|
|
|
# create configuration data
|
|
if self._board.CFGDATA_SIZE > 0:
|
|
# create config data files
|
|
gen_config_file (self._fv_dir, self._board.BOARD_PKG_NAME, self._board._PLATFORM_ID,
|
|
self._board._CFGDATA_PRIVATE_KEY, self._board.CFG_DATABASE_SIZE, self._board.CFGDATA_SIZE,
|
|
self._board._CFGDATA_INT_FILE, self._board._CFGDATA_EXT_FILE, HASH_VAL_STRING[self._board.SIGN_HASH_TYPE])
|
|
|
|
# rebuild reset vector
|
|
vtf_dir = os.path.join('BootloaderCorePkg', 'Stage1A', 'Ia32', 'Vtf0')
|
|
x = subprocess.call([sys.executable, 'Build.py'], cwd=vtf_dir)
|
|
if x: raise Exception ('Failed to build reset vector !')
|
|
|
|
def build(self):
|
|
print("Build [%s] ..." % self._board.BOARD_NAME)
|
|
|
|
# Run pre-build
|
|
self.pre_build()
|
|
|
|
# Run build
|
|
cmd_args = [
|
|
"build" if os.name == 'posix' else "build.bat",
|
|
"--platform", os.path.join('BootloaderCorePkg', 'BootloaderCorePkg.dsc'),
|
|
"-b", self._target,
|
|
"--arch", 'IA32',
|
|
"--tagname", self._toolchain,
|
|
"-n", str(multiprocessing.cpu_count()),
|
|
"-y", "Report.log",
|
|
"-Y", "PCD",
|
|
"-Y", "FLASH",
|
|
"-Y", "LIBRARY"]
|
|
run_process (cmd_args)
|
|
|
|
# Run post-build
|
|
self.post_build()
|
|
|
|
print("Done [%s] !" % self._board.BOARD_NAME)
|
|
|
|
|
|
def post_build(self):
|
|
|
|
# create bootloader reserved binary of 4K size
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'SBLRSVD.bin'), 0x1000)
|
|
|
|
# create variable region for UEFI payload
|
|
if self._board.UEFI_VARIABLE_SIZE > 0:
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'UEFIVARIABLE.bin'), self._board.UEFI_VARIABLE_SIZE)
|
|
|
|
# create ACM binary
|
|
if self._board.ACM_SIZE > 0:
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'ACM.bin'), self._board.ACM_SIZE)
|
|
|
|
# create ACM3 binary
|
|
if self._board.ACM3_SIZE > 0:
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'ACM3.bin'), self._board.ACM3_SIZE)
|
|
|
|
# create MRC data
|
|
if self._board.MRCDATA_SIZE:
|
|
gen_file_with_size (os.path.join(self._fv_dir, 'MRCDATA.bin'), self._board.MRCDATA_SIZE)
|
|
|
|
|
|
# create variable binary
|
|
if self._board.VARIABLE_SIZE:
|
|
varhdr = VariableRegionHeader.from_buffer(bytearray(b'\xFF' * sizeof(VariableRegionHeader)))
|
|
varhdr.Signature = b'VARS'
|
|
varhdr.Size = self._board.VARIABLE_SIZE >> 1
|
|
varhdr.State = 0xFE
|
|
varfile = open (os.path.join(self._fv_dir, "VARIABLE.bin"), "wb")
|
|
varfile.write(varhdr)
|
|
varfile.write(b'\xFF' * (self._board.VARIABLE_SIZE - sizeof(varhdr)));
|
|
varfile.close()
|
|
|
|
|
|
# create microcode binary
|
|
if self._board.UCODE_SIZE > 0:
|
|
shutil.copy (
|
|
os.path.join(self._fv_dir, '../IA32/Microcode.bin'),
|
|
os.path.join(self._fv_dir, "UCODE.bin"))
|
|
|
|
# generate payload
|
|
gen_payload_bin (self._fv_dir, self._pld_list,
|
|
os.path.join(self._fv_dir, "PAYLOAD.bin"),
|
|
self._board._CONTAINER_PRIVATE_KEY, HASH_VAL_STRING[self._board.SIGN_HASH_TYPE], self._board.BOARD_PKG_NAME)
|
|
|
|
# create firmware update key
|
|
if self._board.ENABLE_FWU:
|
|
srcfile = "../IA32/PayloadPkg/FirmwareUpdate/FirmwareUpdate/OUTPUT/FirmwareUpdate.efi"
|
|
shutil.copyfile(
|
|
os.path.join(self._fv_dir, srcfile),
|
|
os.path.join(self._fv_dir, "FWUPDATE.bin"))
|
|
|
|
# create SPI IAS image if required
|
|
if self._board.SPI_IAS1_SIZE > 0 or self._board.SPI_IAS2_SIZE > 0:
|
|
for idx in range (1, 3):
|
|
file_path = os.path.join('Platform', self._board.BOARD_PKG_NAME, 'SpiIasBin', 'iasimage%d.bin' % idx)
|
|
file_space = getattr(self._board, 'SPI_IAS%d_SIZE' % idx)
|
|
gen_ias_file (file_path, file_space, os.path.join(self._fv_dir, "SPI_IAS%d.bin" % idx))
|
|
|
|
# generate container images
|
|
if getattr(self._board, "GetContainerList", None):
|
|
container_list = self._board.GetContainerList ()
|
|
component_dir = os.path.join(os.environ['PLT_SOURCE'], 'Platform', self._board.BOARD_PKG_NAME, 'Binaries')
|
|
gen_container_bin (container_list, self._fv_dir, component_dir, self._key_dir, '')
|
|
|
|
# patch stages
|
|
self.patch_stages ()
|
|
|
|
# create redundant components
|
|
self.create_redundant_components ()
|
|
|
|
|
|
# stitch all components
|
|
layout_name = 'ImgStitch.txt'
|
|
self.create_bootloader_image (layout_name)
|
|
|
|
# print flash map
|
|
if len(self._comp_list) > 0:
|
|
print_addr = False if getattr(self._board, "GetFlashMapList", None) else True
|
|
flash_map_text = decode_flash_map (os.path.join(self._fv_dir, 'FlashMap.bin'), print_addr)
|
|
print('%s' % flash_map_text)
|
|
fd = open (os.path.join(self._fv_dir, 'FlashMap.txt'), 'w')
|
|
fd.write (flash_map_text)
|
|
fd.close ()
|
|
|
|
|
|
def main():
|
|
prep_env ()
|
|
board_cfgs = []
|
|
board_names = []
|
|
|
|
# Find all boards
|
|
search_dir = os.environ['SBL_SOURCE']
|
|
if 'PLT_SOURCE' in os.environ:
|
|
search_dir = os.path.abspath(os.path.join(search_dir, os.path.pardir))
|
|
board_pkgs = os.listdir (search_dir)
|
|
for pkg in board_pkgs:
|
|
get_board_config_file (os.path.join (search_dir, pkg), board_cfgs)
|
|
|
|
for cfgfile in board_cfgs:
|
|
brdcfg = imp.load_source('BoardConfig', cfgfile)
|
|
board_names.append(brdcfg.Board().BOARD_NAME)
|
|
|
|
|
|
ap = argparse.ArgumentParser()
|
|
sp = ap.add_subparsers(help='command')
|
|
|
|
def cmd_build(args):
|
|
for index, name in enumerate(board_names):
|
|
if args.board == name:
|
|
brdcfg = imp.load_source('BoardConfig', board_cfgs[index])
|
|
board = brdcfg.Board(
|
|
RELEASE_MODE = args.release, \
|
|
NO_OPT_MODE = args.noopt, \
|
|
FSPDEBUG_MODE = args.fspdebug, \
|
|
USE_VERSION = args.usever, \
|
|
_PAYLOAD_NAME = args.payload, \
|
|
_FSP_PATH_NAME = args.fsppath
|
|
);
|
|
os.environ['PLT_SOURCE'] = os.path.abspath (os.path.join (os.path.dirname (board_cfgs[index]), '../..'))
|
|
Build(board).build()
|
|
break
|
|
|
|
buildp = sp.add_parser('build', help='build firmware')
|
|
buildp.add_argument('-r', '--release', action='store_true', help='Release build')
|
|
buildp.add_argument('-v', '--usever', action='store_true', help='Use board version file')
|
|
buildp.add_argument('-fp', dest='fsppath', type=str, help='FSP binary path relative to FspBin in Silicon folder', default='')
|
|
buildp.add_argument('-fd', '--fspdebug', action='store_true', help='Use debug FSP binary')
|
|
buildp.add_argument('-no', '--noopt', action='store_true', help='No compile/link optimization for debugging purpose. Not enabled in Release build.')
|
|
buildp.add_argument('-p', '--payload' , dest='payload', type=str, help='Payload file name', default ='OsLoader.efi')
|
|
buildp.add_argument('board', metavar='board', choices=board_names, help='Board Name (%s)' % ', '.join(board_names))
|
|
buildp.set_defaults(func=cmd_build)
|
|
|
|
def cmd_clean(args):
|
|
workspace = os.environ['WORKSPACE']
|
|
sbl_dir = os.environ['SBL_SOURCE']
|
|
dirs = ['Build', 'Conf']
|
|
files = [
|
|
os.path.join (sbl_dir, 'BootloaderCorePkg/Stage1A/Ia32/Vtf0/Bin/ResetVector.ia32.raw'),
|
|
os.path.join (sbl_dir, 'BootloaderCorePkg/Platform.dsc'),
|
|
os.path.join (workspace, 'Report.log')
|
|
]
|
|
|
|
if args.distclean:
|
|
dirs.extend ([
|
|
'Outputs',
|
|
])
|
|
|
|
files.extend ([
|
|
])
|
|
|
|
for dir in dirs:
|
|
dirpath = os.path.join (workspace, dir)
|
|
print('Removing %s' % dirpath)
|
|
shutil.rmtree(dirpath, ignore_errors=True)
|
|
|
|
for file in files:
|
|
if os.path.exists(file):
|
|
print('Removing %s' % file)
|
|
os.remove(file)
|
|
|
|
if os.path.exists(os.path.join (sbl_dir, '.git')):
|
|
cmd = 'git clean -xdf BaseTools'
|
|
x = subprocess.call(cmd.split(' '), cwd=sbl_dir)
|
|
if x: raise Exception ('Failed to run clean-up commands !')
|
|
|
|
print('Clean Done !')
|
|
|
|
cleanp = sp.add_parser('clean', help='clean build dir')
|
|
cleanp.add_argument('-d', '--distclean', action='store_true', help='Distribution clean')
|
|
cleanp.set_defaults(func=cmd_clean)
|
|
|
|
args = ap.parse_args()
|
|
args.func(args)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|