Files
seL4/tools/hardware/outputs/c_header.py
julia f9eb65c9a5 tools: don't exclude device-tree reserved memory
... from the device UT listed by platform_gen. The kernel itself
does not care about this memory, and it is just given as device UT.

We also just remove the reserved array entirely from the return of
`get_physical_memory` since it only seems to be a footgun, it's
only used internally to affect what memory the kernel wants to use.

Co-authored-by: Kent McLeod <kent@kry10.com>
Signed-off-by: julia <git.ts@trainwit.ch>
2025-10-16 11:05:15 +01:00

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, 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'))