mirror of
https://github.com/rust-embedded/rust-raspberrypi-OS-tutorials.git
synced 2024-11-15 18:14:02 +00:00
190 lines
5.1 KiB
Ruby
190 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# SPDX-License-Identifier: MIT OR Apache-2.0
|
|
#
|
|
# Copyright (c) 2021-2022 Andre Richter <andre.o.richter@gmail.com>
|
|
|
|
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 size_human_readable(size)
|
|
if size >= (1024 * 1024)
|
|
"#{(size / (1024 * 1024)).to_s.rjust(3)} MiB"
|
|
elsif size >= 1024
|
|
"#{(size / 1024).to_s.rjust(3)} KiB"
|
|
else
|
|
raise
|
|
end
|
|
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 = size_human_readable(@virt_region.size * 65_536)
|
|
|
|
"#{name} | #{virt_start} | #{phys_start} | #{size} | #{@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
|