# frozen_string_literal: true # SPDX-License-Identifier: MIT OR Apache-2.0 # # Copyright (c) 2021-2022 Andre Richter module Granule64KiB SIZE = 64 * 1024 SHIFT = Math.log2(SIZE).to_i end module Granule512MiB SIZE = 512 * 1024 * 1024 SHIFT = Math.log2(SIZE).to_i MASK = SIZE - 1 end # Monkey-patch Integer with some helper functions. class Integer def power_of_two? self[0].zero? end def aligned?(alignment) raise unless alignment.power_of_two? (self & (alignment - 1)).zero? end def align_up(alignment) raise unless alignment.power_of_two? (self + alignment - 1) & ~(alignment - 1) end def to_hex_underscore(with_leading_zeros: false) fmt = with_leading_zeros ? '%016x' : '%x' value = format(fmt, self).to_s.reverse.scan(/.{4}|.+/).join('_').reverse format('0x%s', value) end end # An array where each value is the start address of a Page. class MemoryRegion < Array def initialize(start_addr, size, granule_size) raise unless start_addr.aligned?(granule_size) raise unless size.positive? raise unless (size % granule_size).zero? num_pages = size / granule_size super(num_pages) do |i| (i * granule_size) + start_addr end end end # Collection of memory attributes. class AttributeFields attr_reader :mem_attributes, :acc_perms, :execute_never def initialize(mem_attributes, acc_perms, execute_never) @mem_attributes = mem_attributes @acc_perms = acc_perms @execute_never = execute_never end def to_s x = case @mem_attributes when :CacheableDRAM 'C' else '?' end y = case @acc_perms when :ReadWrite 'RW' when :ReadOnly 'RO' else '??' end z = @execute_never ? 'XN' : 'X ' "#{x} #{y} #{z}" end end # A container that describes a virt-to-phys region mapping. class MappingDescriptor @max_section_name_length = 'Sections'.length class << self attr_accessor :max_section_name_length def update_max_section_name_length(length) @max_section_name_length = [@max_section_name_length, length].max end end attr_reader :name, :virt_region, :phys_region, :attributes def initialize(name, virt_region, phys_region, attributes) @name = name @virt_region = virt_region @phys_region = phys_region @attributes = attributes end def to_s name = @name.ljust(self.class.max_section_name_length) virt_start = @virt_region.first.to_hex_underscore(with_leading_zeros: true) phys_start = @phys_region.first.to_hex_underscore(with_leading_zeros: true) size = ((@virt_region.size * 65_536) / 1024).to_s.rjust(3) "#{name} | #{virt_start} | #{phys_start} | #{size} KiB | #{@attributes}" end def self.print_divider print ' ' print '-' * max_section_name_length puts '--------------------------------------------------------------------' end def self.print_header print_divider print ' ' print 'Sections'.center(max_section_name_length) print ' ' print 'Virt Start Addr'.center(21) print ' ' print 'Phys Start Addr'.center(21) print ' ' print 'Size'.center(7) print ' ' print 'Attr'.center(7) puts print_divider end end def kernel_map_binary mapping_descriptors = KERNEL_ELF.generate_mapping_descriptors # Generate_mapping_descriptors updates the header being printed with this call. So it must come # afterwards. MappingDescriptor.print_header mapping_descriptors.each do |i| print 'Generating'.rjust(12).green.bold print ' ' puts i.to_s TRANSLATION_TABLES.map_at(i.virt_region, i.phys_region, i.attributes) end MappingDescriptor.print_divider end def kernel_patch_tables(kernel_elf_path) print 'Patching'.rjust(12).green.bold print ' Kernel table struct at ELF file offset ' puts BSP.kernel_tables_offset_in_file.to_hex_underscore File.binwrite(kernel_elf_path, TRANSLATION_TABLES.to_binary, BSP.kernel_tables_offset_in_file) end def kernel_patch_base_addr(kernel_elf_path) print 'Patching'.rjust(12).green.bold print ' Kernel tables physical base address start argument to value ' print TRANSLATION_TABLES.phys_tables_base_addr.to_hex_underscore print ' at ELF file offset ' puts BSP.phys_kernel_tables_base_addr_offset_in_file.to_hex_underscore File.binwrite(kernel_elf_path, TRANSLATION_TABLES.phys_tables_base_addr_binary, BSP.phys_kernel_tables_base_addr_offset_in_file) end