#!/usr/bin/env python3 import os import common import thread_pool from shell_helpers import LF class Main(common.BuildCliFunction): def __init__(self): super().__init__( defaults={ 'gcc_which': 'crosstool-ng', 'mode': 'baremetal', }, description='''\ Build the baremetal examples with crosstool-NG. ''', ) self._add_argument('--ccflags') self._add_argument('--force-rebuild') self._add_argument('--optimization-level') self.add_argument( 'targets', default=[], help='Analogous to ./build-userland target selection', nargs='*', ) def build(self): build_dir = self.get_build_dir() extra_obj_baremetal_bootloader = os.path.join( self.env['baremetal_build_lib_dir'], 'bootloader{}'.format(self.env['obj_ext']) ) extra_obj_lkmc_common = os.path.join( self.env['baremetal_build_lib_dir'], self.env['common_basename_noext'] + self.env['obj_ext'] ) cc_flags = [ '-I', self.env['root_dir'], LF, '-O{}'.format(self.env['optimization_level']), LF, '-nostartfiles', LF, ] if self.env['arch'] == 'arm': cc_flags.extend([ '-mhard-float', LF, # This uses the soft float ABI for calling functions from objets in Newlib which # our crosstool-NG config compiles with soft floats, while emiting hard float # from C and allowing us to use it from assembly, e.g. for the VMRS instruction: # which would otherwise fail "with selected processor does not support XXX in ARM mode" # Bibliography: # - https://stackoverflow.com/questions/9753749/arm-compilation-error-vfp-registered-used-by-executable-not-object-file # - https://stackoverflow.com/questions/41131432/cross-compiling-error-selected-processor-does-not-support-fmrx-r3-fpexc-in/41131782#41131782 # - https://embeddedartistry.com/blog/2017/10/9/r1q7pksku2q3gww9rpqef0dnskphtc '-mfloat-abi=softfp', LF, '-mfpu=crypto-neon-fp-armv8', LF, ]) if self.env['emulator'] == 'gem5': if self.env['machine'] == 'VExpress_GEM5_V1': entry_address = 0x80000000 uart_address = 0x1c090000 elif self.env['machine'] == 'RealViewPBX': entry_address = 0x10000 uart_address = 0x10009000 else: raise Exception('unknown machine: ' + self.env['machine']) cc_flags.extend([ '-DLKMC_GEM5=1', LF, '-DLKMC_M5OPS_ENABLE=1', LF, ]) else: entry_address = 0x40000000 uart_address = 0x09000000 cc_flags.extend(['-D', 'LKMC_QEMU=1', LF]) cc_flags.extend(['-D', 'LKMC_UART0_ADDR={:#x}'.format(uart_address), LF]) cc_flags.extend(self.sh.shlex_split(self.env['ccflags'])) bootloader_src = os.path.join( self.env['baremetal_source_lib_dir'], '{}{}'.format( self.env['arch'], self.env['asm_ext'] ) ) for in_path, out_path in [ (bootloader_src, extra_obj_baremetal_bootloader), (self.env['common_c'], extra_obj_lkmc_common), ( self.env['baremetal_syscalls_src'], self.env['baremetal_syscalls_obj'] ), ( self.env['baremetal_syscalls_asm_src'], self.env['baremetal_syscalls_asm_obj'] ), ]: self._build_one( in_path=in_path, out_path=out_path, cc_flags=cc_flags, extra_deps=[self.env['common_h']], link=False, ) cc_flags.extend([ '-Wl,--section-start=.text={:#x}'.format(entry_address), LF, '-T', self.env['baremetal_link_script'], LF, ]) with thread_pool.ThreadPool( self._build_one, nthreads=self.env['nproc'], submit_raise_exit=self.env['quit_on_fail'], ) as my_thread_pool: for target in self.env['targets']: for path, in_dirnames, in_filenames in self.sh.walk(target): for in_filename in in_filenames: in_ext = os.path.splitext(in_filename)[1] if not in_ext in self.env['baremetal_build_in_exts']: continue in_path = os.path.join(path, in_filename) my_thread_pool.submit({ 'cc_flags': cc_flags, 'extra_deps': [ self.env['baremetal_link_script'], self.env['common_h'] ], 'extra_objs': [ self.env['baremetal_syscalls_obj'], self.env['baremetal_syscalls_asm_obj'] ], 'extra_objs_baremetal_bootloader': [extra_obj_baremetal_bootloader], 'extra_objs_lkmc_common': [extra_obj_lkmc_common], 'in_path': in_path, 'out_path': self.resolve_baremetal_executable(in_path), }) return self._handle_thread_pool_errors(my_thread_pool) def get_build_dir(self): return self.env['baremetal_build_dir'] def setup_one(self): self.env['targets'] = self.resolve_targets( [ self.env['baremetal_source_dir'], self.env['userland_source_dir'] ], self.env['targets'] ) if __name__ == '__main__': Main().cli()