mirror of
https://github.com/seL4/seL4.git
synced 2026-03-27 10:29:57 +00:00
Right now the `platform_gen.json` and `platform_gen.yaml` files contain regions that are not actually used by the kernel. This is fine for `platform_gen.h` because they are guarded by #ifdefs with kernel config defines but the same approach does not work for YAML and JSON. So, this patch changes the Python scripts that generate this files so that they only generate the regions that actually end up being used. Signed-off-by: Ivan-Velickovic <i.velickovic@unsw.edu.au>
231 lines
7.0 KiB
Python
231 lines
7.0 KiB
Python
#
|
|
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
#
|
|
|
|
''' generate a c header file from the device tree '''
|
|
import argparse
|
|
import builtins
|
|
import jinja2
|
|
from typing import Dict, List
|
|
import hardware
|
|
from hardware.config import Config
|
|
from hardware.fdt import FdtParser
|
|
from hardware.utils.rule import HardwareYaml
|
|
|
|
|
|
HEADER_TEMPLATE = '''/*
|
|
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-only
|
|
*/
|
|
|
|
/*
|
|
* This file is autogenerated by <kernel>/tools/hardware/outputs/c_header.py.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#define PHYS_BASE_RAW {{ "0x{:x}".format(physBase) }}
|
|
|
|
#ifndef __ASSEMBLER__
|
|
|
|
#include <config.h>
|
|
#include <mode/hardware.h> /* for KDEV_BASE */
|
|
#include <linker.h> /* for BOOT_RODATA */
|
|
#include <basic_types.h> /* for p_region_t, kernel_frame_t (arch/types.h) */
|
|
|
|
/* Wrap raw physBase location constant to give it a symbolic name in C that's
|
|
* visible to verification. This is necessary as there are no real constants
|
|
* in C except enums, and enums constants must fit in an int.
|
|
*/
|
|
static inline CONST word_t physBase(void)
|
|
{
|
|
return PHYS_BASE_RAW;
|
|
}
|
|
|
|
/* INTERRUPTS */
|
|
{% for irq in kernel_irqs %}
|
|
/* {{ irq.desc }} */
|
|
{% if irq.has_enable() %}
|
|
{{ irq.get_enable_macro_str() }}
|
|
{% endif %}
|
|
{% if irq.has_sel() %}
|
|
{{ irq.get_sel_macro_str() }}
|
|
{% endif %}
|
|
#define {{ irq.label }} {{ irq.irq }}
|
|
{% if irq.has_sel() %}
|
|
#else
|
|
#define {{ irq.label }} {{ irq.false_irq }}
|
|
{{ irq.get_sel_endif() }}
|
|
{% endif %}
|
|
{% if irq.has_enable() %}
|
|
{{ irq.get_enable_endif() }}
|
|
{% endif %}
|
|
{% endfor -%}
|
|
|
|
/* KERNEL DEVICES */
|
|
{% for (addr, macro) in sorted(kernel_macros.items()) %}
|
|
#define {{ macro }} (KDEV_BASE + {{ "0x{:x}".format(addr) }})
|
|
{% endfor %}
|
|
|
|
{% if len(kernel_regions) > 0 %}
|
|
static const kernel_frame_t BOOT_RODATA kernel_device_frames[] = {
|
|
{% for group in kernel_regions %}
|
|
{% if group.has_macro() %}
|
|
{{ group.get_macro() }}
|
|
{% endif %}
|
|
/* {{ group.get_desc() }} */
|
|
{% for reg in group.regions %}
|
|
{
|
|
.paddr = {{ "0x{:x}".format(reg.base) }},
|
|
{% set map_addr = group.get_map_offset(reg) %}
|
|
{% if map_addr in kernel_macros %}
|
|
.pptr = {{ kernel_macros[map_addr] }},
|
|
{% else %}
|
|
/* contains {{ ', '.join(group.labels.keys()) }} */
|
|
.pptr = KDEV_BASE + {{ "0x{:x}".format(map_addr) }},
|
|
{% endif %}
|
|
{% if config.arch == 'arm' %}
|
|
.armExecuteNever = true,
|
|
{% endif %}
|
|
.userAvailable = {{ str(group.user_ok).lower() }}
|
|
},
|
|
{% endfor %}
|
|
{% if group.has_macro() %}
|
|
{{ group.get_endif() }}
|
|
{% endif %}
|
|
{% endfor %}
|
|
};
|
|
|
|
/* Elements in kernel_device_frames may be enabled in specific configurations
|
|
* only, but the ARRAY_SIZE() macro will automatically take care of this.
|
|
* However, one corner case remains unsolved where all elements are disabled
|
|
* and this becomes an empty array effectively. Then the C parser used in the
|
|
* formal verification process will fail, because it follows the strict C rules
|
|
* which do not allow empty arrays. Luckily, we have not met this case yet...
|
|
*/
|
|
#define NUM_KERNEL_DEVICE_FRAMES ARRAY_SIZE(kernel_device_frames)
|
|
{% else %}
|
|
/* The C parser used for formal verification process follows strict C rules,
|
|
* which do not allow empty arrays. Thus this is defined as NULL.
|
|
*/
|
|
static const kernel_frame_t BOOT_RODATA *const kernel_device_frames = NULL;
|
|
#define NUM_KERNEL_DEVICE_FRAMES 0
|
|
{% endif %}
|
|
|
|
/* PHYSICAL MEMORY */
|
|
static const p_region_t BOOT_RODATA avail_p_regs[] = {
|
|
{% for reg in physical_memory %}
|
|
/* {{ reg.owner.path }} */
|
|
{
|
|
.start = {{ "0x{:x}".format(reg.base) }},
|
|
.end = {{ "0x{:x}".format(reg.base + reg.size) }}
|
|
},
|
|
{% endfor %}
|
|
};
|
|
|
|
#endif /* !__ASSEMBLER__ */
|
|
|
|
'''
|
|
|
|
|
|
def get_kernel_devices(tree: FdtParser, hw_yaml: HardwareYaml, kernel_config_dict: Dict[str, str]) -> (List, Dict):
|
|
'''
|
|
Given a device tree and a set of rules, returns a tuple (groups, offsets).
|
|
|
|
Groups is a list of 'KernelRegionGroups', each of which represents a single
|
|
contiguous region of memory that is associated with a device.
|
|
Offsets is a dict of offset -> label, where label is the name given to the
|
|
kernel for that address (e.g. SERIAL_PPTR) and offset is the offset from
|
|
KDEV_BASE at which it's mapped.
|
|
'''
|
|
kernel_devices = tree.get_kernel_devices()
|
|
|
|
kernel_offset = 0
|
|
groups = []
|
|
for dev in kernel_devices:
|
|
dev_rule = hw_yaml.get_rule(dev)
|
|
new_regions = dev_rule.get_regions(dev)
|
|
for reg in new_regions:
|
|
if reg in groups:
|
|
other = groups[groups.index(reg)]
|
|
other.take_labels(reg)
|
|
else:
|
|
groups.append(reg)
|
|
|
|
offsets = {}
|
|
for group in groups:
|
|
kernel_offset = group.set_kernel_offset(kernel_offset)
|
|
offsets.update(group.get_labelled_addresses())
|
|
return (groups, offsets)
|
|
|
|
|
|
def get_interrupts(tree: FdtParser, hw_yaml: HardwareYaml) -> List:
|
|
''' Get dict of interrupts, {label: KernelInterrupt} from the DT and hardware rules. '''
|
|
kernel_devices = tree.get_kernel_devices()
|
|
|
|
irqs = []
|
|
for dev in kernel_devices:
|
|
dev_rule = hw_yaml.get_rule(dev)
|
|
if len(dev_rule.interrupts.items()) > 0:
|
|
irqs += dev_rule.get_interrupts(tree, dev)
|
|
|
|
ret = {}
|
|
for irq in irqs:
|
|
if irq.label in ret:
|
|
if irq.prio > ret[irq.label].prio:
|
|
ret[irq.label] = irq
|
|
else:
|
|
ret[irq.label] = irq
|
|
|
|
ret = list(ret.values())
|
|
ret.sort(key=lambda a: a.label)
|
|
return ret
|
|
|
|
|
|
def create_c_header_file(config, kernel_irqs: List, kernel_macros: Dict,
|
|
kernel_regions: List, physBase: int, physical_memory,
|
|
outputStream):
|
|
|
|
jinja_env = jinja2.Environment(loader=jinja2.BaseLoader, trim_blocks=True,
|
|
lstrip_blocks=True)
|
|
|
|
template = jinja_env.from_string(HEADER_TEMPLATE)
|
|
template_args = dict(
|
|
builtins.__dict__,
|
|
**{
|
|
'config': config,
|
|
'kernel_irqs': kernel_irqs,
|
|
'kernel_macros': kernel_macros,
|
|
'kernel_regions': kernel_regions,
|
|
'physBase': physBase,
|
|
'physical_memory': physical_memory})
|
|
data = template.render(template_args)
|
|
|
|
with outputStream:
|
|
outputStream.write(data)
|
|
|
|
|
|
def run(tree: FdtParser, hw_yaml: HardwareYaml, config: Config, kernel_config_dict, args: argparse.Namespace):
|
|
if not args.header_out:
|
|
raise ValueError('You need to specify a header-out to use c header output')
|
|
|
|
physical_memory, reserved, physBase = hardware.utils.memory.get_physical_memory(tree, config)
|
|
kernel_regions, kernel_macros = get_kernel_devices(tree, hw_yaml, kernel_config_dict)
|
|
|
|
create_c_header_file(
|
|
config,
|
|
get_interrupts(tree, hw_yaml),
|
|
kernel_macros,
|
|
kernel_regions,
|
|
physBase,
|
|
physical_memory,
|
|
args.header_out)
|
|
|
|
|
|
def add_args(parser):
|
|
parser.add_argument('--header-out', help='output file for c header',
|
|
type=argparse.FileType('w'))
|