1#!/usr/bin/env python 2# 3# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) 4# 5# SPDX-License-Identifier: BSD-2-Clause 6# 7""" 8Generate an initial list of untyped objects based on the available physical 9memory regions, the kernel image size, user image size, and devices on a platform 10""" 11 12import argparse 13import logging 14from collections import namedtuple, deque, defaultdict 15 16from elftools.elf.elffile import ELFFile 17from pyaml import yaml 18from sortedcontainers import SortedList 19 20from capdl import register_object_sizes 21from capdl.ELF import ELF 22from capdl.Object import get_object_size_bits, ObjectType 23from capdl.util import ctz, valid_architectures, lookup_architecture, round_down, round_up, PAGE_SIZE 24 25Region = namedtuple('Region', ['start', 'end']) 26 27 28def create_untypeds_for_region(object_sizes, region, arch, device): 29 untyped = [] 30 start = region.start 31 end = region.end 32 while start != end: 33 assert (start <= end) 34 size_bits = (end - start).bit_length() - 1 if start < end else arch.word_size_bits() 35 align_bits = ctz(start) if start > 0 else size_bits 36 size_bits = min(size_bits, 37 align_bits, 38 object_sizes['seL4_Value_MaxUntypedBits']) 39 if size_bits > object_sizes['seL4_Value_MinUntypedBits']: 40 untyped.append({'device': device, 'size_bits': size_bits, 'paddr': start}) 41 start += 1 << size_bits 42 return untyped 43 44 45def init_freemem(available, reserved): 46 """ 47 Remove any reserved regions from available and return a new list of 48 available regions that does not contain any reserved regions 49 50 This method mirrors init_freemem in the kernel. 51 """ 52 53 freemem = [] 54 available = deque(available) 55 reserved = deque(reserved) 56 while len(available) and len(reserved): 57 if reserved[0].start == reserved[0].end: 58 # reserved region is empty - skip it 59 reserved.popleft() 60 if available[0].start >= available[0].end: 61 # skip the entire region - it's empty now after trimming 62 available.popleft() 63 elif reserved[0].end <= available[0].start: 64 # the reserved region is below the available region - skip it 65 reserved.popleft() 66 elif reserved[0].start >= available[0].end: 67 # the reserved region is above the available region - take the whole thing 68 freemem.append(available[0]) 69 available.popleft() 70 else: 71 # the reserved region overlaps with the available region 72 if reserved[0].start <= available[0].start: 73 # the region overlaps with the start of the available region. 74 # trim start of the available region 75 available[0] = Region(min(available[0].end, reserved[0].end), 76 available[0].end) 77 reserved.popleft() 78 else: 79 assert reserved[0].start < available[0].end 80 # take the first chunk of the available region and move 81 # the start to the end of the reserved region 82 freemem.append(Region(available[0].start, reserved[0].start)) 83 if available[0].end > reserved[0].end: 84 available[0] = Region(reserved[0].end, available[0].end) 85 reserved.popleft() 86 else: 87 available.popleft() 88 89 # no more reserved regions - add the rest 90 freemem += list(available) 91 return freemem 92 93 94def get_load_bounds(elf): 95 end = 0 96 start = 0xFFFFFFFFFFFFFFFF 97 for s in elf.iter_segments(): 98 if s['p_type'] == 'PT_LOAD': 99 paddr = s['p_paddr'] 100 memsz = s['p_memsz'] 101 start = min(paddr, start) 102 end = max(paddr + memsz, end) 103 print("ELF image: {0}<-->{1}".format(hex(start), hex(end))) 104 return Region(start, end) 105 106 107def get_symbol_size(elf, name): 108 symbol_table = elf.get_section_by_name('.symtab') 109 symbol = symbol_table.get_symbol_by_name(name) 110 if not symbol: 111 logging.fatal("No symbol {0}".format(name)) 112 return symbol['st_size'] 113 114 115def main(args): 116 arch = lookup_architecture(args.architecture) 117 addresses = yaml.load(args.input) 118 object_sizes = yaml.load(args.object_sizes) 119 register_object_sizes(object_sizes) 120 121 # create the list of reserved regions. This duplicates the load_images part of the elf loader. Ultimately 122 # we should feed this info to the elf loader rather than doing it dynamically 123 reserved = SortedList() 124 # first the kernel image 125 kernel_elf = ELFFile(args.kernel_elf) 126 kernel_region = get_load_bounds(kernel_elf) 127 # elfloader currently rounds end to page boundary 128 kernel_region = Region(kernel_region.start, round_up(kernel_region.end, PAGE_SIZE)) 129 reserved.add(kernel_region) 130 131 # now the DTB 132 next_paddr = kernel_region.end 133 if args.dtb_size: 134 dtb_region = Region(next_paddr, round_up(next_paddr + args.dtb_size, PAGE_SIZE)) 135 reserved.add(dtb_region) 136 print("DTB: {0}<-->{1}".format(hex(dtb_region.start), hex(dtb_region.end))) 137 138 available = SortedList() 139 for a in addresses['memory']: 140 # trim to paddr-top 141 start, end = a['start'], a['end'] 142 if args.paddr_top is not None: 143 start = min(start, args.paddr_top) 144 end = min(end, args.paddr_top) 145 if start != end: 146 available.add(Region(start, end)) 147 148 # calculate free regions based on available + reserved regions 149 freemem = init_freemem(available, reserved) 150 151 # create untyped for each region 152 untypeds = [] 153 for f in freemem: 154 untypeds += create_untypeds_for_region(object_sizes, f, arch, False) 155 156 # create untyped for each device untyped 157 for d in addresses['devices']: 158 untypeds += create_untypeds_for_region(object_sizes, 159 Region(d['start'], d['end']), arch, True) 160 161 # finally output the file 162 yaml.dump(untypeds, args.output) 163 164 165if __name__ == '__main__': 166 def int_or_hex(s): 167 if s.strip().startswith('0x'): 168 return int(s, 0) 169 else: 170 return int(s) 171 172 parser = argparse.ArgumentParser() 173 parser.add_argument('--input', required=True, type=argparse.FileType('r'), 174 help='input yaml describing physical and device memory') 175 parser.add_argument('--output', required=True, type=argparse.FileType('w'), 176 help='output file for generated untyped yaml') 177 parser.add_argument('--linker', type=argparse.FileType('w'), help="Output file for linker") 178 parser.add_argument('--object-sizes', required=True, type=argparse.FileType('r'), 179 help='Yaml file with kernel object sizes') 180 parser.add_argument('--extra-bi-size-bits', default=0, type=int, 181 help='Size_bits of extra bootinfo frame (0 if none)') 182 parser.add_argument('--kernel-elf', type=argparse.FileType('rb'), 183 help='Kernel elf file', required=False) 184 parser.add_argument('--paddr-top', type=int_or_hex, 185 help='Kernel\'s PADDR_TOP (highest usable physical memory addr)') 186 parser.add_argument('--architecture', choices=valid_architectures()) 187 parser.add_argument('--dtb-size', type=int_or_hex, default=0, 188 help='DTB (device tree binary) blob size') 189 parser.add_argument('--elffile', nargs='+', action='append') 190 191 main(parser.parse_args()) 192