#!/usr/bin/python3 # SPDX-License-Identifier: BSD-2-Clause-Patent import os import sys import json import shutil import pprint import argparse import subprocess def openssl_configure(openssldir, target, ec = True): """ Run openssl Configure script. """ cmdline = [ 'perl', 'Configure', '--config=../UefiAsm.conf', '--api=1.1.1', '--with-rand-seed=none', target, 'no-afalgeng', 'no-aria', 'no-async', 'no-autoerrinit', 'no-autoload-config', 'no-bf', 'no-blake2', 'no-camellia', 'no-capieng', 'no-cast', 'no-chacha', 'no-cmac', 'no-cmp', 'no-cms', 'no-ct', 'no-deprecated', 'no-des', 'no-dgram', 'no-dsa', 'no-dso', 'no-dtls', 'no-dtls1-method', 'no-dtls1_2-method', 'no-dynamic-engine', 'no-ec2m', 'no-engine', 'no-err', 'no-filenames', 'no-gost', 'no-hw', 'no-idea', 'no-ktls', 'no-makedepend', 'no-module', 'no-md4', 'no-mdc2', 'no-multiblock', 'no-nextprotoneg', 'no-pic', 'no-psk', 'no-ocb', 'no-ocsp', 'no-padlockeng', 'no-poly1305', 'no-posix-io', 'no-rc2', 'no-rc4', 'no-rc5', 'no-rfc3779', 'no-rmd160', 'no-scrypt', 'no-seed', 'no-shared', 'no-siphash', 'no-siv', 'no-sm2', 'no-sm4', 'no-sock', 'no-srp', 'no-srtp', 'no-ssl', 'no-ssl3-method', 'no-ssl-trace', 'no-static-engine', 'no-stdio', 'no-threads', 'no-tls1_3', 'no-ts', 'no-ui-console', 'no-whirlpool', 'disable-legacy', ] if not ec: cmdline += [ 'no-ec', ] print('') print(f'# -*- configure openssl for {target} (ec={ec}) -*-') rc = subprocess.run(cmdline, cwd = openssldir, stdout = subprocess.PIPE, stderr = subprocess.PIPE) if rc.returncode: print(rc.stdout) print(rc.stderr) sys.exit(rc.returncode) def openssl_run_make(openssldir, target): """ Run make utility to generate files or cleanup. Target can be either a string or a list of strings. """ cmdline = [ 'make', '--silent' ] if isinstance(target, list): cmdline += target else: cmdline += [ target, ] rc = subprocess.run(cmdline, cwd = openssldir) rc.check_returncode() def get_configdata(openssldir): """ Slurp openssl config data as JSON, using a little perl helper script. """ cmdline = [ 'perl', 'perl2json.pl', openssldir, ] rc = subprocess.run(cmdline, stdout = subprocess.PIPE) rc.check_returncode() return json.loads(rc.stdout) def is_asm(filename): """ Check whenevr the passed file is an assembler file """ if filename.endswith('.s') or filename.endswith('.S'): return True return False def copy_generated_file(src, dst): src_file = [] with open(src, 'r') as fsrc: src_file = fsrc.readlines() with open(dst, 'w') as fdst: for lines in range(len(src_file)): s = src_file[lines] s = s.rstrip() + "\r\n" fdst.write(s.expandtabs()) def generate_files(openssldir, opensslgendir, asm, filelist): """ Generate files, using make, and copy over the results to the directory tree for generated openssl files. Creates subdirectories as needed. """ openssl_run_make(openssldir, filelist) for filename in filelist: src = os.path.join(openssldir, filename) if is_asm(filename): """ rename MSFT asm files to .nasm """ if 'IA32-MSFT' in asm: filename = filename.replace('.S', '.nasm') elif 'X64-MSFT' in asm: filename = filename.replace('.s', '.nasm') dst = os.path.join(opensslgendir, asm, filename) else: dst = os.path.join(opensslgendir, filename) os.makedirs(os.path.dirname(dst), exist_ok = True) copy_generated_file(src, dst) def generate_include_files(openssldir, opensslgendir, asm, cfg): """ Generate openssl include files """ print('# generate include files') filelist = cfg['unified_info']['generate'].keys() filelist = list(filter(lambda f: 'include' in f, filelist)) generate_files(openssldir, opensslgendir, asm, filelist) def generate_library_files(openssldir, opensslgendir, asm, cfg, obj): """ Generate openssl source files for a given library. Handles mostly assembler files, but a few C sources are generated too. """ filelist = get_source_list(cfg, obj, True) if filelist: print(f'# generate source files for {obj}') generate_files(openssldir, opensslgendir, asm, filelist) def generate_all_files(openssldir, opensslgendir, asm, cfg): """ Generate all files needed. """ generate_include_files(openssldir, opensslgendir, asm, cfg) generate_library_files(openssldir, opensslgendir, asm, cfg, 'libcrypto') generate_library_files(openssldir, opensslgendir, asm, cfg, 'providers/libcommon.a') generate_library_files(openssldir, opensslgendir, asm, cfg, 'libssl') def get_source_list(cfg, obj, gen): """ Gets the list of source files needed to create a specific object. * If 'gen' is True the function returns the list of generated files. * If 'gen' is False the function returns the list of files not generated (which are used from the submodule directly). Note: Will call itself recursively to resolve nested dependencies. """ sources = cfg['unified_info']['sources'] generate = cfg['unified_info']['generate'] srclist = [] if sources.get(obj): for item in sources.get(obj): srclist += get_source_list(cfg, item, gen) else: is_generated = generate.get(obj) is not None if is_generated == gen: srclist += [ obj, ] return srclist def asm_filter_fn(filename): """ Filter asm source and define lists. Drops files we don't want include. """ exclude = [ '/bn/', 'OPENSSL_BN_ASM', 'OPENSSL_IA32_SSE2', '/ec/', 'ECP_NISTZ256_ASM', 'X25519_ASM', ] for item in exclude: if item in filename: return False return True def get_sources(cfg, obj, asm): """ Get the list of all sources files. Will fetch both generated and not generated file lists and update the paths accordingly, so the openssl submodule or the sub-tree for generated files is referenced as needed. """ srclist = get_source_list(cfg, obj, False) genlist = get_source_list(cfg, obj, True) srclist = list(map(lambda x: f'$(OPENSSL_PATH)/{x}', srclist)) c_list = list(map(lambda x: f'$(OPENSSL_GEN_PATH)/{x}', filter(lambda x: not is_asm(x), genlist))) asm_list = list(map(lambda x: f'$(OPENSSL_GEN_PATH)/{asm}/{x}', filter(is_asm, genlist))) asm_list = list(filter(asm_filter_fn, asm_list)) return srclist + c_list + asm_list def sources_filter_fn(filename): """ Filter source lists. Drops files we don't want include or need replace with our own uefi-specific version. """ exclude = [ 'randfile.c', '/store/', '/storemgmt/', '/encode_decode/encode', '/pkcs12/', 'statem_srvr.c', 'extensions_srvr.c', 'defltprov.c', 'baseprov.c', 'provider_predefined.c', 'ecp_nistz256.c', 'x86_64-gcc.c', ] for item in exclude: if item in filename: return False return True def libcrypto_sources(cfg, asm = None): """ Get source file list for libcrypto """ files = get_sources(cfg, 'libcrypto', asm) files += get_sources(cfg, 'providers/libcommon.a', asm) files = list(filter(sources_filter_fn, files)) return files def libssl_sources(cfg, asm = None): """ Get source file list for libssl """ files = get_sources(cfg, 'libssl', asm) files = list(filter(sources_filter_fn, files)) return files def update_inf(filename, sources, arch = None, defines = []): """ Update inf file, replace source file list and build flags. """ head = '' tail = '' state = 0 if arch: section = f'Sources.{arch}' flags = f'OPENSSL_FLAGS_{arch}' else: section = None flags = f'OPENSSL_FLAGS_NOASM' state = 1 # read and parse file with open(filename, 'r') as f: while True: line = f.readline() if line == '': break if state in [0, 1]: if flags in line: (keep, replace) = line.split('=') args = map(lambda x: f'-D{x}', defines) head += keep + '= ' + ' '.join(args) head = head.rstrip() + '\r\n' else: head += line.rstrip() + '\r\n' if state == 0 and section in line: state = 1 if state == 1 and 'Autogenerated files list starts here' in line: state = 2 if state == 2 and 'Autogenerated files list ends here' in line: state = 3 if state == 3: tail += line.rstrip() + '\r\n' # write updated file with open(filename, 'w') as f: f.write(head) for src in sources: f.write(f' {src}\r\n') f.write(tail) def update_MSFT_asm_format(asm, filelist): """ rename MSFT asm files to .nasm """ if 'IA32-MSFT' in asm: for file_index in range(len(filelist)): filelist[file_index] = filelist[file_index].replace('.S', '.nasm') elif 'X64-MSFT' in asm: for file_index in range(len(filelist)): filelist[file_index] = filelist[file_index].replace('.s', '.nasm') def main(): # prepare os.chdir(os.path.dirname(os.path.abspath(__file__))) openssldir = os.path.join(os.getcwd(), 'openssl') opensslgendir = os.path.join(os.getcwd(), 'OpensslGen') # asm accel configs (see UefiAsm.conf) for ec in [True, False]: if ec: inf = 'OpensslLibFullAccel.inf' hdr = 'configuration-ec.h' else: inf = 'OpensslLibAccel.inf' hdr = 'configuration-noec.h' sources = {} defines = {} for asm in [ 'UEFI-IA32-MSFT', 'UEFI-IA32-GCC', 'UEFI-X64-MSFT', 'UEFI-X64-GCC']: (uefi, arch, cc) = asm.split('-') archcc = f'{arch}-{cc}' openssl_configure(openssldir, asm, ec = ec); cfg = get_configdata(openssldir) generate_all_files(openssldir, opensslgendir, archcc, cfg) shutil.move(os.path.join(opensslgendir, 'include', 'openssl', 'configuration.h'), os.path.join(opensslgendir, 'include', 'openssl', hdr)) openssl_run_make(openssldir, 'distclean') srclist = libcrypto_sources(cfg, archcc) + libssl_sources(cfg, archcc) sources[archcc] = list(map(lambda x: f'{x} | {cc}', filter(is_asm, srclist))) update_MSFT_asm_format(archcc, sources[archcc]) sources[arch] = list(filter(lambda x: not is_asm(x), srclist)) defines[arch] = cfg['unified_info']['defines']['libcrypto'] defines[arch] = list(filter(asm_filter_fn, defines[arch])) ia32accel = sources['IA32'] + sources['IA32-MSFT'] + sources['IA32-GCC'] x64accel = sources['X64'] + sources['X64-MSFT'] + sources['X64-GCC'] update_inf(inf, ia32accel, 'IA32', defines['IA32']) update_inf(inf, x64accel, 'X64', defines['X64']) # noaccel - ec enabled openssl_configure(openssldir, 'UEFI', ec = True); cfg = get_configdata(openssldir) generate_all_files(openssldir, opensslgendir, None, cfg) openssl_run_make(openssldir, 'distclean') defines = [] if 'libcrypto' in cfg['unified_info']['defines']: defines = cfg['unified_info']['defines']['libcrypto'] update_inf('OpensslLibFull.inf', libcrypto_sources(cfg) + libssl_sources(cfg), defines) # noaccel - ec disabled openssl_configure(openssldir, 'UEFI', ec = False); cfg = get_configdata(openssldir) generate_all_files(openssldir, opensslgendir, None, cfg) openssl_run_make(openssldir, 'distclean') update_inf('OpensslLibCrypto.inf', libcrypto_sources(cfg), None, defines) update_inf('OpensslLib.inf', libcrypto_sources(cfg) + libssl_sources(cfg), None, defines) # wrap header file confighdr = os.path.join(opensslgendir, 'include', 'openssl', 'configuration.h') with open(confighdr, 'w') as f: f.write('#ifdef EDK2_OPENSSL_NOEC\r\n' '# include "configuration-noec.h"\r\n' '#else\r\n' '# include "configuration-ec.h"\r\n' '#endif\r\n') if __name__ == '__main__': sys.exit(main())