# frozen_string_literal: true # SPDX-License-Identifier: MIT OR Apache-2.0 # # Copyright (c) 2021-2022 Andre Richter # KernelELF class KernelELF SECTION_FLAG_ALLOC = 2 def initialize(kernel_elf_path) @elf = ELFTools::ELFFile.new(File.open(kernel_elf_path)) @symtab_section = @elf.section_by_name('.symtab') end def machine @elf.machine.to_sym end def symbol_value(symbol_name) @symtab_section.symbol_by_name(symbol_name).header.st_value end def segment_containing_virt_addr(virt_addr) @elf.each_segments do |segment| return segment if segment.vma_in?(virt_addr) end end def virt_to_phys(virt_addr) segment = segment_containing_virt_addr(virt_addr) translation_offset = segment.header.p_vaddr - segment.header.p_paddr virt_addr - translation_offset end def virt_addr_to_file_offset(virt_addr) segment = segment_containing_virt_addr(virt_addr) segment.vma_to_offset(virt_addr) end def sections_in_segment(segment) head = segment.mem_head tail = segment.mem_tail sections = @elf.each_sections.select do |section| file_offset = section.header.sh_addr flags = section.header.sh_flags file_offset >= head && file_offset < tail && (flags & SECTION_FLAG_ALLOC != 0) end sections.map(&:name).join(' ') end def select_load_segments @elf.each_segments.select do |segment| segment.instance_of?(ELFTools::Segments::LoadSegment) end end def segment_get_acc_perms(segment) if segment.readable? && segment.writable? :ReadWrite elsif segment.readable? :ReadOnly else :Invalid end end def update_max_section_name_length(descriptors) MappingDescriptor.update_max_section_name_length(descriptors.map { |i| i.name.size }.max) end def generate_mapping_descriptors descriptors = select_load_segments.map do |segment| # Assume each segment is page aligned. size = segment.mem_size.align_up(BSP.kernel_granule::SIZE) virt_start_addr = segment.header.p_vaddr phys_start_addr = segment.header.p_paddr acc_perms = segment_get_acc_perms(segment) execute_never = !segment.executable? section_names = sections_in_segment(segment) virt_region = MemoryRegion.new(virt_start_addr, size, BSP.kernel_granule::SIZE) phys_region = MemoryRegion.new(phys_start_addr, size, BSP.kernel_granule::SIZE) attributes = AttributeFields.new(:CacheableDRAM, acc_perms, execute_never) MappingDescriptor.new(section_names, virt_region, phys_region, attributes) end update_max_section_name_length(descriptors) descriptors end end