Files
2026-07-13 13:04:25 +08:00

151 lines
5.0 KiB
Python

# SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
import rich_click as click
import yaml
from esp_pylib.cli_options import OptionEatAll
SEC_SRV_TABLE_SPLIT_ID = 30
OUTPUT_HEADERS = [
'secure_service_num.h',
'secure_service_dec.h',
'secure_service_int.h',
'secure_service_ext.h',
]
def parse_services(yml_files: list[str], excluded_fam: set[str]) -> list[tuple[int, str, int]]:
services, service_ids = [], set()
for yml_file in yml_files:
with open(yml_file) as f:
data = yaml.safe_load(f)
for family in data.get('secure_services', []):
family_name = family.get('family', '')
if family_name in excluded_fam:
continue
for entry in family.get('entries', []):
service_id = entry['id']
if service_id in service_ids:
raise ValueError(f'Duplicate service call ID found: 0x{service_id:X}')
service_ids.add(service_id)
services.append((service_id, entry['function'], entry['args']))
return sorted(services, key=lambda x: x[0])
def generate_num_header(services: list[tuple[int, str, int]], output_file: str) -> None:
header = """/**
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
"""
body = '\n'.join(f'#define SS_{name.upper()}\t{nr}' for nr, name, _ in services)
footer = f'\n#define MAX_SECURE_SERVICES_ID\t{services[-1][0] + 1 if services else 0}\n'
footer += f'#define SECURE_SERVICES_NUM\t{len(services)}\n\n'
footer += f'#define SECURE_SERVICES_SPLIT_ID\t{SEC_SRV_TABLE_SPLIT_ID}\n\n'
footer += """typedef void (*secure_service_t)(void);
typedef struct { secure_service_t func; int nargs; } secure_service_entry_t;
"""
footer += '\n#ifdef __cplusplus\n}\n#endif\n'
with open(output_file, 'w') as f:
f.write(header + body + footer)
def generate_dec_header(services: list[tuple[int, str, int]], output_file: str) -> None:
header = """/**
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
"""
body = '\n'.join(f'void _ss_{name}(void);' for _, name, _ in services)
footer = '\n#ifdef __cplusplus\n}\n#endif\n'
with open(output_file, 'w') as f:
f.write(header + body + footer)
def generate_table_split(services: list[tuple[int, str, int]], output_file_1: str, output_file_2: str) -> None:
header = """/**
* THIS FILE WAS AUTOMATICALLY GENERATED. DO NOT EDIT!
*/
#pragma once
"""
services_1 = [service for service in services if service[0] < SEC_SRV_TABLE_SPLIT_ID]
services_2 = [service for service in services if service[0] >= SEC_SRV_TABLE_SPLIT_ID]
body_1 = '\n'.join(f'__SECURE_SERVICE({nr}, {name}, {nargs})' for nr, name, nargs in services_1)
body_2 = '\n'.join(f'__SECURE_SERVICE({nr}, {name}, {nargs})' for nr, name, nargs in services_2)
with open(output_file_1, 'w') as f1, open(output_file_2, 'w') as f2:
f1.write(header + body_1)
f2.write(header + body_2)
def generate_wrap_list(yml_files: list[str], excluded_fam: set[str]) -> None:
wrap_list: list[str] = []
for yml_file in yml_files:
with open(yml_file) as f:
data = yaml.safe_load(f)
wrap_list.extend(
f'-Wl,--wrap={entry["function"]}'
for family in data.get('secure_services', [])
for entry in family.get('entries', [])
if entry['type'] == 'IDF' and family.get('family', '') not in excluded_fam
)
print(' '.join(wrap_list), end='')
@click.command(context_settings=dict(help_option_names=['-h', '--help']))
@click.option('--wrap', is_flag=True, default=False, help='Generate linker wrap options')
@click.option(
'-s',
'--sec_srv',
'sec_srv',
multiple=True,
required=True,
type=str,
cls=OptionEatAll,
help='Secure service table(s) in YAML (space- or flag-separated)',
)
@click.option(
'--exclude',
multiple=True,
type=str,
cls=OptionEatAll,
help='API families to exclude from the output (space- or flag-separated)',
)
def main(wrap: bool, sec_srv: tuple[str, ...], exclude: tuple[str, ...]) -> None:
"""Generate secure service outputs from YAML table."""
yml_files = list(sec_srv)
excluded_fam = set(exclude)
try:
if wrap:
generate_wrap_list(yml_files, excluded_fam)
else:
services = parse_services(yml_files, excluded_fam)
generate_num_header(services, OUTPUT_HEADERS[0])
generate_dec_header(services, OUTPUT_HEADERS[1])
generate_table_split(services, OUTPUT_HEADERS[2], OUTPUT_HEADERS[3])
except (ValueError, OSError, yaml.YAMLError) as e:
raise click.ClickException(str(e))
if __name__ == '__main__':
from esp_pylib.excepthook import install_exception_reporting
install_exception_reporting()
main()