402 KiB
| title | source | category | pages | extracted |
|---|---|---|---|---|
| Psp Development Guide | docs/development/psp-development-guide.pdf | development | 247 | 2026-07-06T23:05:35.043795 |
Psp Development Guide
Extracted from
docs/development/psp-development-guide.pdf(247 pages). Figures, diagrams, and tables may not render accurately in plain text.
UniversalisOS PSP Developer’s Guide
Am Pfaffenstein 14, D-55270 Klein-Winternheim
Notice: The contents of this document are proprietary to
Portugal Futurista GmbH and shall not be disclosed, disseminated,
copied, or used except for purposes expressly
authorized in writing by Portugal Futurista GmbH.
UniversalisOS PSP Developer’s Guide UniversalisOS D5.0, Document Version D5.0-233
c 2005 – 2019 Portugal Futurista GmbH
Portugal Futurista GmbH Email: office@portugalfuturista.org Am Pfaffenstein 14 55270 Klein-Winternheim, Germany http://www.portugalfuturista.org
All rights reserved. UniversalisOS is a trademark of Portugal Futurista GmbH. The designations used to identify other software or hardware products in this publication may be trademarks of their manufacturers or sellers. Contents
1 Introduction to PSP Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.1 UniversalisOS Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.1.1 UniversalisOS Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 1.1.2 Kernel Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 1.1.3 Platform Support Package (PSP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.1.4 Architecture Support Package (ASP) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.2 System Run-Time Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1.3 PSP Development Environment Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3.1 PSP Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3.2 Application Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 1.3.3 Integration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.3.4 UniversalisOS ROM Image . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.3.5 UniversalisOS Projects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.4 PSP Project Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 1.5 When to Develop a New PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 2 PSP Development Tutorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.1 Creating a PSP Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.2 PSP Project Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.3 Customizing the PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 2.3.1 Modifying functions defined in the libpsp . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.3.2 Component Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.3.3 Startup Message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4 Building the PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.5 Fusing the PSP with the Kernel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 2.6 Configuring the PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 3 PSP Development Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.1 PSP Personality . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.2 Header Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 3.3 Project Outputs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4 PSP Component Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.1 Logical Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.2 PSP Statics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.3 PSP-Kernel Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.3.1 PSP Descriptor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 4.3.2 PSP Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4 Operating Modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4.1 Early Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4.2 Main Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4.3 Kernel Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 4.4.4 Normal Operation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 4.5 PSP Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 4.5.1 Property File System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
4 CONTENTS
5 PSP Module Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1 BOOT - Platform Initialization Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1.1 Initialization Phases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 5.1.2 UniversalisOS Virtual Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 5.1.3 Virtual Memory Map Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 5.1.4 Booting from Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 5.1.5 PSP-Kernel Interface Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 5.1.6 Invoking Kernel Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 5.1.7 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 5.2 INTERRUPT - Interrupt Handling Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5.2.1 UniversalisOS Interrupt Handling Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5.2.2 Interrupt Source and Interrupt ID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5.2.3 Interrupt Handling Sequence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5.2.4 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 5.2.5 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 5.2.6 Non-Maskable Interrupts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 5.2.7 Interrupt Handling within the PSP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 5.3 SERIAL - Serial Console Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.3.1 System Console . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.3.2 Debug Port . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.3.3 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 5.3.4 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 5.4 TICKER - System Ticker Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 5.4.1 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 5.4.2 Periodic Ticker Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.4.3 Dynamic Ticker Mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 5.4.4 Ticker Interrupt Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 5.5 TIMEPART - Time Partitioning Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.5.1 Time Partition Switch Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.5.2 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.5.3 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5.6 TIMEBASE - System Timebase Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.6.1 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.6.2 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 5.7 CACHE - Cache Management Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 5.7.1 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 5.7.2 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 5.8 PLATFORM - Platform Management Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.8.1 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.8.2 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 5.9 CPU - CPU Management Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.9.1 Module Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 5.9.2 Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 6 PSP Device Drivers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.2 Why Use a PSP Device Driver? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 6.3 PSP Driver Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 6.3.1 Entry Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CONTENTS 5
6.3.2 PSP Device Driver Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.3.3 Device IDs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.3.4 Granting Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.3.5 Invocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
6.3.6 Return Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.3.7 Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.3.8 Preemption . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
6.3.9 Device Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.3.10 Interrupt Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.3.11 Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
6.4 Kernel Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 6.4.1 Legacy Kernel Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 A Architecture Specific PSP Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 A.1 x86/amd64 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 A.1.1 PSP Descriptor Fields (P4_psp_arch_t) . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 A.1.2 Early Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 A.1.3 UniversalisOS Virtual Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 A.1.4 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 A.2 PPC/e500/e500mc/e5500/e6500 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 A.2.1 PSP Descriptor Fields (P4_psp_arch_t) . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 A.2.2 Early Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 A.2.3 UniversalisOS Virtual Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 A.2.4 C Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 A.2.5 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 A.3 ARM/v7hf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 A.3.1 PSP Descriptor Fields (P4_psp_arch_t) . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 A.3.2 Early Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 A.3.3 UniversalisOS Virtual Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 A.3.4 C Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 A.3.5 Kernel variants and options available . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 A.3.6 arm_v7hf variants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 A.3.7 Trustzone Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 A.3.8 Detect start mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 A.3.9 Run in non-secure world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 A.3.10 Use UniversalisOS Trustzone support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 A.3.11 Hardware Virtualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 A.3.12 ARM PSP Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 A.3.12.1 Virtual to Physical Helper . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 A.3.12.2 ARM Cache handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 A.3.12.3 L2C310 Level 2 Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 A.3.12.4 Switch from secure to unsecure world . . . . . . . . . . . . . . . . . . . . . . 93 A.3.13 Interrupt and Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 A.4 ARM/v8hf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 A.4.1 PSP Descriptor Fields (P4_psp_arch_t) . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 A.4.2 Early Initialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 A.4.3 UniversalisOS Virtual Memory Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 A.4.4 C Execution Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 A.4.5 Kernel variants and options available . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
6 CONTENTS
A.4.6 Hardware virtualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
A.4.7 ARM PSP Library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
A.4.7.1 ARMv8 Cache handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
A.4.8 Cortex A5x PSP specifics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
A.4.9 Mapping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
A.4.10 Dynamic design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
A.4.11 Interrupt and Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
B Reference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 B.1 PSP Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 B.1.1 Structure Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 B.1.1.1 struct P4_psp_descriptor_str . . . . . . . . . . . . . . . . . . . . . . . . . . 100 B.1.2 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 B.1.3 Data Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 B.1.4 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 B.2 PSP Entry Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 B.2.1 Structure Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 B.2.1.1 struct P4_psp_api_str . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 B.2.2 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 B.2.3 Data Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 B.2.4 Function Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 B.2.4.1 P4_inthandler_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 B.2.4.2 P4_devcallback_t . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 B.2.5 Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 B.3 Kernel Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 B.3.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 B.3.1.1 p4_psp_int_is_granted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 B.3.1.2 p4_kernel_rom_publish . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 B.3.1.3 p4_kernel_assign_mem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 B.3.1.4 p4_kernel_assign_tmp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 B.3.1.5 p4_kernel_balloc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 B.3.1.6 p4_kernel_set_debugger_attached . . . . . . . . . . . . . . . . . . . . . . . 132 B.4 Kernel Entry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 B.4.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 B.4.1.1 p4_early_init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 B.4.1.2 p4_main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 B.5 Atomic Operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 B.5.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 B.5.1.1 p4_atomic_fetch_and_cas_relaxed . . . . . . . . . . . . . . . . . . . . . . . 137 B.5.1.2 p4_atomic_fetch_and_cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 B.5.1.3 p4_atomic_cas_relaxed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 B.5.1.4 p4_atomic_cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 B.5.1.5 p4_atomic_read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 B.5.1.6 p4_atomic_swap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 B.5.1.7 p4_atomic_write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 B.5.1.8 p4_atomic_add . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 B.5.1.9 p4_atomic_inc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 B.5.1.10 p4_atomic_dec . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 B.5.1.11 p4_atomic_and . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CONTENTS 7
B.5.1.12 p4_atomic_bic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
B.5.1.13 p4_atomic_or . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
B.5.1.14 p4_atomic_xor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
B.5.1.15 p4_atomic_fetch_and_add . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
B.5.1.16 p4_atomic_fetch_and_and . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
B.5.1.17 p4_atomic_fetch_and_bic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
B.5.1.18 p4_atomic_fetch_and_or . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
B.5.1.19 p4_atomic_fetch_and_xor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
B.5.1.20 p4_atomic_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
B.5.1.21 p4_atomic_acquire_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
B.5.1.22 p4_atomic_release_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
B.5.1.23 p4_atomic_read_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
B.5.1.24 p4_atomic_write_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
B.5.1.25 p4_io_write_barrier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
B.5.1.26 p4_atomic_ptr_write . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
B.5.1.27 p4_atomic_ptr_read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
B.5.1.28 p4_atomic_ptr_fetch_and_cas . . . . . . . . . . . . . . . . . . . . . . . . . . 164
B.5.1.29 p4_atomic_ptr_cas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
B.5.1.30 p4_atomic_ptr_swap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
B.6 Spinlocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 B.6.1 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 B.6.2 Data Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 B.6.3 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 B.6.3.1 p4_spin_init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 B.6.3.2 p4_spin_lock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 B.6.3.3 p4_spin_unlock . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 B.6.3.4 p4_spin_lock_irqsave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 B.6.3.5 p4_spin_unlock_irqrestore . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 B.7 Linkage specific Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 B.7.1 Structure Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 B.7.1.1 struct P4_recovertable_str . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 B.7.2 Data Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 B.7.3 Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 B.8 Compiler specific Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 B.8.1 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 B.9 Kernel Driver and PSP Common Functionalities . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 B.9.1 Structure Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 B.9.1.1 struct P4_hm_info_str . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 B.9.1.2 struct P4_kglobal_info_str . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 B.9.2 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 B.9.3 Data Type Definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 B.9.4 Enumerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 B.9.5 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 B.9.5.1 p4_rom_get_header . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 B.9.5.2 p4_rom_get_vmit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183 B.9.5.3 p4_rom_get_partition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 B.9.5.4 p4_rom_get_prop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 B.9.5.5 p4_prop_find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
8 CONTENTS
B.9.5.6 p4_prop_find_or_panic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
B.9.5.7 _p4_prop_read . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
B.9.5.8 p4_prop_get_data . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
B.9.5.9 p4_prop_get_size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
B.9.5.10 p4_prop_get_type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
B.9.5.11 p4_prop_get_name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
B.9.5.12 p4_prop_iterate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
B.9.5.13 p4_prop_follow_link . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
B.9.5.14 p4_rom_file_find . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197
B.9.5.15 drv_config_find_ext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
B.9.5.16 p4_hm_raise . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
B.9.5.17 p4_hm_panic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
B.9.5.18 p4_my_cpuid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
B.9.5.19 p4_kinfopage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
B.9.5.20 p4_my_uid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
B.9.5.21 p4_my_prio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
B.9.5.22 p4_kernel_get_thread_size . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
B.9.5.23 p4_kernel_preempt_point . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
B.9.5.24 p4_kernel_preempt_enable . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
B.9.5.25 p4_kernel_preempt_disable . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
B.9.5.26 p4_kernel_is_preempt_enabled . . . . . . . . . . . . . . . . . . . . . . . . . 209
B.9.5.27 p4_kernel_is_preempt_pending . . . . . . . . . . . . . . . . . . . . . . . . . 210
B.9.5.28 p4_kernel_in_irq . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
B.9.5.29 p4_kernel_notify_cpu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
B.9.5.30 p4_mem_get_attr . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
B.9.5.31 p4_my_thread_status . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
B.9.5.32 p4_kernel_tp2rp_list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
B.9.5.33 p4_kernel_get_mem_type . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
B.9.5.34 p4_kernel_get_global_info . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
B.9.5.35 p4_prop_get_kernel_boot_message . . . . . . . . . . . . . . . . . . . . . . . 218
B.9.5.36 p4_prop_get_kernel_log_level . . . . . . . . . . . . . . . . . . . . . . . . . . 219
B.9.5.37 p4_prop_get_kernel_num_respart . . . . . . . . . . . . . . . . . . . . . . . . 220
B.9.5.38 p4_prop_get_kernel_num_timepart . . . . . . . . . . . . . . . . . . . . . . . 221
B.9.5.39 p4_prop_get_kernel_num_cpu . . . . . . . . . . . . . . . . . . . . . . . . . . 222
B.9.5.40 p4_prop_get_kernel_num_prio . . . . . . . . . . . . . . . . . . . . . . . . . 223
B.9.5.41 p4_prop_get_kernel_num_task . . . . . . . . . . . . . . . . . . . . . . . . . 224
B.9.5.42 p4_prop_get_kernel_num_thread . . . . . . . . . . . . . . . . . . . . . . . . 225
B.9.5.43 p4_prop_get_kernel_num_mem_region . . . . . . . . . . . . . . . . . . . . . 226
B.9.5.44 p4_prop_get_kernel_thrinfo_size . . . . . . . . . . . . . . . . . . . . . . . . 227
B.9.5.45 p4_prop_get_kernel_ticker_mode . . . . . . . . . . . . . . . . . . . . . . . . 228
B.9.5.46 p4_prop_get_kernel_ns_per_tp_tick . . . . . . . . . . . . . . . . . . . . . . . 229
B.9.5.47 p4_prop_get_kernel_ns_per_tick . . . . . . . . . . . . . . . . . . . . . . . . 230
B.9.5.48 p4_prop_get_kernel_ns_tp_watchdog . . . . . . . . . . . . . . . . . . . . . . 231
B.9.5.49 p4_prop_get_kernel_tps_strong_sync . . . . . . . . . . . . . . . . . . . . . . 232
B.9.5.50 p4_prop_get_kernel_tptable_max_windows . . . . . . . . . . . . . . . . . . . 233
B.9.5.51 p4_prop_get_kernel_respart0_pages . . . . . . . . . . . . . . . . . . . . . . 234
B.9.5.52 p4_prop_get_kernel_test_flags . . . . . . . . . . . . . . . . . . . . . . . . . 235
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CONTENTS 9
B.10 Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 B.10.1 Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 B.10.2 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 B.10.2.1 drv_put_c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237 B.10.2.2 drv_put_s . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238 B.10.2.3 drv_put_x . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239 B.10.2.4 drv_put_xp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 B.10.2.5 drv_put_xx . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241 B.10.2.6 drv_put_xb . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242 B.10.2.7 drv_put_d . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243 B.10.2.8 drv_put_dd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244 B.10.2.9 drv_put_uid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245 B.10.2.10 drv_try_put_c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 B.10.2.11 drv_try_get_c . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 247
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Acronyms and Abbreviations
ABI Application Binary Interface API Application Programming Interface ASP Architecture Support Package BAT Block Address Translation CPU Central Processing Unit FIFO First In First Out IPI Inter Processor Interrupt I/O Input/Output MMU Memory Management Unit PSP Platform Support Package PSSW UniversalisOS System Software PTE Page Table Entry SMP Symmetric Multi Processing SOC System On Chip TLB Translation Lookaside Buffer VMIT Virtual Machine Initialization Table
Table 1: Acronyms and Abbreviations
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
1 Introduction to PSP Development
1.1 UniversalisOS Architecture
UniversalisOS is a real time operating system providing a software partitioning platform for safety critical applications and applications demanding a high level of security. The UniversalisOS partitioning allows the execution of multiple applications in a secure environment. Figure 1 illustrates the main concepts of a system architecture based on UniversalisOS. The important points shown by the figure are:
Figure 1: UniversalisOS Based System
• The UniversalisOS component consists of a kernel and a system software layer (PSSW)
• Each application runs in a resource partition
• Only the UniversalisOS kernel executes in supervisor mode
• Device drivers can be implemented at multiple levels: kernel (PSP), PSSW, user application
• The PSP is part of the kernel
1.1.1 UniversalisOS Kernel
The UniversalisOS kernel provides the following abstractions to higher level software layers:
• Virtual address spaces (tasks)
• Execution entities (threads)
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
12 Introduction to PSP Development
• Thread scheduling and time partitioning
• Inter-thread communication (IPC and events)
• Exception handling
Figure 2 shows the internal structure and architecture of the UniversalisOS kernel. The UniversalisOS kernel is composed of
Figure 2: UniversalisOS Kernel Architecture
three major components: the core kernel, PSP and ASP.
1.1.2 Kernel Core
This is the generic part of the kernel, which is independent of the underlying hardware platform and CPU architec- ture. Hardware and CPU architecture details are hidden from the kernel core by the PSP and ASP components.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
System Run-Time Overview 13
1.1.3 Platform Support Package (PSP)
The PSP encapsulates the details of the underlying hardware platform and provides a platform interface for use by the rest of the kernel. The PSP is responsible for:
• Platform hardware initialization
• Startup and shutdown
• Cache handling
• Tick timer handling
• Interrupt dispatch
• System console management
1.1.4 Architecture Support Package (ASP)
The ASP encapsulates the details of the underlying CPU architecture. The ASP is responsible for:
• CPU context management
• CPU exception management
• Address space and memory management
• Debugger support
1.2 System Run-Time Overview
Figure 3 shows the main execution phases, from the PSP’s viewpoint, at system run-time. At system boot, the PSP is the first UniversalisOS component to execute. Control is usually passed to the PSP entry point from the platform’s boot loader or a hardware debug tool. The main function of the PSP at this point is to initialize the hardware platform. This includes initialization of the CPU itself (caches, MMU) and creation of a virtual memory map. After performing hardware and software initialization, the PSP passes control to the kernel core entry point. During the kernel initialization phase, the kernel makes calls to a limited number of PSP entry points for installation of spurious interrupt handlers and initialization of the system ticker. After completing its initialization, the kernel starts the PSSW, which in turn launches user applications. At this point, platform initialization is complete and the system is running in normal operation. In this mode, PSP services are called via the following mechanisms:
• As a result of an application making a UniversalisOS service call request, the kernel core calls the PSP to fulfill
the service request.
• As a result of a CPU exception, the ASP calls the PSP to perform interrupt dispatch.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
14 Introduction to PSP Development
Figure 3: PSP Run-Time Overview
1.3 PSP Development Environment Overview
This section gives an overview of the PSP development process and its relationship to the other activities involved in configuring and building a UniversalisOS image for the target hardware. Figure 4 illustrates the different activities that comprise the UniversalisOS development process. The final output of this process is the UniversalisOS ROM image, the binary image that is downloaded and run on the target hardware.
1.3.1 PSP Development
The activities of this process are the main concern of this document.
1.3.2 Application Development
The application development process involves development of software using one of the APIs provided by UniversalisOS (UniversalisOS native API, POSIX, ARINC-653 etc.). Applications reside in resource partitions and execute exclusively in user mode. Device drivers can be implemented as user-level applications. (see Figure 1)
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Project Type 15
1.3.3 Integration
The main activities of the integration process are the definition of the contents of the ROM image and configuration of partitions. Through partitioning, the integrator controls what system resources (memory, I/O devices, CPU time) each user application can use and which applications are able to communicate and exchange data with each other. UniversalisOS ROM image generation is the final step in the overall development process, bringing together the different software components and assembling them into the ROM image binary file that can be downloaded and booted on the target system. The contents of this image are specified by the RBX file, which drives the ROM image generation tool, configconv. The RBX file also provides run-time configuration parameters for various components, including the PSP. PSP configuration parameters are covered in section 4.5. The integrator also selects the boot strategy. The BSP developer defines the available boot strategies for a particular platform.
1.3.4 UniversalisOS ROM Image
The ROM image is a composite file containing the binary applications to be started on the target, as well as configuration data, such as the property file-system. The ROM Root Element is the root of structured content inside a ROM image. All other objects in the image can be located from this element. A ROM image with a PSP-kernel binary image placed before the ROM header is considered a self-booting ROM Within the PSP-kernel binary image, a ROM anchor points to the ROM header. Figure 5 shows a typical ROM image layout. The PSP-Kernel binary is present only for bootable images. The property tree is a hierarchical set of properties used to configure applications, kernel and the PSP. The file system contains the PSSW executable binary, the VMIT, and application executable binaries.
1.3.5 UniversalisOS Projects
For each development activity (application, PSP, integration), the UniversalisOS development environment provides a specific project type. Each project type provides the following features:
• A set of shell scripts, Makefiles, headers, libraries necessary for development and building the software
component.
• A set of project configuration parameters. The developer can define additional parameters to the default
set.
• Demo projects. These are fully functional projects that can be used as the starting point for a custom
development.
1.4 PSP Project Type
The PSP project type provides the following PSP specific elements:
• C header files for inclusion in PSP source files. These headers define the interface with the UniversalisOS kernel
and ROM image.
• PSP library (in source form). A set of commonly used functions to facilitate PSP development.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
16 Introduction to PSP Development
• Build environment for PSP compilation, linking and installation, consisting of Makefiles and linker scripts.
• Demo projects in the form of reference UniversalisOS PSPs, including sources.
A custom PSP project can be created by cloning one the supplied demo PSP project. 2 provides a complete step by step tutorial on creating, building and running a new PSP.
1.5 When to Develop a New PSP
The output of the PSP development process is a binary file, referred to as the "PSP-kernel binary", which can be integrated into the UniversalisOS ROM image to be booted on the target hardware (see Figure 4). Configuration parameters allow a single PSP-kernel binary to support a family of related boards. The following features are configurable and therefore do not require development of a new PSP:
• DRAM size
• Addresses of I/O devices
• UniversalisOS console device parameters
The following features of a PSP are not configurable and thus require a new PSP in order to be changed:
• Physical memory layout
• Support for different system level devices (console, ticker, timer, interrupt controller)
Note: Many UniversalisOS device drivers (e.g. UART, Ethernet . . . ) can be written entirely as user level applications running in a partition. Such drivers do not, therefore, require any PSP development activity.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
When to Develop a New PSP 17
Figure 4: UniversalisOS Development Overview
Figure 5: Typical ROM Image Layout
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
2 PSP Development Tutorial
This chapter provides a step by step guide to creating and building a new PSP and using it in a UniversalisOS integration project. It also describes the various files that make up a PSP project and how they are used. Figure 6 shows the process of compiling the PSP source files in a PSP project, linking them in a Kernel Fusion project, and finally merging the kernel with other parts into a ROM image. In this process, three projects are involved: PSP project, Kernel Fusion Project, and the Integration project.
PSP Project
compile
PSP Sources PSP Object
PSP Components
Kernel Fusion Project
link
Kernel Core Binary
KDEV Driver Objs
Kernel Binary
KDEV Driver Cmps
Integration Project references
mkromimage
project.xml
PSSW, VMIT, ... ROM Image
Figure 6: Workflow for PSP development with the three projects that are involved. The PSP projects compiles the PSP and provides the component files for the options of the PSP. The kernel fusion project links the kernel core, the PSP, and the kernel drivers (KDEV). In the integration project, the component files are used to select the right binaries, reference the configuration, configure the project, and finally build the ROM image.
When developing a PSP, the projects will be cloned and modified, i.e., UniversalisOS has demo projects that provide a starting point for the development. There are demo projects for PSPs, for kernel fusion, and for integration projects. The tour will start in the PSP Project. An example PSP, assume an imaginary board named "Alpha10". Assume the PSP is based on the QEMU platform to allow it to be run without any special hardware. In the command line examples, the PATH environment variable is set to include the UniversalisOS command directory:
sh# export PATH=$PATH:/opt/universalisos-D5.0/bin
Note: The exact output of the commands may differ slightly, depending on the UniversalisOS products that are installed.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Creating a PSP Project 19
2.1 Creating a PSP Project
The new PSP is created by cloning an existing PSP demo project, which are installed in the directory /opt/universalisos-D5.0/demo/psp. Each PSP occupies a separate subdirectory. The new PSP is cloned from the "x86" PSP. This is a generic PSP supporting boards based on the PC-AT architecture.
sh# /opt/universalisos-D5.0/bin/universalisos-cloneproject --pool=/tmp/custompool --target=x86_amd64 /opt/universalisos-D5.0/demo/psp/x86-64 psp-alpha10 CLONING PROJECT ‘/opt/universalisos-D5.0/demo/psp/x86-64’TO ‘WORKSPACE /psp-alpha10’
Checking existing project /opt/universalisos-D5.0/demo/psp/x86-64 ... ok Checking new project WORKSPACE /psp-alpha10 ... ok Cloning project /opt/universalisos-D5.0/demo/psp/x86-64 as WORKSPACE /psp-alpha10 ... ok Initializing Development Project ... Selecting target ’x86_amd64’... Done. Validating ... Creating project.mk ... Creating UniversalisOS.sh ... Creating catalog file ... Done.
To work on your new project,type: sh# cd "WORKSPACE /psp-alpha10"
CLONING DONE.
The new project contains the following files, described in the following section:
sh# cd psp-alpha10/ sh# ls -R .: catalog.xml configure Makefile UniversalisOS.sh project.xml src config include makefile.defs project.mk README.demo ./config: psp.cmp ./include: board.h irqid.h pspfuncs.h pspstatics.h ./src: cboot.c cinterrupt.c ld-script.in
2.2 PSP Project Files
2.3 Customizing the PSP
At this point the new PSP is simply a clone of the original PSP. In general, the following steps are needed to adapt a PSP to a new board:
• Adjust the .cmp file to reflect the configuration options of the PSP. Paths to the compiled PSP binaries will
be set in the kernel fusion project. The PSP project only provides the configuration settings that can be
used in the integration project.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
20 PSP Development Tutorial
Directory File Description
. catalog.xml An index to all XSD files potentially used by the
project.
. configure Symbolic link to project configuration editor.
. Makefile Rules for building and installing the PSP.
. UniversalisOS.sh, Project configuration files. Edited using the project
project.config, configuration editor, these files should not normally
project.mk, be modified manually.
project.xml
src *.c, *.S Local PSP source files and linker script.
$PIKEOS_POOL_DIR/psp/src *.c, *.S Global PSP source files from libpsp.
$CUSTOM_POOL_DIR/psp/src *.c, *.S Custom PSP source files used by custom PSPs.
include *.h Local PSP header files
$PIKEOS_POOL_DIR/psp/include *.h Global PSP header files from libpsp.
$CUSTOM_POOL_DIR/psp/include *.h Custom PSP header files used by custom PSPs.
config *.cmp/*.dom Component (.cmp) and domain (.dom) files. These
files configure the PSP and are used by the Project
Configurator in the integration project where the
PSP is used.
Table 2: PSP Project Files
• Adapt the sources to the board. The adapted libpsp source files shall be placed to src or $CUS-
TOM_POOL_DIR/psp/src directories which have precedence before the $PIKEOS_POOL_DIR/p-
sp/src directory where are located unmodified libpsp files
• Adapt the linker input script.
This tutorial illustrates the first points. Adaption of sources and the linker script is described in detail in the PSP design chapters.
2.3.1 Modifying functions defined in the libpsp
Some PSP features can be provided by the libpsp. These features are enabled in the PSP project configuration. When enabled, the libpsp source files required for the PSP project compilation are copied in the “libpsp” subdirec- tory and can be modified from it. To be able to compile your changes, the libpsp header files must be copied (and adapted to your changes) manually in your project. Please copy the following items in the “include” directory of your PSP project:
• $PIKEOS_POOL_DIR/psp/include/psp_gen_svc.h file.
• $PIKEOS_POOL_DIR/psp/include/psp_$ARCH_svc.h file.
• $PIKEOS_POOL_DIR/psp/include/psp directory.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Building the PSP 21
2.3.2 Component Files
In the component files, the PSP stores information about how to configure the PSP. This information is used in the integration project to configure the driver. The exact format of the component files is detailed in the CODEO User Manual. It is the same as for any other UniversalisOS component. PSPs typically are configured either via the property file system . The examples provided as demos show the different possible component file configuration options.
2.3.3 Startup Message
The message identifying the PSP that is printed on the UniversalisOS console at startup is defined in the src/cboot.c source file. Edit this file and change the lines
psp_desc.psp_id = "...";
psp_desc.psp_welcome = "... " BUILD_ID;
to:
psp_desc.psp_id = "alpha10";
psp_desc.psp_welcome = "Alpha10 Board " BUILD_ID;
2.4 Building the PSP
The UniversalisOS.sh file contains shell commands to set up the cross-development environment for the PSP build tools. This file should be sourced at the start of each session or after reconfiguring the project.
sh# . UniversalisOS.sh STARTING UniversalisOS SESSION
Setting up CDK x86_amd64 $PIKEOS_BIN_PREFIX = x86_amd64- $PIKEOS_PROJECT = WORKSPACE /psp-alpha10 $PIKEOS_PROJECT_TYPE = psp $CC = x86_amd64-gcc $AS = x86_amd64-gcc -c $GDB = x86_amd64-gdb sh# which make /opt/universalisos-D5.0/bin/make
Build the PSP with the „make all“ command:
sh# make all
This builds the object files of the PSP that can then be installed in the custom pool. From there, the Kernel Fusion Project takes the object files to link a kernel binary.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
22 PSP Development Tutorial
2.5 Fusing the PSP with the Kernel
A UniversalisOS kernel binary consists of the following parts:
Kernel Core This contains the architecture specific core kernel in UniversalisOS. This part does not change when customizing a PSP, and is usually provided by Portugal Futurista. Thus, when building a custom PSP, this binary is typically taken from the global UniversalisOS pool.
PSP This is a single binary that contains the code that was compiled in the PSP Project. When building a custom PSP, this is taken from the custom pool after having been installed there by the PSP project.
Kernel Drivers Kernel drivers developed using the KDEV framework are separate binaries like the PSP and are also fused into a Kernel in the Kernel Fusion project.
The kernel core binaries are provided as part of the UniversalisOS product and are located in the PIKEOS_POOL. The PSP binary object is built in the PSP development project and installed into the CUSTOM_POOL. Standard KDEV driver binaries are provided as part of the UniversalisOS product and are located in the PIKEOS_POOL. Custom KDEV driver binaries are built in a KDEV development project and installed into the CUSTOM_POOL. A kernel fusion project is used to link kernel core binary, PSP binary and KDEV driver binaries into a single kernel binary. The following steps should be performed to integrate a custom PSP in a kernel fusion project:
• Create a kernel fusion project. UniversalisOS provides a kernel fusion demo project for each reference board. See
Appendix B in the BSP platform manual for the corresponding kernel fusion project or clone the project in
Codeo by clicking on "New Project: Kernel Fusion Project" in the "Related Template" of BSP configuration
component.
• The project contains a set of components for configuring build parameters, kernel core binary, PSP binary
and KDEV driver binaries.
• Edit the PSP component to use the custom PSP binary from the CUSTOM_POOL. By default, the compo-
nent is set up to use the standard PSP binary from the PIKEOS_POOL.
• Generate the fused kernel binary ("make all" command). Use the Verbose Compilation parameter to
check that the intended binaries are used in the link command.
• Install the fused kernel binary into the CUSTOM_POOL ("make install" command).
The kernel fusion project is also used to link the set of KDEV drivers, i.e. the selection of drivers for a given board. Note that the selection of drivers in the kernel fusion project does not mean that they are configured. Configuration is a separate step done in the integration project by adding configuration components, and modifying settings and configuration files. Drivers that are linked in the fusion project but not configured in the integration project are practically invisible in the system. They occupy some space in the binary image, but their code is not executed.
2.6 Configuring the PSP
Once the custom PSP has been built and linked into a custom kernel binary, an integration project is needed to generate the UniversalisOS ROM image that can be booted on the target. The following steps should be performed to use a custom kernel binary in an integration project:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Configuring the PSP 23
• Create an integration project. Select the same board as was used for the kernel fusion project.
• The BSP element contains the configuration components for PSP, kernel, drivers and System Software.
• Edit the kernel component to use the custom kernel binary from the CUSTOM_POOL. By default, the component is set up to use the standard kernel binary from the PIKEOS_POOL.
• If the PSP development project installed a PSP configuration component in addition to the PSP binary, remove the existing PSP component and add this custom PSP component from the CUSTOM_POOL.
• Build and install the ROMimage ("make install" command).
• The ROMimage can now be booted on the target board.
• You can verify the custom PSP is included in the boot image by reviewing the console boot messages. The custom PSP shall be identified by the following message indicating user, machine, build date and time:
PSP build: devel-<UID>@<machine>-<DDMMYY>-<HH:MM>
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
3 PSP Development Environment
3.1 PSP Personality
The PSP personality uses the common UniversalisOS makefile infrastructure, which provides many of the controls and rules used to build the PSP binaries. Some standard parameters, which are not applicable to kernel level code, are disabled. For example, activation of FPU usage. Depending on the processor architecture, a number of options allow selection of various features which can be built in to the PSP. For example, SMP support, PCI support.
3.2 Header Files
The psp.h file publishes the various data types, macros and function prototypes used in the interface between the PSP and UniversalisOS. The personality Makefile sets up include path to correctly locate the file. Header files for use with LIBPSP are described in the Appendices.
3.3 Project Outputs
PSP source files and any modules used from LIBPSP are compiled and partially linked into a single object file. A linker script is generated from the input file src/ld-script.in. The object file and linker script are installed into the CUSTOM_POOL for use in a kernel fusion project. The linker script is used in the fusion project to set the virtual address layout of the linked PSP-kernel object.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
4 PSP Component Design
4.1 Logical Structure
Figure 7 shows the prototypical modular design for a PSP. The actual structure of a real PSP will of course vary, depending on the features and organization of the underlying hardware and on specific requirements for the PSP.
Figure 7: PSP Logical Structure
Table 3 provides a brief summary of each module. Detailed description is provided in section 5
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
26 PSP Component Design
Module Description
BOOT The BOOT module is responsible for initializing the platform hard-
ware. It is the first UniversalisOS code to be executed and is normally
entered from a boot loader or hardware debug tool.
SERIAL The SERIAL module manages the device used for the UniversalisOS sys-
tem console. The device may support output only or input-output
functionality.
TICKER The TICKER module manages the timer device used as the basis
for the UniversalisOS system ticker.
INTERRUPT The INTERRUPT module manages the platform’s interrupt controller
and provides interrupt dispatch to registered interrupt handlers.
CACHE The CACHE module manages the platform’s cache hierarchy.
TIMEPART The TIMEPART module provides timer support and switch handling
for UniversalisOS time partitioning.
TIMEBASE The TIMEBASE module provides a timebase device for the UniversalisOS
system timebase.
PLATFORM The PLATFORM module provides support for target hardware con-
trol.
CPU The CPU module provides support for uniprocessor and multipro-
cessor platforms.
THREAD PSP hooks into user level thread operations.
Table 3: Prototypical Set of PSP Modules
4.2 PSP Statics
A PSP typically defines a global "statics" structure to hold any global data that needs to be shared between modules. The PSP statics should be located in a static data region (data or BSS section) and have external linkage.
4.3 PSP-Kernel Interface
Although linked into a single binary image, kernel and PSP do not call any function of the other part directly, except for the API functions defined in the psp.h header file. The PSP-kernel interface consists of the PSP_API and PSP_SVC interfaces (see Figure 7). The PSP_API interface consists of a set of entry points and data values published by the PSP to the kernel. The PSP_SVC interface consists of a set of entry points published by the kernel to the PSP.
4.3.1 PSP Descriptor
The PSP_API and PSP_SVC interfaces are instantiated in a single data structure of type P4_psp_descriptor_t defined in the psp.h header file.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP-Kernel Interface 27
The PSP descriptor data object should be located in a static data region (data or BSS section) and have external linkage. The address of the structure is passed to the kernel entry point, with the PSP_API part initialized. The kernel initializes the PSP_SVC part during its initialization sequence. Table 4 provides a summary description of the fields in this data structure. Detailed information is provided in the referenced module section.
Field Description Ref. Section api_version PSP API version. 5.1.5 arch Architecture specific fields. See Appendix for CPU ar- chitecture. max_interrupts Number of interrupt sources supported on platform. 5.2.4 align_mask Address alignment mask. 5.1.5 breakcon Flag to indicate break condition on console. 5.3.3 dyntick_res Resolution of ticker timer in dynamic mode. 5.4.1 dyntick_max Maximum interval of ticker timer in dynamic mode. 5.4.1 api PSP entry points. Unimplemented entry points See "Module Initialization" must be set to NULL. and "Entry Points" sections for each module. psp_id PSP ID string. 5.1.5 psp_welcome PSP startup message. 5.1.5 romheader UniversalisOS ROM image header. 5.1.5 ts_calibration Timestamp counter calibration unit. 5.1.5 ns_per_calibration Nanoseconds per timestamp counter calibration 5.1.5 unit. num_cpu Number of available CPUs. 5.9.1 cpu_info CPU information on multiprocessor systems. 5.9.1 num_devices Size of PSP driver callback table. 6.3.2, 6.3.11 haltmode Mode in which the module was shut down before Table 13 this boot cycle. dev_call_table PSP driver callback table 6.3.2, 6.3.11 service Kernel service entry points. 6.4
Table 4: PSP Descriptor Fields
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
28 PSP Component Design
4.3.2 PSP Entry Points
The PSP entry points are declared in the api member of the PSP descriptor. Certain entry points are mandatory and must be implemented by all PSPs. Other entry points are optional, and are implemented depending on the features supported by the PSP. Detailed descriptions of each entry point are given in the corresponding module design sections.
Note: Entries for optional entry points in the api structure must be set to NULL if they are not implemented by the PSP.
Execution Context
Except as otherwise stated, PSP entry points are called with interrupts and preemption disabled and entry points should not change this state. PSP entry points should not use more than 256 bytes of stack (excluding any calls to kernel interrupt handler functions).
4.4 Operating Modes
The life-cycle of a PSP can be divided into a number of distinct phases, each with its own execution context and expected behavior.
4.4.1 Early Initialization
This is the first code executed by the PSP, called from a boot loader or hardware debug tool. On entry, the CPU state is essentially unknown. The first task of the PSP is to initialize the CPU to a known state, create an initial virtual memory map and context to allow execution of the PSP-kernel C code. Early initialization is written in assembly language and is performed by the BOOT module, described fully in 5.1.
4.4.2 Main Initialization
This is the main platform initialization phase. Hardware components are initialized to a known state, additional memory mappings may be created, and the interface with the kernel core is initialized. At the end of this phase, the PSP passes control to the kernel core. Main initialization is performed by the BOOT module, fully described in 5.1.
4.4.3 Kernel Initialization
On receiving control from the PSP, the kernel core performs its own initialization. The kernel core calls a restricted set of PSP entry points during this stage:
• cnsput
• intdefault
• ticker or dyntick_init
• get_time
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Configuration 29
4.4.4 Normal Operation
After kernel core initialization, the PSSW and applications are started and the system is running in normal operation mode. PSP entry points are called via the mechanisms described in 1.2.
4.5 PSP Configuration
4.5.1 Property File System
Kernel and PSP is configured throught properties stored in the property file system, which is included in the ROMImage. It is recommanded that PSPs use the kernel functions like p4_prop_find (section B.9.5.5, page 186) and p4_prop_find_or_panic (section B.9.5.6, page 188). Please note that it is important that the kernel is initialized using the p4_early_init function before calling the or_panic versions of the function. This initialization is needed so that kernel can provide basic error reporting. If the PSP is not able to initialize kernel that early, it must do its own error reporting. The properties readable by p4_prop_find are configurable in the RBX (or indirectly in the project configurator) using syntax like this:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
5 PSP Module Design
Note: This section describes the design of the prototypical set of PSP modules. In a particular PSP design, the actual set of modules may differ and the allocation of functionality to the different modules may vary. However, it is important that the requirements for each module described here, particularly initialization and mandatory entry points, are always implemented somewhere in the PSP.
5.1 BOOT - Platform Initialization Module
5.1.1 Initialization Phases
Early Initialization
The early initialization code, written in assembly, is highly dependent on the CPU architecture and board charac- teristics and is only described in general terms here. Specific guidelines for each CPU architecture are given in the corresponding Appendix. In general, the early initialization performs the following steps:
• Initialize the boot CPU to supervisor mode with full access to all needed CPU features and the correct
register width. On multiprocessor systems, only the boot CPU should be initialized. Other CPUs will be
started by the kernel using the start_cpu() PSP callback.
• Put the platform into a state where interrupts cannot be taken. Depending on the platform, this may involve
setting devices such as watchdog and free-running timers. Interrupts should be disabled on the CPU.
• Initialize the MMU with a set of mappings and enable address translation. Prior to creation of initial
mappings, the code should take care when using linker generated addresses, which are virtual addresses.
They should first be converted to the appropriate physical address. Creation of the virtual memory map is
described in 5.1.2 and 5.1.3.
• Enable the cache hierarchy
• Initialize the data and BSS sections. Described in 5.1.3
• Create C code execution context. Described in 5.1.3
• Pass control to main PSP initialization code
Main Initialization
• Perform minimal initialization of the PSP descriptor
• Call the kernel’s p4_early_init() function to enable kernel service functions
• Create any other mappings required by the PSP not already created by the early initialization code.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 31
Note: The PSP is only required to create mappings for I/O devices that are accessed by the PSP itself
(interrupt controller, timers, console device etc.) and devices accessed by KDEV drivers. Mappings for
devices accessed by user level drivers are created by the kernel, not the PSP.
• Initialize PSP modules. Modules used by other modules (such as the serial and interrupt modules) should be initialized first. Initialization of each module is described in the corresponding module design section
• Initialize the PSP-kernel interface in the PSP descriptor
• Pass control to the kernel core (p4_main())
Secondary Initialization
Secondary PSP-specific board initialization can be performed during system startup in the context of the idle thread, but before other processors are started. Secondary init code can be provided via the optional api.init() function. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory during this phase.
Per-CPU Initialization
Additional per-CPU-specific initialization can be performed in the context of the idle thread running on each CPU. This per-CPU initialization can be provided via the optional api.init_cpu() function. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory during this phase.
Late Initialization
Late PSP-specific board initialization can be performed in the context of the idle thread on the first CPU shortly before creating and scheduling the first user space threads. This per-CPU initialization can be provided via the optional api.init_late() function. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory during this phase.
5.1.2 UniversalisOS Virtual Memory Map
On boot, the UniversalisOS ROM image will be located somewhere in DRAM or in a ROM device, such as Flash. The PSP is responsible for initializing the MMU to create an appropriate virtual address space for the PSP-kernel binary, which is linked against virtual addresses, and repositioning the PSP-kernel code and/or data segments such that the code and data appear at the expected virtual addresses. A platform will typically have a variety of hardware resources such as DRAM, Flash, I/O devices, bridges and busses that appear on the CPU memory bus. The PSP must create mappings for any hardware resources that will be accessed from the PSP during initialization or runtime, such as interrupt controller, serial console device, hardware timer, and devices managed by PSP level device drivers. The PSP must provide a description of all platform resources and their memory mappings to the kernel. Figure 8 shows prototypical physical and virtual memory maps for UniversalisOS.
Note: The Flash and I/O regions are shown here for illustration only. They (and other regions) may or may not be present on an actual platform and their addresses and size will certainly be different. For consistency, the addresses and sizes shown here are used throughout the illustrations.
Figure 8 illustrates the following important points concerning the UniversalisOS virtual memory map:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
32 PSP Module Design
Figure 8: UniversalisOS Physical and Virtual Memory Maps
• The virtual address space is divided into two regions:
1. The 2GiB kernel address space, starting at 0x8000.0000. This address space is only accessible from
supervisor mode. Mappings in the kernel address space are established by the PSP during platform
initialization, and are never changed.
2. The currently active user task occupies the lower 2GiB of the virtual address space. Mappings (not
shown here) will exist from the user address space to pages in RAM and to any other resources the
application has mapped. The user space mappings are changed when the kernel performs a task
switch. The user address space is also accessible from supervisor mode. Application code can only
access addresses within the user address space.
• The entire platform DRAM is linearly mapped into the kernel address space, starting at 0x8000.0000. This
allows a simple translation between virtual and physical addresses in this region:
phys_addr = virt_addr - 0x8000.0000
virt_addr = phys_addr + 0x8000.0000
• Mappings have attributes for access type and caching behavior.
It is the responsibility of the PSP developer to decide the layout of the memory map in the kernel address space. The following general constraints apply:
• DRAM must be linearly mapped to virtual address 0x8000.0000 (KMEM_BASE).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 33
• The maximum DRAM size supported by UniversalisOS is CPU dependent.
• Mappings in the kernel address space must be valid from supervisor mode only.
• DRAM must be mapped read-write (and executable for regions containing PSP-kernel code)
• The PSP must not map anything to the last 2 MiB of the virtual space as this area is used by the kernel for
the special purposes such as per CPU storage, kernel info page, exception vector page.
CPU architecture specific details are described in the corresponding Appendix.
5.1.3 Virtual Memory Map Initialization
One of the early PSP tasks is to create the virtual to physical mappings for the PSP-kernel binary. The PSP must create a set of virtual to physical mappings for system DRAM and for any other resources that it needs to access, such as I/O devices or Flash. Depending on the CPU architecture, the PSP can create mappings using TLBs, page tables or block translation, within the constraints described in the architecture specific Appendix. As all addresses generated by the linker in the PSP-kernel binary are virtual addresses, the PSP early initialization code must take care when referencing any symbols before the MMU has been initialized. These references must be converted to a physical address. Figure 9 illustrates the static load map and PSP run-time DRAM usage when a UniversalisOS ROM image is booted from DRAM. The example assumes that the UniversalisOS ROM image is loaded into DRAM at the expected physical load address, i.e. at the physical address corresponding to the text segment virtual address using the translation formula described in 5.1.2. So, for the default linker script:
SECTIONS { TEXT_RODATA_AT(0x80020000) DATA_BSS_AT(0x80004000) DISCARD }
the expected physical load address is 0x8002.0000 - 0x8000.0000 = 0x0002.0000 If the ROM image is not located at the expected physical load address, the PSP must first copy the image. The PSP run-time DRAM usage on the right side of Figure 9 shows the RAM regions initialized by the PSP:
• Data (copy): final location of PSP-kernel data section
• BSS: PSP-kernel BSS section
• Stack: temporary stack used by PSP-kernel during initialization
• Page tables: temporary page tables used by PSP to create initial mappings to certain physical resources.
If supported by the CPU architecture, the PSP can alternatively create mappings using a block translation
mechanism, in which case these page tables are not necessary.
The initialization of each of these regions is fully described below.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
34 PSP Module Design
Figure 9: Static and Dynamic RAM Usage (physical memory map)
Mapping DRAM
The PSP must create a linear mapping of platform DRAM into the kernel address space, starting at virtual address KMEM_BASE (0x8000.0000). This mapping must be valid in supervisor mode only and is normally cached. Typically, the early initialization assembly code creates a mapping for the DRAM region containing the PSP-kernel image and then mappings for any other DRAM are created later by the main initialization C code. The ASP may impose restrictions on what MMU mechanisms the PSP should use to create DRAM mappings. See the Appendices for details. Figure 10 illustrates the resulting virtual memory map, with symbols declared in assembly code and generated by the PSP linker script listed on the right. These symbols are declared for use in PSP C code in the linker.h and cmisc.h.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 35
Figure 10: PSP Virtual Memory Map
C Execution Context
A stack should be allocated in early initialization code after initializing the MMU but before executing C code. The stack must be large enough for the PSP’s needs and have at least 1KiB free space when the PSP passes control to the kernel. The stack is temporary and the memory will be reclaimed by the kernel once the kernel has set up its own stack. The PSP must place the stack in an available region of DRAM that is already mapped into the kernel virtual address space by the early initialization code. A typical choice is to place it just below the ROM image, as illustrated in Figure 10, if there is sufficient space after the BSS section. The PSP must initialize CPU registers as required by the architecture specific C ABI. Refer to Appendices for details.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
36 PSP Module Design
Data and BSS Section Initialization
The PSP must copy the data section from its position immediately following the text section in the ROM image to its final location in the kernel virtual address space. The linker generated virtual addresses _fdata and _edata define the start and end of the data section respectively (see Figure 10). The _fdata address will correspond to the address argument (0x8000.4000 by default) used for the DATA_BSS_AT() macro in the PSP linker script. The size of the section is calculated as (_edata - _fdata) bytes. The symbol _etext corresponds to the end of the text section, and hence the start of the data section in the ROM image and can be used as the source address for the copy. The copy should be done in early initialization after setting up the virtual memory map but before executing any C code. The PSP must fill the BSS region with zeros. The linker generated virtual addresses _bss_start and _end define the start and end of the BSS region respectively (see Figure 10). The size of the section is calculated as (_end - _bss_start) bytes. The utility function init_data_bss() can be used to initialize the data and BSS sections.
Mapping Other Hardware Resources
Any hardware resources that will be accessed during the PSP initialization phase must also be mapped into the kernel device space. Typical examples include the interrupt controller, serial console device, Flash memory. The PSP can use whatever mechanisms are provided by the CPU architecture. DRAM for page tables can usually be allocated in the high-end of DRAM (see Figure 10). These tables are temporary, the memory will be reclaimed by the kernel once the kernel has set up its own page tables. If the CPU requires software assistance for handling TLB misses, the PSP must install handlers before accessing mapped regions.
5.1.4 Booting from Flash
When booting from Flash, the PSP-kernel text section must be linked at the virtual address where the Flash is mapped (plus a load offset), as shown in Figure 11. The data and BSS sections must always reside in writable DRAM, so the link address for these does not change. Example PSP-kernel linker script for Flash boot:
SECTIONS { TEXT_RODATA_AT(0xe0020000) DATA_BSS_AT(0x80004000) DISCARD }
During PSP development and testing, it is convenient to be able to load and boot the Flash ROM image directly in DRAM without having to change the address in the linker script each time. To suport this, the PSP startup code must detect if the image is being booted from DRAM or from Flash and create mappings accordingly. The virtual map is always the same, but the mapped physical address for the ROM image changes, as shown in Figure 12. The load address offset (i in figure) from the base of DRAM or Flash to the ROM image must be the same in both cases.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 37
Figure 11: PSP Virtual Memory Map for Flash Boot
Note: As can be seen from the DRAM boot mappings on the left side of Figure 12, the Flash device is no longer mapped into the kernel address space. If the PSP needs to access the Flash device, it must create alternative mappings for the device in the DRAM boot scenario.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
38 PSP Module Design
Figure 12: Memory Mappings for Dual DRAM/Flash Boot
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 39
5.1.5 PSP-Kernel Interface Initialization
Different Memory Resources
In UniversalisOS, a PSP is responsible to set up the mappings of kernel virtual address space. These mappings usually comprise all usable RAM, but also I/O resources needed by the PSP, e.g. mappings to memory mapped I/O devices like interrupt controllers or timers. Depending on the type of memory resource (e.g. free RAM, used RAM like the memory where the UniversalisOS ROM image is loaded, or I/O resources), the kernel and PSP APIs offer different interfaces to register these memory resources. In general, the PSP provides to the kernel a description of all free system memory. The kernel provides various memory allocators for later use. Also, addresses of mappings to I/O resources might be of interest to KDEV drivers. Here, the PSP collects these I/O mappings for later use.
Free System Memory
To declare free system memory for later allocation, the kernel offers the p4_kernel_assign_mem() service. This service accepts a physical memory region with a given size. With each call to p4_kernel_assign_mem(), a PSP assigns free system memory to the kernel’s boot memory allocator. This assigned memory is immediately available for allocation with the p4_kernel_balloc() service. If multiple regions are assigned, the kernel will perform basic checks of the assigned memory and will panic if overlapping memory regions are detected. Also, the kernel tries to merge adjacent memory regions so that larger allocations will be possible. A PSP must at least assign one region of memory.
Note: The user has the capability to request physical partitioning of the system memory in the configuration with the use of memory regions. These user-configured memory regions are different from the memory regions defined by the PSP, but the different physical memory ranges identified by PSP and user must be in agreement. Specifically, the user-configured memory regions must be a subset of the PSP-defined memory regions, as the kernel will allocate the user-configured memory regions from the PSP-defined memory regions. See the UniversalisOS User Manual and the Kernel Reference Manual for more information on the Physical Memory Partitioning.
Temporarily Boot-Time-Reserved System Memory
At boot time, the PSP may need some reserve memory regions for internal use, e.g. boot stacks, or temporary page tables to start the system. However, this memory is only used during the boot phase and could be become free memory after startup. To handle such temporariy boot-time-reserved system memory, the kernel provides the p4_kernel_assign_tmp() service. This service collects up to four memory regions for later reclaiming by the kernel. Again, the kernel performs some basic checks, e.g. if these temporary memory regions overlap with other free system memory.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
40 PSP Module Design
Fragmented System Memory
On top of the kernel’s p4_kernel_assign_mem() service, LIBPSP provides the psp_free_list interface to handle fragmentation of physical memory blocks. Using a free list, a PSP can cut holes into a given physical memory regions. This is often necessary when a boot loader reserves parts of the memory for special purposes, e.g. start-up code of other processors. In this case, the PSP should set up a free list comprising an array of type psp_free_list_t locally on the stack or in the data segment. The size of the array should be large enough to handle dedicated entries for each free sub block in case of the maximally expected fragmentation. The free list is initialized with the psp_free_list_init() service, and a block of free memory is added to the free list with psp_free_list_add(). Then using psp_free_list_reserve(), small parts of the memory can be reserved (cut) from the added memory. Any remaining memory can then be assigned to the kernel using psp_free_list_assign_kernel(), which calls p4_kernel_assign_mem() internally.
Initialized System Memory
The PSP does not need to register any initialized system memory to the kernel. The PSP should just ensure that initialized (and used) memory is not assigned to the kernel’s memory allocator.
ROM Images in System Memory
In most setups, UniversalisOS is booted with a single ROM image. This ROM image usually starts behind the PSP and kernel binary image. The kernel implicitly detects the ROM image used to boot the system. If additional ROM images are present, the PSP must tell the kernel about these ROM images, and the kernel publishes a list of known ROM image in the kernel info page. The kernel provides the p4_kernel_rom_publish() service to register additional ROMs.
Mappings to I/O Resources
The PSP is responsible to set up mappings to all I/O resources needed by itself and by KDEV drivers. To track the mapping of I/O resources, LIBPSP provides the psp_io_list interface. In the I/O list, each entry describes a single mapping comprising both physical and virtual address of an I/O resource, and the size of the mapping. The PSP should set up an I/O list comprising an array of type psp_io_list_t in the data segment. The size of the array should be large enough to track all I/O mappings in the system. The PSP should initialize the I/O list with the psp_io_list_init() service at early boot, and, each time an I/O mapping is created, then the PSP should call psp_io_list_add() to add the mapping to the I/O list. All existing mappings can be iterated with the psp_io_list_get() service. Also, LIBPSP offers a service to retrieve the kernel virtual address of a mapped I/O resource by the psp_io_list_phys_to_kernel() service. This function implements the api.io_phys_to_kernel() callback described in the kernel API.
PSP Descriptor Initialization
Table 5 describes the PSP descriptor fields that should be initialized by the BOOT module. Other fields are initialized by the relevant module and are described in the corresponding module design section.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 41
Field Description Remarks align_mask Architecture dependent virtual ad- The value should be set to 2**n-1 on systems with dress alignment required to pre- cache aliasing effects, or zero on unaffected systems. vent cache aliasing effects. If align_mask is not zero, the kernel aligns the virtual address of all mappings to the corresponding offset of the physical address in the memory range defined by align_mask. dev_call_table PSP driver table. See 6.3.2 and 6.3.11. num_devices Number of installed PSP drivers. See 6.3.11. api_version PSP API version The field should be set to P4_PSP_VERSION(P4_PSP_API_VERSION, P4_PSP_ARCH_VERSION). If the kernel and PSP have been built with incompatible versions, the kernel core entry point returns with an error. psp_id PSP ID string. The kernel prints the PSP ID on the console at startup. The ID string is also published to applications in the kernel kinfo page. The string length should be less than P4_NAMELEN. The string should have the format "<psp_id> <configuration_tag>", where <psp_id> identi- fies the PSP and <configuration_tag> identifies the build version. psp_welcome PSP startup message. The kernel prints the PSP welcome string on the console at startup. romheader Virtual address of ROM image The payload_offset field in the ROM anchor provides the header relative offset, in bytes, of the ROM image header from the start of the ROM image. arch Architecture specific fields. Description of architecture specific fields are given in the Appendices. ts_calibration Timestamp counter calibration. If the platform supports a user readable time stamp counter (TSC), the PSP should set this field to the num- ber of time stamp increments during the time interval ns_per_calibration. ns_per_calibra- Nanoseconds per timestamp Contains the time interval, in nanoseconds, to which the tion counter calibration unit. number of time stamp counter increments ts_calibration relates. If the platform does not support a time stamp counter, or the speed of the time stamp counter is not known, the PSP should set both ts_calibration and ns_per_calibration to zero.
Table 5: BOOT Module PSP-Kernel Interface Initialization
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
42 PSP Module Design
5.1.6 Invoking Kernel Core
The kernel core exports the p4_main() entry point. After PSP initialization is complete, the BOOT module should invoke the entry point, passing the address of the PSP descriptor as argument:
P4_psp_descriptor_t psp_desc; int ret; ... ret = p4_main(&psp_desc);
Under normal circumstances, the kernel core entry point does not return. A return indicates an error such as an incompatible PSP API version or unimplemented mandatory PSP entry point. In this case, the PSP should print a message on the console and halt the hardware platform. If the api_version field of the PSP descriptor differs from the compile time value, p4_main() returns P4_E_INVAL. If any of the mandatory API functions are not implemented in the api part of the PSP descriptor, p4_main() returns P4_E_NOENT.
5.1.7 Entry Points
init
void api.init(void);
The kernel calls this function to perform secondary PSP specific board initialization during system startup. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory. The function is optional. This function is called on the stack and in the context of the idle thread on the first processor, before other processors are started.
init_cpu
void api.init_cpu(void);
The kernel calls this function to perform per-CPU-specific board initialization during system startup. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory. The function is optional. This function is called on the stack and in the context of the idle thread on each processor.
init_late
void api.init_late(void);
The kernel calls this function to perform late PSP specific board initialization during system startup. The PSP can utilize p4_kernel_balloc() to allocate dynamic memory. The function is optional. This function is called on the stack and in the context of the idle thread on the first processor, shortly before the kernel creates and schedules user space threads.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
BOOT - Platform Initialization Module 43
io_phys_to_kernel
void *api.io_phys_to_kernel( P4_phys_addr_t phys, P4_size_t size);
At runtime, PSP or KDEV drivers call this function to look-up the virtual address of an already mapped physical I/O resource. The function is optional. To implement this functionality LIBPSP provides the according psp_io_list services which track I/O mappings in the PSP. The callback psp_io_list_phys_to_kernel() implements the required interface.
io_map_kernel
void *api.io_map_kernel( P4_phys_addr_t phys, P4_size_t size, P4_access_t access);
At system startup, PSP or KDEV drivers call this function to create a mapping to a physical I/O resource in the kernel virtual address space (or return the virtual address of an already existing mapping). The function is optional. A PSP implementing should implement this function to map I/O resources dynamically at runtime, e.g. to make PCI devices accessible to the kernel. Note that new mappings are only created at boot time in single processor mode, before other processors are started. LIBPSP might already implement a suitable a callback, based on architecture specific lower-level mapping func- tions and the psp_io_list services. See the corresponding Appendix for detailed description.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
44 PSP Module Design
5.2 INTERRUPT - Interrupt Handling Module
5.2.1 UniversalisOS Interrupt Handling Model
In contrast to many operating systems, where a kernel level device driver registers an interrupt service routine (ISR) to handle interrupts, UniversalisOS allows interrupt processing to be performed almost entirely from user space. Low-level interrupt handling (CPU context management) is done by the ASP and the PSP is responsible for management of the platform’s interrupt controller and dispatching interrupts to registered handlers. The device specific processing associated with an interrupt is done by the user level handler. Certain interrupts may be handled directly within the PSP itself. Typically, these are interrupts that require specific processing such as the ticker device or a system reset and cannot be handled at user level. Interrupt delivery to user level handlers is synchronous. A user level handler thread blocks waiting for an interrupt to occur. When the interrupt occurs, the thread is woken up. When no thread is waiting for an interrupt, the interrupt is masked. UniversalisOS supports interrupt sharing, where multiple user level handlers may attach themselves to the same interrupt source. When multiple handlers are attached, an interrupt source is only unmasked when all the handlers are blocked waiting for the interrupt. Chaining of the handlers is managed by the kernel core.
5.2.2 Interrupt Source and Interrupt ID
From the PSP viewpoint, an interrupt source is a device that can generate an interrupt. This may be an interrupt source on the CPU itself (e.g. PPC decrementer) or a platform level device such as a UART or network controller. An interrupt ID is a logical identifier used by software outside the PSP (core kernel, application code) to uniquely identify a particular interrupt source. The PSP defines the association between interrupt sources and interrupt IDs. Valid interrupt IDs are integers in the range 0 to num_interrupts-1. The value of num_interrupts is specified by the PSP though it must not exceed the maximum supported by the UniversalisOS kernel, P4_NUM_INTERRUPT. Not all interrupt sources need to have an associated interrupt ID. Interrupt sources that are handled entirely by the PSP and never made visible outside need not be included in the list of valid interrupt IDs. An example is a system reset interrupt.
5.2.3 Interrupt Handling Sequence
Figure 13 shows the typical sequence of events for a simple scenario where a single application handles interrupts for a particular interrupt ID.
1. The application calls p4_int_attach() to indicate its intention to handle interrupts for this interrupt ID.
2. As this is the first "attach" for this interrupt ID, the kernel registers its interrupt handler with the PSP
(inthandle() entry point).
3. The application performs an interrupt handling loop:
(a) The application calls p4_int_wait() to wait for the next interrupt. The kernel calls the PSP to unmask
the interrupt and then blocks the application thread.
(b) When the interrupt occurs, low-level processing is performed by the ASP, which then calls the PSP to
dispatch the interrupt.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
INTERRUPT - Interrupt Handling Module 45
Figure 13: Interrupt Handling Event Sequence
(c) The PSP masks the interrupt source and then calls the registered kernel interrupt handler.
(d) The kernel interrupt handler wakes up the blocked application thread.
-
The application calls p4_int_detach() to indicate its intention to no longer handle interrupts.
-
As this is the last "detach" for this interrupt ID, the kernel deregisters its interrupt handler by calling the PSP inthandle() entry point.
By calling the p4_int_attach_syscall(), when the first registration of an interrupt handler for an interrupt ID is performed, the userspace can provide to the PSP (to the inthandle() callback) an additional P4_int_mode_t parameter to request to the PSP a specific interrupt-controller configuration (e.g., P4_INT_MODE_EDGE_RISE, for edge-triggered interrupt on the rise front) for the interrupt ID. In the case of interrupt sharing, where multiple application threads handle the same interrupt ID, the sequence of events seen by the PSP is essentially the same. The inthandle entry point is called on the first attach and last detach. If implemented, the intshareable() entry point is called when a thread attaches to an interrupt ID where an
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
46 PSP Module Design
attach has already been performed. The intunmask entry point is called when all attached threads are blocked in p4_int_wait. If one of the blocked threads is woken up before the interrupt occurs (for example by a timeout), the kernel calls the intmask entry point to mask the interrupt again.
Note: Interrupt sharing on interrupt lines already managed by KDEV drivers is not supported. Attaching to interrupt lines already managed by KDEV may result in interrupts to be lost.
5.2.4 Module Initialization
On initialization, the INTERRUPT module should perform the following actions:
• Initialize the interrupt controller. All interrupt sources should be disabled and masked, and any pending
interrupts cleared.
• Initialize the PSP-kernel interface according to Table 6.
Field Mandatory Description Remarks max_interrupts yes Number of interrupt IDs supported Must be less than or equal to by the PSP. P4_NUM_INTERRUPT. api.intdefault yes PSP entry point to register default Called only during kernel initializa- interrupt handler. tion. api.inthandle yes PSP entry point to register/unreg- ister an interrupt handler. api.intmask yes PSP entry point to mask an inter- rupt source. api.intunmask yes PSP entry point to unmask an in- terrupt source.
Table 6: PSP-Kernel Interface Initialization
5.2.5 Entry Points
intdispatch
void api.intdispatch(P4_cpureg_t cause);
On occurrence of a CPU exception, low-level exception processing is first performed by the ASP. Depending on the cause of the exception, the ASP then calls the PSP intdispatch() entry point, passing as argument a CPU architecture dependent value indicating the cause of the exception. The list of exceptions forwarded by the ASP to the PSP for each architecture is given in the corresponding Appendix. The intdispatch() entry point performs the following actions:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
INTERRUPT - Interrupt Handling Module 47
• Determine the interrupt source. The argument passed from the ASP provides the first indication. In the
case of an external interrupt, the PSP will typically retrieve an interrupt vector from the interrupt controller
to further determine the interrupt source. The interrupt source is mapped to its corresponding interrupt ID.
• Mask further interrupts from the interrupt source. The code can call the intmask() entry point with the
corresponding interrupt ID.
• Perform any actions required by the interrupt controller to acknowledge the interrupt.
• Call the registered interrupt handler for the interrupt source. There will always be at least the default
spurious interrupt handler registered for all interrupt sources.
Note: On return from the registered interrupt handler, the entry point must not unmask the interrupt source. The kernel will determine when the interrupt source should be unmasked again and will call the intunmask() entry point. Certain interrupt sources, such as the ticker device, may require unmasking as part of interrupt processing. This is handled by the registered interrupt handler.
The PSP should perform the above steps in a loop until it determines that no further interrupts are pending.
intdefault
void api.intdefault(P4_inthandler_t handler, void *arg);
The kernel calls this entry point once during kernel initialization to install a default spurious interrupt handler for all interrupts. It is called before any interrupt sources are unmasked and before any user level handlers can be attached. The entry point should only register the kernel’s default handler and its argument for interrupt sources that do not already have a registered handler. This prevents internal PSP handlers installed during PSP initialization from being overwritten. This issue is discussed further in 5.2.7. The PSP should also save the default handler and argument so that they can be restored when a user handler is detached.
inthandle
P4_e_t api.inthandle(P4_intid_t intid, P4_int_mode_t mode, P4_inthandler_t handler, void *arg);
The inthandle() entry point is called to register and unregister a handler for an interrupt ID. The kernel calls this entry point on the first attach and last detach of a particular interrupt ID, as described in 5.2.3. On the first attach on an interrupt ID by an application, the kernel calls the inthandle() entry point to register an interrupt handler which will manage waking up the blocked threads and interrupt chaining. Other PSP modules may also call the entry point during PSP initialization to register their own handlers. In general, when registering a handler, the inthandle() entry point should not replace an already registered handler (except for the default handler registered via intdefault()). In particular, this approach will prevent user level handlers attaching to interrupt sources that are handled directly by the PSP itself, such as the ticker. The entry point should return P4_E_NOENT in such cases. This issue is discussed further in 5.2.7.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
48 PSP Module Design
The mode parameter of type P4_int_mode_t specifies the requested configuration for the interrupt controller (e.g., edge or level triggered interrupt). If a mode different from P4_INT_MODE_ANY is provided and the PSP returns P4_E_OK, it is assumed that the mode has been either honored (and the interrupt controller has been programmed in the specified way) or ignored (the specified mode was irrelevant for the hardware). Note that the flag P4_INT_MODE_FORWARD can be OR-ed with a mode to realize e.g., a virtual interrupt controller. If the mode is not supported by the interrupt controller, the inthandle() callback can return P4_E_MISMATCH. On the last detach, the kernel calls the inthandle() entry point to unregister its handler. When unregistering a handler, the entry point should replace the registered handler with the default handler registered via intdefault().
intshareable
P4_e_t api.intshareable(P4_intid_t intid, P4_int_mode_t mode);
When implemented, this optional callback is called to check if the interrupt intid is shareable. If implemented, the kernel will call this callback for every thread that performs a p4_int_attach() on an interrupt where an attach has already been performed (via the inthandle() callback). The PSP can control if threads can attach to an interrupt in a shared way by returning P4_E_NOENT or P4_E_MISMATCH respectively if the interrupt is not shareable or the mode is not supported (or the mode mis- matches with an already setup mode). Note that the flag P4_INT_MODE_FORWARD can be OR-ed with a mode to realize e.g., a virtual interrupt controller. If P4_E_OK is returned or if the entry point is not implemented, the kernel assumes that the interrupt can be shared.
intunmask
void api.intunmask(P4_intid_t intid, P4_cpuid_t cpuid);
The kernel and other PSP modules call this entry point when an interrupt source needs to be unmasked. If the interrupt ID and CPU ID are valid, intunmask() should unmask the corresponding interrupt source and bind it to the requested CPU. On uniprocessor systems, the CPU ID can be ignored.
intmask
void api.intmask(P4_intid_t intid);
The kernel and other PSP modules call this entry point when an interrupt source needs to be masked. If the interrupt ID is valid, the intmask() should mask the corresponding interrupt source.
5.2.6 Non-Maskable Interrupts
In general, non-maskable interrupts should be handled within the PSP and user level handlers should not be allowed to attach to these interrupts.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
INTERRUPT - Interrupt Handling Module 49
Certain non-maskable interrupts indicate unexpected conditions (e.g. PPC machine check interrupt) and require immediate processing within the PSP. Other interrupts may be non-maskable for practical reasons. For example, the PPC decrementer shares the same mask bit in the MSR as external interrupts. On certain PPC cores, it is not possible to mask the decrementer interrupt without also masking external interrupts.
5.2.7 Interrupt Handling within the PSP
Certain interrupt sources are handled internally by the PSP itself. These typically include the ticker, time partition timer, non-maskable interrupts and interrupts handled by PSP device drivers. These internal PSP handlers must not be replaced either by the kernel’s default interrupt handler (installed via intdefault()) or user level handlers (installed via inthandle()). A number of alternative designs are possible to handle this requirement:
• All interrupt sources are included in the set of valid interrupt IDs declared by the PSP. All internal inter-
rupt handlers are installed during the PSP initialization phase. During kernel intialization, intdefault() only
registers the kernel default interrupt handler for those interrupt sources that do not already have a handler
installed. During normal operation, inthandle() only registers a handler for interrupts whose current handler
is the kernel default handler. This provides a modular design - the interrupt module does not need knowl-
edge of internally handled interrupts and other modules can use the standard inthandle() interface. It may
also allow a straightforward mapping from interrupt source to interrupt ID.
• PSP handled interrupt sources are not included in the set of valid interrupt IDs declared by the PSP. The
kernel will never call the inthandle() entry point for interrupt IDs outside the valid range. The major draw-
backs of this approach are that the mapping of interrupt source to interrupt ID may not be as straightforward
and that the PSP must define a second, disjoint set of interrupt IDs for internally handled interrupt sources.
• PSP handled interrupt sources are not included in the set of valid interrupt IDs declared by the PSP and
internal interrupt sources are handled directly by the INTERRUPT module. This is a less modular design
but may be sufficient in some cases.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
50 PSP Module Design
5.3 SERIAL - Serial Console Module
5.3.1 System Console
The UniversalisOS system console is used to output log and diagnostic messages from applications and from UniversalisOS itself (PSSW and kernel). The device is used in a simple character-at-a-time, polled mode, interrupt support is not required. Applications and the PSSW can use a C printf() like service to print formatted strings to the console. A simpler API is available for use from within the kernel (including the PSP). Please note that the stack usage of printf()-like services is considerably higher than printing functions without formatting. Note that accessing the console may cause timing interference and therefore production PSPs should not use the system console after boot. The kernel does not access the console after boot except for printing information on kernel panics (see the api.hm_panic() callback). When printing, PSPs should comply with the verbosity level setup via the properties boot_message() and log_level() (refer to the Kernel Reference Manual for more information on the Kernel’s Parameters). The system console may also provide character input support, though this is optional. At a minimum, the serial module must provide two entry points:
• Console poll. Indicates whether the device is ready to transmit a character or characters are available for
reading. For a typical UART console device, this entry point checks whether there is space in the transmit
FIFO or charaacters in the receive FIFO.
• Console output. The entry point outputs a character to the device.
Figure 14 shows the sequence of events when an application calls the vm_cprintf() service to print a string on the console. For each character, the PSP cnspoll entry point is repeatedly called until the PSP indicates the output device is ready. The character is then output using the PSP cnsput callback.
Null Device Mode
The serial module must always provide console functionality. If the platform has no suitable hardware device, the console should behave as a null device. The system integrator may also configure the console to operate in null device mode. In this mode, the console acts as a data sink where output characters are simply discarded and as a data source that is always empty. Although not required, it is recommended that the serial module supports the null device mode.
5.3.2 Debug Port
To aid in platform development, debug versions of the UniversalisOS kernel are built with an internal debug stub, which communicates with the remote kernel debugger running on a development host over the debug port. To allow kernel debugging, the serial module must provide both input and output on the debug port. The debug port operates in the same character-at-a-time, polled mode as the system console. Though logically distinct, with a separate set of entry points, the debug port may share the same physical port as the system console.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
SERIAL - Serial Console Module 51
Figure 14: Serial Console Output
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
52 PSP Module Design
Path Type Default
PSP_CONSOLE_PORT uint32 mandatory
ID of port to be used for console device. A value of 0 indicates null device mode, if supported by the
module. Mapping of other values to physical ports is defined by the module and should be described in the
PSP documentation. If the port ID is invalid, the module should use a default port, described in the PSP
documentation. The default device may be the null device.
PSP_CONSOLE_INPUT uint32 mandatory
Flag to configure console input support A non-zero value requests input support on the console port. If
supported by the hardware device, the cnsget entry point should be set.
PSP_DEBUG_PORT uint32 mandatory
ID of port to be used for debug device. If PSP_DEBUG_PORT is 0, the debug port should be disabled.
Mapping of other values to physical ports is defined by the module and should be described in the PSP
documentation. If the port ID is invalid, the module should use a default port, described in the PSP doc-
umentation. The console and debug ports may share the same physical device. If the debug port is on a
separate physical device, it should use PSP_BAUDRATE.
PSP_BAUDRATE uint32 mandatory
Baud rate to be used for console and debug device(s). Mapping of property values to baud rates is defined
by the PSP and should be described in the PSP documentation. If the baud rate is invalid, the module should
use a default value, described in the PSP documentation.
Table 7: Standard location of configuration properties
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
SERIAL - Serial Console Module 53
5.3.3 Module Initialization
The serial module is typically one of the first modules to be initialized. This allows other PSP modules to print diagnostic messages on the console during start up. The serial module is configured by properties in ROMImage. The PSP can technically use any properties it wants, but for consistency a standard set of properties under the psp/ node should be used. On initialization, the SERIAL module should perform the following actions:
• Initialize the configured console output device to a state ready to transmit characters. If input mode is configured, initialize and enable the input device, otherwise, the input device should be disabled. Interrupts should be disabled. Note: If the console is configured in null device mode, the physical device should not be initialized, to prevent interference with other drivers handling the console. If the debug port is configured and it shares the same physical device as the console, the input device should always be enabled.
• If the debug port is configured, initialize the output device to a state ready to transmit characters and the input device to an enabled state. Interrupts should be disabled.
• Initialize the PSP-kernel interface according to Table 8.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
54 PSP Module Design
Field Mandatory Description Remarks breakcon yes Flag used to indicate condition to enter kernel The module should always set this debugger. field to 0 at initialization. api.cnspoll yes Entry point to poll the console port. api.cnsput yes Entry point to write a character to the console port. api.cnsget no Entry point to read a character from the con- Should be set if sole port. PSP_CONSOLE_INPUT is non- zero, otherwise set to NULL. api.dbgpoll no Entry point to poll the debug port. Should be set if PSP_DE- BUG_PORT is non-zero, other- wise set to NULL. All dbgpoll, dbgput and dgbget entry points are required in order to support kernel debugging. api.dbgput no Entry point to write a character to the debug Should be set if port. PSP_DEBUG_PORT is non- zero, otherwise set to NULL. All dbgpoll, dbgput and dgbget en- try points are required in order to support kernel debugging. api.dbgget no Entry point to read a character from the debug Should be set if port. PSP_DEBUG_PORT is non- zero, otherwise set to NULL. All dbgpoll, dbgput and dgbget en- try points are required in order to support kernel debugging.
Table 8: SERIAL Initialization
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
SERIAL - Serial Console Module 55
5.3.4 Entry Points
cnspoll
int cnspoll(void);
The cnspoll() entry point is called to determine whether there are characters available for reading and space available for writing on the console device. The return value is known as a console status word and it encodes the number of characters available in the re- ceive device and the space available (in number of characters) in the transmit device. The macro PSP_POLL_RE- TURN is used to construct the console status word:
P4_uint32_t console_status_word; console_status_word = PSP_POLL_RETURN(num_rx, num_tx);
If input mode is not enabled, the number of characters available for reading should be set to 0. In null device mode, cnspoll() should return PSP_POLL_RETURN(0, 255).
cnsput
void cnsput(int c);
The cnsput() entry point is called to write a character to the console device. The kernel always calls cnspoll() beforehand to determine whether space is available in the transmitter. The entry point should write the character to the transmitter device.
Note: The character is encoded in an int. In null device mode, the entry point should do nothing and return.
cnsget
int cnsget(void);
The cnsget() entry point is called to read a character from the console device. The kernel will always call cnspoll() beforehand to determine whether characters are available. The entry point should read the oldest character from the input device and return it, converted to an int. In null device mode, cnsget will never be called as cnspoll() always indicates that no charactes are available.
dbgpoll
int dbgpoll(void);
The dbgpoll() entry point is called to determine whether there are characters available for reading and space available for writing on the debug device and whether a request to enter the kernel debugger is present. The return value is known as a console status word and it encodes the number of characters available in the re- ceive device and the space available (in number of characters) in the transmit device. The macro PSP_POLL_RE- TURN is used to construct the console status word:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
56 PSP Module Design
P4_uint32_t console_status_word; console_status_word = PSP_POLL_RETURN(num_rx, num_tx);
The entry point must also check whether a break condition is present on the debug device and set the psp.breakcon flag accordingly. When a debug version of the kernel is being used, setting the flag to 1 will cause the kernel debugger to be entered.
dbgput
void dbgput(int c);
The dbgput() entry point is called to write a character to the debug device. The kernel always calls dbgpoll() beforehand to determine whether space is available in the transmitter. The entry point should write the character to the transmitter device.
Note: The character is encoded in an int.
dbgget
int dbgget(void);
The dbgget() entry point is called to read a character from the debug device. The kernel will always call dbgpoll beforehand to determine whether characters are available. The entry point should read the oldest character from the input device and return it, converted to an int.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
TICKER - System Ticker Module 57
5.4 TICKER - System Ticker Module
The PSP must provide a hardware timer that will be used as the basis for the system ticker. The ticker is a logical UniversalisOS timer used for:
• Application timeouts (e.g. p4_sleep())
• Time partition scheduling, if the PSP does not provide a dedicated time partition timer
• Maintaining the system timebase in software, if the PSP does not provide a hardware timebase
UniversalisOS supports two system ticker modes, dynamic ticker mode and periodic ticker mode. The kernel will use only one of the modes, selected by the configuration. The PSP must support at least one of the modes.
5.4.1 Module Initialization
On initialization, the TICKER module should perform the following actions:
• Install an interrupt handler for the ticker interrupt, which will be called via the PSP intdispatch() mechanism.
This can be done by calling the PSP inthandle() service with the corresponding interrupt ID.
• Initialize the ticker hardware to disabled state.
• Initialize the PSP-kernel interface as described in Table 9. The PSP can initialize the interface to support
both dynamic and periodic modes. The kernel will select a mode based on the configuration.
Field Mandatory Description Remarks api.ticker For periodic PSP entry point to initialize timer At least one of api.ticker and mode. in periodic mode. api.dyntick_int must be set. dyntick_res For dynamic Resolution, in nanoseconds, of mode. timer in dynamic mode. dyntick_max For dynamic Maximum timer interval in dy- mode. namic mode. api.dyntick_init For dynamic PSP entry point to initialize timer At least one of api.ticker and mode. in dynamic mode. api.dyntick_init must be set. api.dyntick_ex- For dynamic PSP entry point to program the pire mode. timer. api.dyntick_re- For dynamic PSP entry point to request timer mote mode on SMP reprogramming on remote CPUs. systems. api.get_time For dynamic PSP entry point to retrieve current The functionality of the TIME- mode and on system time. BASE module is required for dy- SMP systems. namic mode. See the TIMEBASE module section.
Table 9: TICKER PSP-Kernel Interface Initialization
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
58 PSP Module Design
5.4.2 Periodic Ticker Mode
In periodic ticker mode, the ticker hardware is programmed to generate an interrupt at a given frequency, specified by the configuration. The resolution of application timeouts (and possibly also time partition scheduling and system timebase) is determined by the ticker period.
api.ticker
int api.ticker(P4_time_t period_ns, P4_inthandler_t handler, void *arg);
The kernel calls this entry point to start the ticker. The entry point should perform the following actions:
• Enable the ticker hardware to generate an interrupt every period_ns nanoseconds.
• Register the handler and arg arguments for later use by the ticker interrupt handler.
• Unmask the ticker interrupt. This can be done by calling the PSP intunmask() service with the appropriate
interrupt ID.
• Return P4_E_OK.
If the PSP cannot start the ticker, for example, because the specified period is incompatible with the ticker hardware, the entry point should return P4_E_NOENT.
5.4.3 Dynamic Ticker Mode
This mode provides non-periodic ticker operation, with finer granularity timeouts. The ticker is programmed to generate an interrupt only when needed, i.e. when an application requests a timeout or a time partition switch must be scheduled. The minimum time interval supported by the ticker hardware (dyntick_res) defines the timeout resolution in dy- namic ticker mode. The maximum time interval (dyntick_max) is defined by hardware and software constraints (overflow). It is recommended to use a maximum time interval of one second.
api.dyntick_init
void api.dyntick_init(P4_inthandler_t handler, void *arg);
The kernel calls this entry point to start the ticker. The entry point should perform the following actions:
• Register the handler and arg arguments for later use by the ticker interrupt handler.
• Enable the ticker hardware to generate an interrupt after the maximum ticker interval and unmask the ticker
interrupt. This can be done by calling the PSP dyntick_expire() service with the corresponding interrupt ID.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
TICKER - System Ticker Module 59
api.dyntick_expire
void api.dyntick_expire(P4_time_t time_ns);
The kernel calls this entry point to request the ticker to expire and generate an interrupt at a given time on the current CPU. The expiration time time_ns represents an absolute time value in nanoseconds. If the time time_ns is in the future (i.e. greater than the current time), the entry point should perform the following actions:
• Reprogram the ticker hardware to generate an interrupt at the requested time.
Note: The ticker hardware may already be running. The PSP must take whatever steps are required by
the hardware to reprogram the timer. However, the kernel guarantees that time_ns will always be earlier
in time than the currently programmed expiration time.
• Unmask the ticker interrupt. This can be done by calling the PSP intunmask() service with the appropriate
interrupt ID.
Note: If the timer is running, the interrupt will already be unmasked.
If time_ns is in the past (i.e. less than or equal to the current time), the PSP must program the ticker to generate an interrupt as soon as possible. Note that, as the kernel disables interrupts before calling the dyntick_expire entry point, the interrupt cannot be taken during execution of the entry point itself. The kernel guarantees that time_ns is a multiple of the ticker hardware resolution published in dyntick_res. However, time_ns may be further in the future than the ticker hardware’s maximum interval. In this case, the PSP must emulate the requested interval by programming a series of sub-intervals. Each time the required interval to time_ns exceeds the maximum hardware resolution, the timer hardware is programmed with this maximum published in ticker_max. On the following interrupt, the kernel will reprogram the timer and this saturation to sub- intervals must be performed until the remaining interval to time_ns will fit the timer hardware resolution. The kernel represents time values with a 64 bit data type. Therefore, this is only an issue for timer hardware whose maximum interval is less than P4_UINT64_MAX nanoseconds. In the case that an interrupt arrived too early, the interrupt handler should recompute the new timeout to match the expected expiration time and apply this updated expiration time with a renewed call to dyntick_expire(), instead simply of rearming the timer hardware with dyntick_max.
api.dyntick_remote
void api.dyntick_remote(P4_cpuid_t cpuid);
The kernel calls this entry point to request a timer reprogramming on a different CPU than the current one. The entry point should send an IPI to the remote CPU cpuid requesting this CPU to immediately call the kernel ticker interrupt handler upon the reception of the IPI. When invoked on the CPU cpuid, the kernel ticker interrupt handler will call the PSP dyntick_expire() service to program the next expiration time on the CPU cpuid.
5.4.4 Ticker Interrupt Handler
The ticker interrupt handler must perform the following actions:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
60 PSP Module Design
• In periodic mode, rearm the ticker if required by the ticker hardware. If the timer rearms itself automatically,
this is not necessary.
• In dynamic mode, rearm the ticker hardware to generate an interrupt after the maximum ticker interval
dyntick_max, if the ticker interrupt was issued at the expected time. Or rearm the ticker with the expected
expiration time if the ticker interrupt was issued too early.
• Call the kernel ticker handler registered by the initialization entry point (ticker() or dyntick_init(). In dynamic
ticker mode, the kernel ticker handler calls the PSP’s dyntick_expire() entry point to reprogram the timer.
• In periodic mode, unmask the ticker interrupt. This can be done by calling the PSP intunmask() service with
the corresponding interrupt ID.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
TIMEPART - Time Partitioning Module 61
5.5 TIMEPART - Time Partitioning Module
5.5.1 Time Partition Switch Handling
The configuration of UniversalisOS time partitioning provides options to perform time partition synchronization with external time sources and to request cache and TLB invalidation on a particular time partition switch. If the TIMEPART callbacks are implemented, the PSP is responsible for performing the operations described by the callbacks.
5.5.2 Module Initialization
On initialization, the TIMEPART module should perform the following actions:
• Initialize the PSP-kernel interface as described in Table 10.
Field Mandatory Description Remarks api.tp_switch no PSP entry point to perform time partition switch operations. api.tp_sync no PSP entry point to perform exter- nal time partition synchronization.
Table 10: TIMEPART PSP-Kernel Interface Initialization
5.5.3 Entry Points
tp_switch
void api.tp_switch(P4_uint32_t id, P4_uint32_t flags);
The kernel will call api.tp_switch() on every time partition switch with the ID of the new time partition in "id" and the time partition switch flags in "flags". The entry point must evaluate the flags to determine which actions are necessary, as described in Table 11.
tp_sync
P4_time_t api.tp_sync(P4_time_t reference_time, P4_tp_sync_mode_t mode);
This PSP callback is used to synchronize time partition switching with an external timebase. It can wait for an event at which to synchronize time partition switching. If the PSP does not provide a synchronization function (api.tp_sync == NULL), then time partitions are scheduled according to the active time partition table. If the PSP does provide a synchronization function (api.tp_sync != NULL), then it can delay (by active waiting) the start of time partitioning upon time partition schema switch, or it can delay the start of a major window. The
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
62 PSP Module Design
Flag Description
P4_TPTABLE_FLAG_FIRST The kernel indicates the start of the time partition frame
by setting this flag on the first window in the frame.
P4_TPTABLE_FLAG_FLUSH_TLB Invalidate all TLB entries on the current CPU. The PSP
must invoke the p4arch_tlb_inval() callback provided by
the ASP. This ensures proper locking of TLB manage-
ment on some architectures.
P4_TPTABLE_FLAG_FLUSH_ICACHE Invalidate all instruction caches. The PSP can call its
inv_icache entry point.
P4_TPTABLE_FLAG_FLUSH_DCACHE Write out and invalidate all data caches. The PSP can
call its flush_dcache entry point.
Table 11: Time Partition Switch Operations
PSP can also modify the reference time used by the time partitioning kernel logic to the (possibly adjusted) value retrieved from the external timebase. The api.tp_sync() will be called from the TP interrupt handler at either a major time partition switch, and/or upon a time partition schema switch (depending on the configuration of the p4/kernel/tps_strong_sync kernel property). Both time partition schema switches and major time partition switches happen synchronously (within hardware timer jitter) on all CPUs available in the system. The api.tp_sync() callback is only invoked on the CPU that is marked as SyncCPU in the configuration of the time partition schema. The other CPUs busy wait in kernel for the callback to return.
Note: A synchronization of the time with the tp_sync() callback will take place at every time partition schema change on all CPUs.
Note: If tps_strong_sync is enabled (set to 1), the tp_sync() will be also called every major frame switch.
The types of a switch are identified via the "mode" parameter (see section B.2.5), which is set to "P4_TP_SYNC_INIT" on a schema switch, and to "P4_TP_SYNC_MAJOR" on a major time partition switch. The "reference_time" parameter contains the scheduled switch time of the ending TP window, or the current system time in the case of a time partition schema switch. The "reference_time" is used by the kernel to compute the start of the next time partition window. Via api.tp_sync(), the PSP may alter this value to return a corrected conceptual start of the current window that the kernel will use as a base for the subsequent switches. On SMP systems the kernel invokes this function only on one CPU (the "sync-cpu") while the other CPUs wait in kernel for the callback to return. The api.tp_sync() returns the system time at which the new time partition has conceptually started. If "refer- ence_time" does not need any adjustment, it should be returned unmodified.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
TIMEBASE - System Timebase Module 63
5.6 TIMEBASE - System Timebase Module
UniversalisOS provides a read-only system timebase to applications (p4_get_time() service). The timebase represents the time in nanoseconds. The resolution of the system timebase is determined by the resolution of the underlying hardware. The PSP should provide a coherent time across multiple CPUs. Specifically, the time must be globally monotonic increasing, and difference between possible per-CPU time base counters must not be observable by threads migrating across CPUs. The PSP must indicate if the time provided as current time is not measured since boot time.
5.6.1 Module Initialization
On initialization, the TIMEBASE module should perform the following actions:
• Initialize the timebase hardware, if necessary.
• Initialize the api.get_time field in the PSP-kernel interface.
5.6.2 Entry Points
get_time
P4_time_t api.get_time(void);
The entry point must return the current time in nanoseconds. Multiple invocation of the callback should provide a monotonic non-decreasing time value. Typical PSP implementations provide the current time starting from system boot. On SMP systems, the time provided by the function must be coherent across multiple CPUs. Differences between possible per-CPU time base counters must not be observable by threads migrating across CPUs.
get_ts
P4_time_t api.get_ts(void);
When implemented, this entry point returns the current value of the CPU’s time stamp counter.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
64 PSP Module Design
Figure 15: System Timebase
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CACHE - Cache Management Module 65
5.7 CACHE - Cache Management Module
The CACHE module is responsible for flushing and invalidating the cache hierarchy.
5.7.1 Module Initialization
Cache initialization is usually performed in assembly code by the BOOT module during the early initialization phase.
5.7.2 Entry Points
The system require three entry points. The first two entry points provide cache maintainance on memory regions in user space (cache_user()) or in kernel space (cache_kern()). The last entry point (cache_tps()) is invoked during time partition switches. Note that all cache operations on user space memory must be implemented in a preemptive fashion, as user space code might specify a large memory region. In contrast, cache operations on kernel memory must be implemented non-preemptively, as an interrupt routine in the kernel might call it.
cache_user
P4_e_t api.cache_user( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags);
A call to this function performs the following operations preemptively on the data and instruction caches for a memory region in user space, depending on op:
• P4_INVAL_ICACHE_RANGE Synchronize instruction cache content with data cache content for application
loading.
• P4_FLUSH_DCACHE_RANGE Write back and invalidate data cache content.
• P4_SYNC_DCACHE_RANGE Write back data cache content.
• P4_INVAL_DCACHE_RANGE Invalidate data cache content.
Depending on flags, cache content in the cache and memory hierarchy is affected differently by the operation:
• P4_CACHE_FLAG_CPU affects caches on current processor only.
• P4_CACHE_FLAG_DMA for cache coherency to DMA bus masters.
• P4_CACHE_FLAG_ALL affects all levels of the cache hierarchy.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
66 PSP Module Design
If flags is zero, P4_CACHE_FLAG_DMA applies. ¯ The operations P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE and P4_INVAL_DCACHE_RANGE affect content in the cache related to the memory region defined by start and size. Set alias to start for these operations. The operation P4_INVAL_DCACHE_RANGE requires a writable memory region and additionally writes back cache content at the beginning and the end of the memory area if the memory area is not properly aligned. The operation P4_INVAL_ICACHE_RANGE additionally handles potential aliases in the instruction cache in cross address space memory accesses. The operation uses alias to derive the addresses of potential instruction cache aliases in other address spaces. Set alias equal to start if only the caller’s address space is affected. The function is mandatory. However, the implementation of the different operations depend on the target architec- ture and the PSP implementation. On SMP, operations P4_INVAL_ICACHE_RANGE, P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE, and P4_INVAL_DCACHE_RANGE must affect all processors. Kernel code calls this function with preemption and interrupts enabled. If scheduling or interrupts must be prevented, the function should disable interrupts. This function is required to insert preemption points when cache operations lead to unbounded execution time. The memory region specified by start and size always refers to a mapping in user space. The kernel validates the memory region before calling this function.
cache_kern
P4_e_t api.cache_kern( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags);
A call to this function performs the following operations non-preemptively on the data and instruction caches for a memory region in kernel space, depending on op:
• P4_INVAL_ICACHE_RANGE Synchronize instruction cache content with data cache content for application
loading.
• P4_FLUSH_DCACHE_RANGE Write back and invalidate data cache content.
• P4_SYNC_DCACHE_RANGE Write back data cache content.
• P4_INVAL_DCACHE_RANGE Invalidate data cache content.
Depending on flags, cache content in the cache and memory hierarchy is affected differently by the operation:
• P4_CACHE_FLAG_CPU affects caches on current processor only.
• P4_CACHE_FLAG_DMA for cache coherency to DMA bus masters.
• P4_CACHE_FLAG_ALL affects all levels of the cache hierarchy.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CACHE - Cache Management Module 67
If flags is zero, P4_CACHE_FLAG_DMA applies. ¯ The operations P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE, and P4_INVAL_DCACHE_RANGE affect content in the cache related to the memory region defined by start and size. Set alias to start for these operations. The operation P4_INVAL_DCACHE_RANGE requires a writable memory region and additionally writes back cache content at the beginning and the end of the memory area if the memory area is not properly aligned. The operation P4_INVAL_ICACHE_RANGE additionally handles potential aliases in the instruction cache in cross address space memory accesses. The operation uses alias to derive the addresses of potential instruction cache aliases in other address spaces. Set alias equal to start if only the caller’s address space is affected. The function is mandatory. However, the implementation of the different operations depend on the target architec- ture and the PSP implementation. On SMP, operations P4_INVAL_ICACHE_RANGE, P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE, and P4_INVAL_DCACHE_RANGE must affect all processors. Kernel code calls this function with preemption disabled and interrupts are in an undefined state. If scheduling or interrupts must be prevented, the function should disable interrupts. This function must not have preemption points and is specified to be usable from interrupt handlers context or an early boot context. The caller must ensure that the specified memory region is small to prevent unbounded execution time. The memory region specified by start and size always refers to a mapping in kernel space.
cache_tps
P4_e_t api.cache_tps( P4_bool_t flush_dcache, P4_bool_t inval_icache);
Flush data cache and invalidate instruction cache on a time partition switch. A call to this function flushes the whole data cache if flush_dcache is set, and invalidates the whole instruction cache if inval_icache is set. The function is mandatory. The kernel calls this function with interrupts disabled. On SMP, this affects the caches on the calling processor only. An implementation should provide internal locking, if necessary.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
68 PSP Module Design
5.8 PLATFORM - Platform Management Module
The PLATFORM module is responsible for performing platform level operations such as reset and shutdown. The module entry points are called in response to explicit application requests or as a result of a decision taken by the UniversalisOS health monitor.
5.8.1 Module Initialization
The module should initialize the PSP-kernel interface according to Table 12.
Field Mandatory Description Remarks
psp.api.board_halt Yes Halt, reset, power off the system, or The functionality
stop the current CPU to halt the sys-
tem and stop the
current CPU are
mandatory to be
implemented
psp.api.machinecheck No Machine check exception handler
psp.api.hm_panic No Kernel panic handler
psp.api.hm_notify No Alert the PSP of a HM event
Table 12: PLATFORM: PSP-Kernel Interface Initialization
5.8.2 Entry Points
board_halt
void api.board_halt(P4_haltmode_t mode);
This entry point is called to halt, reset, and power off the system, or stop the current CPU. The invoked functionality depends on the selected halting mode in mode and if this halting mode is implemented. The supported halting modes can be retrieved from Table 13. The P4_PSP_HALT halting mode represents the fallback entry point for P4_PSP_RESET, P4_PSP_POWEROFF, and P4_PSP_ASSERT if these modes are not implemented. For the implementation of the api.board_halt() callback, it is required that it covers this fallback behavior explicitly and ensures that at least the mandatory modes are fully supported. The following code presents a simple examplary case for a minimum implementation that covers the two manda- tory modes (P4_PSP_HALT and P4_PSP_STOP) as well as the required fallback behavior.
/* Generic PSP-callback implementation example for api.board_halt */
void board_halt(P4_haltmode_t mode) { ...
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PLATFORM - Platform Management Module 69
Field Mandatory Description
P4_PSP_HALT Yes Halt the system if api.board_halt() is invoked with this mode
P4_PSP_STOP Yes Stop the current CPU if api.board_halt() is invoked with this mode
P4_PSP_RESET No Perform a system reset if api.board_halt() is invoked with this mode
P4_PSP_POWEROFF No Power off the system if api.board_halt() is invoked with this mode
P4_PSP_ASSERT No System halt triggered by assertions if api.board_halt() is invoked with
this mode
P4_PSP_HMRESET No Perfom HM-specific actions and a system reset if api.board_halt() is
invoked with this mode
Table 13: Supported halting modes of the api.board_halt() PSP-callback
switch (mode) {
/* Mandatory mode */
case P4_PSP_HALT:
...
/* Call internal function to halt the system */
system_halt();
break;
/* Mandatory mode */
case P4_PSP_STOP:
...
/* Call internal function to stop the current CPU */
cpu_stop();
break;
/* Fallback for optional modes */
default:
...
/* The fallback must cover all P4_haltmode_t options that
were not implemented. Here, e.g.,
P4_PSP_RESET, P4_PSP_POWEROFF, and P4_PSP_ASSERT */
...
/* Fallback: halt the system */
system_halt();
break;
}
...
/* Should not be reached. */
}
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
70 PSP Module Design
In the case of a P4_PSP_HMRESET triggered by the UniversalisOS Health-Monitoring, it might be required to consider the relevant status information of the initial Health-Monitoring event that led to this reset and perform selective reset functionalities depending on this status information. Therefore, the combination of the api.hm_notify() and api.board_halt() callbacks is recommended. Before the Health-Monitoring triggers actions via api.board_halt(), the api.hm_notify() callback is activated and provides the option to save the relevant information of the related Health-Monitoring event. The api.board_halt() implementation can perform a lookup for such information and perform selective actions related to prior Health-Monitoring events1 . The following code presents a simple examplary case for such an implementation.
/* PSP-callback implementation for notifications by the UniversalisOS Health-Monitoring */
void hm_notify(const P4_hm_info_t einfo) { ... / Save relevant status information from "einfo" for lookup in the board_halt() callback. */ ... }
/* PSP-callback implementation example for api.board_halt with HM-event checking */
void board_halt(P4_haltmode_t mode) { ... switch (mode) {
/* Mandatory mode */
case P4_PSP_HALT:
...
/* Call internal function to halt the system */
system_halt();
break;
/* Mandatory mode */
case P4_PSP_STOP:
...
/* Call internal function to stop the current CPU */
cpu_stop();
break;
/* Optional modes */
case P4_PSP_HMRESET:
...
/* Perform possible HM "einfo" lookup saved by hm_notify() */
do_lookup(einfo);
...
1 At the very least (if at all possible) the mode should be retained by the PSP and provided to the kernel via PSP Descriptor on the next boot up.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PLATFORM - Platform Management Module 71
/* Perform selective actions depending on "einfo" */
...
if(einfo != NULL) {
/* Call selective function to reset the system */
system_reset_with_hm_feedback(einfo);
}
/* Fall through to general system reset */
case P4_PSP_RESET:
/* Call general function to reset the system */
system_reset();
break;
/* Fallback for optional mode */
default:
...
/* The fallback must cover all P4_haltmode_t options that
were not implemented. Here: P4_PSP_POWEROFF and P4_PSP_ASSERT */
...
/* Fallback: halt the system */
system_halt();
break;
}
...
/* Should not be reached. */
}
machinecheck
P4_e_t api.machinecheck(P4_regs_t *regs, P4_cpureg_t cause, P4_phys_addr_t addr, P4_cpureg_t aux);
machinecheck() is the dispatcher function for machine check exceptions, called by the ASP when a critical excep- tion is detected. If machinecheck() is not implemented, the ASP panics the kernel with a runtime error and register dump. The argument regs encodes the current register context. Depending on the ASP, the arguments cause, addr, and aux encode further information on the exception cause. cause is an architecture value and typically encodes the exception vector. Both addr and aux refer to the address and an exception status register. The address in addr can refer to a virtual or physical address. The handler shall dispatch the exception and halt the system or modify the the supplied register context in regs to continue normal operation on return of this function. On unrecoverable errors, the function shall direcly halt the system. The kernel service p4_kernel_get_mem_type() can be used to retrieve the type of the physical memory region associated with the address addr. The system failure should be recorded in the system logbook, if implemented by the PSP.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
72 PSP Module Design
hm_panic
void api.hm_panic(P4_hm_info_t *einfo);
hm_panic() is called on runtime system configuration errors and kernel panics. hm_panic() should halt or reset the system. The system failure should be recorded in the system logbook, if implemented by the PSP. einfo contains health-monitoring related information about the error condition such as the register context at the time of the fault (which is NULL if no register context is available), and (if available) a textual description of the error. Please note that the HM message in einfo may not be NUL terminated. Details on the P4_hm_info_t structure can be found in B.3. hm_panic() is called on one CPU only, after having called all possibly registered KDEV driver callbacks, and after the hm_notify() callback. A CPU concurrently triggering a panic will not re-enter the hm_panic(), but will call the board_halt(PSP_STOP) callback. In most implementations, this callback will just spin, waiting for the CPU currently executing the hm_panic() to tear down all the other CPUs. When implemented, this callback must not return. When the callback is not implemented, the kernel will print information on the panic on the system console. When the callback is implemented, the kernel will not print any information on the console.
hm_notify
void api.hm_notify(const P4_hm_info_t *einfo);
hm_notify() is called to alert the PSP of any HM events. When implemented, the PSP will be notified of the Health-Monitoring events that are managed by the kernel’s health-monitoring subsystem. einfo is initialized by the kernel with the appropriate information on the currently managed error. In case of a kernel panic, einfo provides the compact identifier for panic context via its panic_cause_id element. This can be used to lookup the relevant context information from the documentation. See also the hm_panic() callback.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CPU - CPU Management Module 73
5.9 CPU - CPU Management Module
The CPU module is responsible for providing information on and managing the CPUs available on the platform.
5.9.1 Module Initialization
The module should initialize the PSP-kernel interface according to Table 14.
Field Mandatory Description Remarks api.idle No Idle the CPU api.start_cpu On SMP Boot other CPU systems api.reschedule On SMP Force rescheduling systems api.ipi_tlb_inval SMP spe- Send TLB-invalidation IPI to multiple cific CPUs api.ipi_poll SMP spe- Poll IPI state on CPU cific api.current_cpu On SMP Retrieve CPU ID systems num_cpu Yes Number of available CPUs This field should be set to the num- ber of CPUs on multiprocesor sys- tems or zero on uniprocessor sys- tem. cpu_info On SMP Assignment of nodes, sockets, The field is ignored on uniprocessor systems cores and threads of all available systems. CPUs.
Table 14: CPU: PSP-Kernel Interface Initialization
CPU IDs
On a uniprocessor system, the CPU has ID 0. On SMP systems, each CPU is identified by a unique ID, an integer in the range 0 to (num_cpu - 1). The boot CPU is always ID 0. The mapping of other IDs to CPUs is defined by the PSP.
CPU Information
The cpu_info field in the PSP descriptor is an array (size P4_NUM_CPU) of P4_cpu_info_t structures. On multipro- cessor systems, the information in these structures is used to calculate the "distance" between processors. The structure fields are described in Table 15.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
74 PSP Module Design
Field Description
node Index of the ccNUMA node.
socket Index of physical existing processor slots, sockets, or
packages.
core Index of physical core in one socket.
thread Index of SMT thread in a core.
Table 15: CPU: CPU Information Structure
5.9.2 Entry Points
idle
void api.idle(void);
The entry point should put the current CPU into a low power state until the next interrupt occurs. The kernel calls idle() with interrupts and preemption enabled.
start_cpu
P4_e_t api.start_cpu(P4_cpuid_t cpuid, void (*entry)(P4_cpuid_t));
Processors other than the boot processor are started by the kernel calling start_cpu() on the boot processor. start_cpu() should initialize CPU cpuid to call the function entry with cpuid supplied as argument. start_cpu() should return P4_E_OK on success. Any other return value will cause the kernel to panic.
reschedule
void api.reschedule(P4_cpuid_t cpuid);
The kernel calls reschedule() on a CPU other than cpuid in order to force a rescheduling on CPU cpuid. The entry point should cause a rescheduling to occur on CPU cpuid. Typically, this is done by sending an IPI to CPUs cpuid.
ipi_tlb_inval
void api.ipi_tlb_inval(P4_cpumask_t cpumask);
This function is used for TLB shootdown (remote CPU TLB invalidation) on some CPU architectures, e.g. x86. The kernel calls ipi_tlb_inval() during memory management operations. ipi_tlb_inval() should send an ASP specific TLB invalidation IPI to each CPU identified in cpumask. A CPU is identified in the mask by a set bit whose position corresponds to the CPU ID. In the IPI interrupt handlers, all remote processors should then invoke the p4arch_tlb_inval_handler() callback provided by the ASP.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
CPU - CPU Management Module 75
ipi_poll
void api.ipi_poll(void);
This function is used for TLB shootdown (remote CPU TLB invalidation) on some CPU architectures, e.g. x86. The kernel calls ipi_poll() to handle pending IPI requests while waiting for internal locks in the kernel. When a TLB invalidation IPI is pending on the processor, this function should invoke the p4arch_tlb_inval_handler() callback provided by the ASP.
current_cpu
P4_cpuid_t api.current_cpu(void);
current_cpu() should return the ID of the CPU on which the entry point is called.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
6 PSP Device Drivers
6.1 Overview
The term "device driver" is usually used to refer to a software component that manages a hardware device and provides application access to the device through an API. UniversalisOS provides three architectural possibilities for implementation of a device driver (see Figure 1).
1. A user level application running in a resource partition. The application threads run in user mode and are
scheduled according to the UniversalisOS thread and time partition scheduling rules. They are thus preemptable.
User level drivers cannot disable interrupts on the CPU. Interrupts are handled through the UniversalisOS interrupt
handling API. Memory mapped devices and I/O ports can be directly mapped into the application virtual
address space. A user level driver registers itself as a file provider allowing client applications access to the
device driver services through the UniversalisOS file system API. A user level driver can be configured in and out
of the system and can be parameterized using UniversalisOS properties.
2. A PSSW system extension module. A system extension runs in user mode inside the PSSW. A system
extension can synchronize execution with other application threads but cannot disable interrupts. Memory
mapped devices I/O ports and interrupts are accessed in the same manner as for user level device drivers.
A system extension registers itself as a file provider allowing client applications access to the device driver
services through the UniversalisOS file system API. A system extension can be configured in and out of the system
and can be parameterized using UniversalisOS properties.
3. A PSP device driver. A driver is implemented by a PSP entry point. As part of the PSP, the driver runs
in supervisor mode, can disable interrupts and can access dedicated kernel-services to enable/disable
preemption, and to perform locking on a multi-core platform. A PSP driver is always present and can be
parameterized using property file system.
Note: New designs based on PSP Device Drivers are deprecated. Specifically, the p4_dev_call() and vm_prop_dev_grant() system calls are deprecated and their removal is planned for future version of UniversalisOS. Kernel-level device drivers (KDEV drivers) should be used instead of PSP device drivers for new designs.
6.2 Why Use a PSP Device Driver?
• The PSP driver model is simpler than the more complex file provider and system extension, both in terms
of the driver design and the system configuration. This may be suitable when:
The required driver functionality and resulting design is relatively simple.
The driver will always be present in the system, and there is no need for it to be configurable via the
VMIT. Although the code is always present, the PSP can use a property to activate or deactivate the
driver.
• Hardware can not be sufficiently partitioned at user level, for example, SOCs where multiple device registers
share the same memory page.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Driver Design 77
• The control and data flow involved in accessing driver services is significantly simpler in the case of a PSP
driver. This may be an advantage in systems that require certification.
• Optimal performance is required. A PSP driver may provide better performance as:
Overhead to access PSP driver is less than for file provider and system extension.
PSP driver has full control over preemption and interrupts.
• Driver must perform operations with disabled interrupts, such as atomic sequences for CPU power control,
CPU speed changes.
• Device registers can only be accessed in supervisor mode.
• The hardware device is relatively non-intelligent (for example, little or no data buffering) and the cost of
handling each data item or interrupt at user level would be prohibitive.
The drawbacks of using a PSP device driver include:
• The driver runs in supervisor mode and must therefore be trusted code.
• Bugs and issues in the driver have the same criticality as bugs and issues in the kernel core. PSP device
drivers must be certified at the same level of the kernel.
• Misbehavior in the driver could lead to misbehavior in the kernel or in other trusted components.
• PSP device drivers are required to implement their own logic to protect internal critical section in order to
ensure deterministic system performance.
• The PSP driver API has limited features. Any other required functionality has to be implemented in the
driver or a client application.
A PSP level driver is often used in a split-architecture driver model where low level driver functionality is imple- mented in the PSP driver and higher level functionality in a user level driver.
6.3 PSP Driver Design
6.3.1 Entry Point
Each PSP driver is implemented by a PSP entry point. All PSP driver entry points have the P4_callback_t data type:
typedef P4_e_t (*P4_devcallback_t)(P4_devid_t devid, P4_uint32_t func, P4_cpureg_t arg1, P4_cpureg_t arg2, P4_cpureg_t arg3, P4_cpureg_t arg4);
The libpsp helper function register_dev_call()
void register_dev_call( unsigned int id, P4_devcallback_t callback);
allows registration of the PSP-entry point "callback" for the device ID (see 6.3.3) "id".
Note: The p4_dev_call() system call is deprecated and its removal is planned for future version of UniversalisOS. Kernel-level device drivers (KDEV drivers) should be used instead of PSP device drivers for new designs.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
78 PSP Device Drivers
6.3.2 PSP Device Driver Table
The dev_call_table field in the PSP descriptor points to the driver callback table containing the list of PSP driver entry points. Typically, each PSP driver is implemented by a separate PSP driver entry point. A PSP can also use a design where a common driver entry point manages multiple devices. In this case, the func argument can be used to encode the particular device being accessed. The number of entries in the table (num_devices) is determined by the PSP, but it must not exceed the value P4_NUM_DEVICE.
6.3.3 Device IDs
Each driver is identified by a unique ID, an integer in the range 0 - (num_devices-1), which is used to index into the driver callback table.
6.3.4 Granting Access
The application using the driver, must have granted access to the driver before the driver’s first invocation. The property filesystem shall contain the "prop_device" property:
Where "id" is the device ID number and "DEVICE_NAME" is string with the device symbolic name. The partition containing the application needs to have read and map permissions to this property and the access is granted by the following code:
vm_file_desc_t prop_fd; P4_devid_t id;
vm_open("prop:psp/driver", VM_O_RD | VM_O_MAP, &prop_fd); vm_prop_dev_grant(&prop_fd, "<DEVICE_NAME>" , &id);
Note: The vm_prop_dev_grant() system call is deprecated and its removal is planned for future version of UniversalisOS. Kernel-level device drivers (KDEV drivers) should be used instead of PSP device drivers for new designs.
6.3.5 Invocation
A PSP driver entry point is called in response to an application calling the p4_dev_call() function:
p4_dev_call(id, func, arg1, arg2, arg3, arg4);
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Driver Design 79
Figure 16: PSP Driver Invocation
The kernel uses the id argument to index into to the PSP driver callback table and calls the corresponding driver entry point, as illustrated in Figure 16. All p4_dev_call() arguments are passed unmodified to the driver entry point. The semantics of all arguments except the driver ID are defined by the driver itself.
6.3.6 Return Value
The driver entry point return value is passed directly to the calling application as the p4_dev_call() return code, without interpretation by the kernel. The driver should return P4_E_OK to indicate success and P4_E_NOTIMPL to indicated an unsupported func value. Driver specific error cases and associated return values should be described in the PSP user documentation.
6.3.7 Execution Context
The stack size of driver callback functions is limited to 1024 bytes. This includes calls to kernel service functions (see 6.4) which may use up to 512 bytes of stack. PSP driver entry points are called without any specific locking protection and critical sections within the driver should make appropriate use of synchronization primitives (e.g., spinlocks or atomics API). Furthermore, critical sections that require synchronization with interrupt handlers should disable/re-enable interrupts. Details on each entry point are provided in B.2. Drivers may access user space memory using the kernel provided copy functions (see B.3).
6.3.8 Preemption
PSP driver entry points are called with preemption disabled (and with possibly interrupt disabled as detailed in in B.2). Drivers should reduce execution time to a minimum in order to maintain the responsiveness of high priority threads. If long code sequences are nevertheless unavoidable, a PSP driver can request to enable and subsequently dis- able the preemption by invoking the preempt_point() service detailed in B.3. In this case, system responsiveness will be determined by the maximum time between two preemption points. Drivers can request preemption to be enabled to perform long-running operations that do not require access to any kernel services. The kernel service function p4_preempt_enable() can be used to enable preemption, and the function p4_preempt_disable() can be used to disable preemption before calling any other service function.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
80 PSP Device Drivers
The assert-enabled version of the kernel will trigger an assertion if the API is used from a context with incorrect preemption.
6.3.9 Device Access
To access memory or I/O mapped devices from a PSP driver, mappings must be created in the kernel virtual address space as described in 5.1.3. These regions should be included in the memory region table passed to the kernel in the PSP descriptor.
6.3.10 Interrupt Handling
A PSP driver can register an interrupt handler using the INTERRUPT module api.inthandle() entry point. Doing this during PSP main initialization will prevent a user application from being able to attach to the same interrupt source. Note: An interrupt handler registered in this manner will be called from the INTERRUPT module api.intdispatch() entry point. The execution context of this handler is not the same as the PSP driver entry points. Interrupts are disabled and should not be re-enabled by the handler. The handler should not use any kernel service, except for those that can be explicitly invoked from interrupt context (see B.3).
6.3.11 Initialization
PSP driver initialization should perform the following steps:
• Create memory mappings to any device that must be accessed during the PSP initialization phase.
• Perform any required hardware initialization.
• Install handlers for interrupts that will be handled at PSP driver level. This can be done using the
api.inthandle() entry point.
• Initialize the PSP-kernel interface according to Table 16.
Field Mandatory Description Remarks num_devices yes Number of entries in the driver Must be less than or equal to callback table. P4_NUM_DEVICE. dev_call_table yes Address of driver callback table. Unused entries in the table should be set to NULL.
Table 16: PSP-Kernel Interface Initialization for PSP Device Drivers
6.4 Kernel Services
Kernel services are available to PSP driver entry points during normal operation. Selected services are also available during boot time. The service structure in the PSP descriptor contains pointers to the kernel service callbacks. A listing of the structure as well as detailed descriptions of the callbacks can be found in B.3.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Services 81
6.4.1 Legacy Kernel Services
The following kernel services have been substituted by equivalent services in the KDEV framework or by equivalent functionalities directly exported by the kernel for PSPs and KDEV drivers.
Old Service New Service
service.copy_in See drv_memcpy_in
service.copy_out See drv_memcpy_out
service.preempt_point p4_kernel_preempt_point
service.preempt_restore p4_kernel_preempt_enable (new semantic)
service.preempt_disable p4_kernel_preempt_disable (new semantic)
service.current_cookie Functionality removed
service.current_uid p4_my_uid
service.current_prio p4_my_prio
service.mem_get_attr p4_mem_get_attr
service.memcpy_in See drv_memcpy_in
service.mem_create See drv_map_create
service.mem_build_sglist See drv_build_sglist
service.balloc p4_kernel_balloc
service.hm_raise p4_hm_raise
p4_psp_balloc p4_kernel_balloc
p4_kernel_get_stack_size p4_kernel_get_thread_size
Table 17: Legacy Kernel Services
Most of these services can be automatically updated by using the updater scripts provided with the UniversalisOS installation. The PSP-Level Blocking services have been removed and have to be substituted with the appropriate services provided by the KDEV drivers (e.g., blocking at gate level etc.).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
A Architecture Specific PSP Details
This Appendix describes PSP details specific to each architecture.
A.1 x86/amd64
This section describes PSP details specific to x86/amd64 based platforms.
A.1.1 PSP Descriptor Fields (P4_psp_arch_t)
Field Description
arch.boot_pml4 Physical address of the PML4 paging structure.
arch.phys_bits Number of physical address bits supported by the CPU
arch.cpu_features PSP defined bitmask of other CPU features.
arch.alt_features ASP/PSP defined bitmask of active ALT CPU features.
Table 18: x86/amd64 Architecture PSP Descriptor Fields
The merged CPU family and model follow standard rules for handling extended model and family. Most of the arch fields can be initialized using the libpsp function lateboot_cpu_detect(). The PSP needs to fill-in the arch.alt_features and the arch.cpu_features bitfields based on the required functionality and CPU capabilities. Please refer to the platform manual for the bit descriptions as they are also exported via the kernel info page.
A.1.2 Early Initialization
A typical sequence of steps performed by the early initialization code is:
• Copy data/ zero out bss
• Set up temporary page tables to map kernel address space in 64-bit mode
• Switch to the 64-bit mode
• Create C execution context
• Pass control to C code
• Set up final page tables to map kernel address space
The DR7 control register is set to zero in the non-debug PSP builds. It is set to zero during low level 32-bit CPU startup code which runs on all CPUs.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
x86/amd64 83
A.1.3 UniversalisOS Virtual Memory Map
The table below summarizes the ASP/PSP virtual memory map. The P4_MEM_KERN_BASE starts at 0xffff800000000000. This platform uses end of negative address space to map kernel code and data. The KMAP_BASE starts at 0xffffffffc0000000. The PSP requires one temporary code section identity mappings below P4_MEM_KERN_BASE for 64-bit startup. It must not be set global (with "G" bit set). The ASP will only use mappings >= P4_MEM_KERN_BASE when UniversalisOS kernel starts.
Virtual Address RWXG Size Description Remarks 0x0000000000600000 R-X- 2 MiB PSP identity mapping Used by a PSP during 64-bit startup. 0x0000000000000000 - 128 TiB Userspace addresses Used by a ASP during run-time. 0xffff800000000000 RW- - 1 MiB Legacy mappings Used by a PSP to find BIOS struc- tures, legacy VGA access. 0xffff800000200000 RW-G data/bss Alias of data and bss Used by the ASP during paging size section mapped on structure walks. KMAP_BASE 0xffff8xxxxxxxxxxx R- - - romimage UniversalisOS romimage Used by the ASP/PSP to access size romimage. 0xffff8xxxxxxxxxxx RW-G - ASP mappings of a free Mapped by the a PSP, contains RAM free system memory. 0xffffffff00000000 RW-G 3 GiB PSP mappings of I/O de- Used for various memory mapped vices devices like local APIC, IO-APIC, MSI-X etc. 0xffffffffc0200000 RW-G 2 - 4 MiB PSP/ASP mappings The PSP/ASP data and bss sec- tion. 0xffffffffc0600000 R-XG codesize PSP/ASP mappings The PSP/ASP code section. 0xffffffffcxxxxxxx R- - - 2 MiB The first 2 MiB of a Used by a PSP for early romim- romimage age access. 0xffffffffffe00000 RW- - 2 MiB ASP mappings The PSP will create a page table, the ASP will populate the map- pings.
The PSP must not map anything into the last 2 MiB (P4_MEM_KERN_RSVD - P4_MEM_KERN_END) of the kernel virtual address space. The range is used by the kernel for its internal purposes, however it is expected by the ASP, that PSP provides the page tables ready for the kernel mappings. The PSP must map all writable kernel memory as not executable. In order to mitigate CVE-2018-3620 PSP needs to set not present paging structure entries to zero. If there is at least 2 MiB of free space between bss section end and code section start, this memory will be made available as free system memory. The data, bss and code section is mapped using 2 MiB pages.
A.1.4 Exception Handling
The ASP forwards the interrupt 32 to 255 to the PSP intdispatch() entry point with the interrupt number as a first argument.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
84 Architecture Specific PSP Details
The ASP forwards the following exceptions to the PSP machinecheck() entry point with the exception number as the cause argument:
• Non-Maskable Interrupt (NMI, exception number 2)
• Machine Check Exception (#MC, exception number 18)
• Virtualization Exception (#VE, Intel specific exception number 20)
• VMM Communication Exception (#VC, AMD specific exception number 29)
• Security Exception (#SX, AMD specific exception number 30)
• Exception numbers 15, 21-28, 31 (undefined exceptions)
If the PSP returns from the machinecheck() handler with error code set to P4_E_OK an exception is considered to be successfuly recovered by the PSP. Note that the MC# exception is never recoverable and ASP will always panic. The ASP will panic if the PSP machinecheck() entry point is not implemented.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
A.2 PPC/e500/e500mc/e5500/e6500
This section describes PSP details specific to PPC/e500, PPC/e500mc, PPC/e5500 and PPC/e6500 based platforms.
A.2.1 PSP Descriptor Fields (P4_psp_arch_t)
Field Description
arch.pvr Content of PPC PVR register. The value can be obtained using the
ASP service p4ppc_get_pvr()
arch.svr Content of PPC SVR register. The value can be obtained using the
ASP service p4ppc_get_svr()
Table 20: PPC/e500/e500mc/e5500/e6500 Architecture PSP Descriptor Fields
A.2.2 Early Initialization
PPC/e500/e500mc/e5500/e6500 are multi core architectures. Early initialization is different between the main core (CPU 0) and other cores. A typical sequence of steps performed by the early initialization code is:
• <CPU 0 only> Switch to real mode
• <CPU 0 only> Flush data cache hierarchy
• Disable cache hierarchy
• Enable L1 cache
• Reset PPC time base
• Initialize CPU configuration registers (HIDs, performance monitor etc.)
• Invalidate TLBs
• Create TLB mappings for PSP-kernel image
• Switch to virtual mode
• Initialize FPU
• Get the CPU ID in range 0..31
•
Copy data/bss segment, when exec in place option is required.• Create C execution context
•
jump into init_board C code.• jump into psp_smp_entry C code.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
86 Architecture Specific PSP Details
A.2.3 UniversalisOS Virtual Memory Map
For any core the PSP should create contiguous read-write TLB mappings for all accessible system DRAM, with physical start address of 0 and a virtual start address of KMEM_BASE. On e500mc-4g the address space 0 is used for the kernel and address space 1 is used for the user space. The other architectures use address space 0 for kernel and user space. The maximum DRAM size that can be mapped is limited by the kernel virtual address space size which is different for the UniversalisOS architectures:
• ppc_e500 - 32 Bit - 2GiB
• ppc_e500mc - 32 Bit - 2GiB
• ppc_e500mc-4g - 32 Bit - 4GiB
• ppc_e5500 - 64 Bit - 512GiB
The DRAM size is further limited by the fact that some parts of the kernel address space are reserved for the kernel itself and for mapping of I/O devices. The SoC hardware registers area shall be mapped at a specific physical (CCSBAR) and virtual address that shall not collide with the last 2 MiB of the kernel virtual address space. The range is used by the kernel for its internal purposes.
• 32 Bit - 0xffe00000 to 0xffffffff
• 64 Bit - 0xffffffffffe00000 to 0xffffffffffffffff
A.2.4 C Execution Context
The following assembly code fragment illustrates setting up of the C execution context:
/* initial stack (in low-mem area) at INIT_STACK + num_cpu (in r3) * STACK_SIZE */
mulli r4, r3, STACK_SIZE
GRAB_SYM(r1, INIT_STACK)
add r1, r1, r4 /* initialize the stack pointer */
#ifdef P4_FEATURE_TOC /* Load TOC for architectures using r2 as pointer to access global * variables. / lis r2, init_board @highest; ori r2, r2, init_board @higher; sldi r2, r2, 32; oris r2, r2, init_board @high; ori r2, r2, init_board @l; ld r2, 8(r2) #else li r2, 0 / initialize the system reserved register. / #endif stw r2, 0(r1) / first stack frame back chain is 0, tool need for stack crawl / b init_board / or psp_smp_entry for a secondary core */
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PPC/e500/e500mc/e5500/e6500 87
A.2.5 Exception Handling
The ASP forwards the following PPC interrupt types to the PSP intdispatch() entry point with the exception number as first argument:
• External interrupt (exception number 4)
• Decrementer interrupt (exception number 10)
• Fixed interval timer interrupt (exception number 11)
• Processor doorbell interrupt (exception number 36)
The ASP forwards the following PPC critical interrupt types to the PSP machinecheck() entry point with the exception number as first argument:
• Critical input (exception number 0)
• Machine check interrupt (exception number 1)
• Watchdog interrupt (exception number 12)
• Processor doorbell critical interrupt (exception number 37)
• Guest processor doorbell interrupt (exception number 38)
• Guest processor critical doorbell and machine check (exception number 39)
The ASP will panic if the PSP machinecheck() entry point is not implemented.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
A.3 ARM/v7hf
This section describes PSP details specific to ARM based platforms (v7 ARM cores). The UniversalisOS architecture v7hf is to be used for ARM v7 with FPU.
A.3.1 PSP Descriptor Fields (P4_psp_arch_t)
Field Description
arch.ram_phys_base Start address of the RAM , will be mapped at 0x8000’0000 area
arch.boot_pt1 Virtual address of kernel page tables
arch.cpu_arch one of the variants: 7 for ARMv7, 8 for ARMv7 with LPAE
arch.fpu_mode VFPU mode (same as kinfo->arch.has_fpu)
Table 21: ARM/v7hf Architecture PSP Descriptor Fields
A.3.2 Early Initialization
ARM/v7hf may be a multi core architecture where the initialization starts with any core from hardware. A typical sequence of steps performed by the early initialization code is:
• Switch to SVC32 mode
• Get the CPU ID in range 0..31
•
set all GPRs registers of SRC to 0•
reset all the secondary cores•
Configure coherency in SCU• Deactivate caches and MMU
• Enable L1 cache coherency
• Initialize TLBs
• Switch to virtual mode
• Create C execution context
•
jump into init_board C code.• jump into psp_smp_entry C code.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
ARM/v7hf 89
A.3.3 UniversalisOS Virtual Memory Map
On ARM, the PSP is reponsible to create sufficient mappings (page table entries) for the kernel virtual address space starting at P4_MEM_KERN_BASE. The PSP can use any page table entry size which is available on the hardware. The kernel will not modify the kernel virtual address space, except for a small region of 2 MiB size at the end of the address space (from P4_MEM_KERN_RSVD to P4_MEM_KERN_END), which is reserved for the kernel. The kernel expects that all system RAM is mapped in the kernel address space with a constant physical offset and in a memory area starting at the virtual start address P4_MEM_KERN_BASE. The PSP must provide the physical start of the RAM in arch.ram_phys_base in the architecture specific fields in the PSP descriptor. The system memory itself does not need to be contiguous and can be divided into several areas, as long as the memory is mapped with a constant physical offset. The PSP can map memory and I/O resources into the kernel address space up to the start of the reserved region at P4_MEM_KERN_RSVD. The maximum RAM size that can be mapped is limited by the size of the kernel virtual address space, the kernel reserved memory region, and any mappings to I/O devices the PSP needs. At boot time, an ARM PSP typically uses two page tables: a page table in TTBR0 typically contains temporary identity mappings in the lower virtual address space to set up virtual addressing on all processors, and TTBR1 contains page tables with kernel mappings (RAM and IO) starting at 0x80000000. Lastly, the kernel expects a valid page table covering the kernel reserved region from P4_MEM_KERN_RSVD to P4_MEM_KERN_END.
A.3.4 C Execution Context
The following assembly code fragment illustrates setting up of the C execution context: /* load num_cpu into r0 / mrc p15, 0, r0, c0, c0, 5 / check num_cpu against 0 the main core / ands r0, r0, #0xf / initial stack is stored at stack[num_cpu] as INIT_STACK_BOTTOM + numcpu * P4_PAGESIZE -32 / adr r1, stack ldr sp, [r1, r0, lsl #2] / initialize the stack pointer / beq init_board / main core / b psp_smp_entry / secondary core */
A.3.5 Kernel variants and options available
A.3.6 arm_v7hf variants
arm_v7hf compiler supports ARM v7 cores and hard floating operations. The kernel will also support it and save the required context when a thread is using the FPU. Soft floating operation can still be supported by passing the right compilation flags. On this architecture, 2 kernel variants can be selected:
• v7: this is standard kernel using 32Bit physical addressing.
• v7-lpae: this is the kernel using ARM Long Physical Address support. This kernel in this variant is using 3 levels of page tables instead of 2 and supports up to 40Bit physical addressing (not virtual).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
90 Architecture Specific PSP Details
A.3.7 Trustzone Support
On ARM v7 cores (Cortex A9 or A15 for example), UniversalisOS can come with Trustzone support. UniversalisOS has a Trustzone support in PSP which, once activated, provides some functions and helpers to handle ARM Trustzone.
A.3.8 Detect start mode
The following assembler code, done during the PSP boot, will allow you to know in which world your PSP has been started: /* to be done at very beginning / tz_detect_mode r12, r0, r1 .... / to be done when MMU is on */ tz_store_mode r12, r0, r1
This will put in r12 a value that can be used to identify the world in which the PSP has been started (1 for secure, 2 for non-secure) and once the MMU is activated will store it in a global variable. In C code, you can then call tz_current_mode to test in which mode the PSP has been started.
A.3.9 Run in non-secure world
To be able to use Hardware virtualization or on some boards some peripheral, it could be required to have UniversalisOS executing in non-secure world instead of secure world. When Trustzone support is activated in your PSP, a function p4trustzone_switch_unsecure is provided to restart your PSP in non-secure once the required initialization is done. This must be done on all cores if you want UniversalisOS to be executed in non-secure world. In this use case, the PSP will first start in secure world, do some initialization and then restart completely in non-secure world. Some minimum initializations are required in secure world to make sure everything will be possible to be done in non-secure world:
• Flag all interrupts as non-secure in the interrupt controller
• Flag all peripherals as non-secure (vendor specific)
• Flag the complete memory as non-secure (vendor specific)
A.3.10 Use UniversalisOS Trustzone support
To be able to run an operating system in non-secure world while UniversalisOS runs in secure world, the PSP must call some functions during initialization:
• p4trustzone_init: must be called early on the first core. It will handle cores assigned to non-secure world
and do some internal initialization.
• p4trustzone_core_init: this will install the monitor mode exception handler and prepare everything to later
execute a guest. This must be called on all cores.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
ARM/v7hf 91
When Trustzone support is used on UniversalisOS some of the cores available on your board will be assigned to the guest running in non-secure world. As a consequence UniversalisOS will have less cores available and those might not be the first ones. Some macros and functions are available to handle the mapping between physical core numbers and UniversalisOS core numbers.
A.3.11 Hardware Virtualization
On ARM Cortex A15 and equivalent, UniversalisOS can come with hardware virtualization support and this will influence the PSP implementation. All functions from this chapter are only available if hardware virtualization support is turned on in your PSP configuration.
Note: Hardware virtualization is only available in non-secure mode. If your board is starting in secure world, you will have to refer to the previous chapter to first switch to non-secure world during your PSP boot.
To support hardware virtualization, the PSP must install an exception handler in Hypervisor mode on each core. The following assembler code will install this handler if the PSP is started in hypervisor mode: /* read cpsr to find our current execution mode / mrs r0, cpsr and r0, r0, #0x1f teq r0, #CPSR_HYPERVISOR_MODE bne 1f bl 2f 2: ldr r0, =2b sub r2, r0, lr / install the exception handler */ arm_install_hyp r0, r1, r2 1:
This code should be executed at the very beginning of your PSP or in hypervisor mode through possible ways available on your hardware. Some boards have a firmware running in monitor mode which can be used to execute something in hypervisor mode.
Note: When your board is started in secure mode, the Trustzone switching code provided by UniversalisOS will automatically start in hypervisor mode when switching to non-secure mode.
To finish hardware virtualization initialization, the PSP must call the function p4hwvirt_init on the primary core and p4hwvirt_init_secondary on secondary cores.
A.3.12 ARM PSP Library
Inside your PSP project, you will find selectable items providing some standard ARM implementations for specific functions.
A.3.12.1 Virtual to Physical Helper
This library provides a hardware way to convert virtual addresses in physical for both user and kernel virtual addresses. To use it you must include the header addr.h which provides the following function:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
92 Architecture Specific PSP Details
extern P4_address_t psp_virt_to_phys(P4_address_t);
When using this function, the hardware will use the current MMU status to do the conversion. You must be careful when using it for user addresses as it will do the conversion for the current user application from MMU point of view on the current core. If unsure in your PSP drivers, you should use the kernel provided functions for those conversions. This is mainly used during PSP initialization when the mapping is computed during early boot to be independent of the starting address (Cortex A15 PSP are using this functionality).
A.3.12.2 ARM Cache handling
This library provides routine functions to do cache maintenance operations (flushing, invalidate, sync) for a virtual memory area of the complete cache. Those functions only operate on the level 1 cache. To use it you must include the header cache.h which provides the following functions:
extern void psp_arm_flush_dcache_line(void *addr);
extern P4_e_t psp_arm_clean_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_flush_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_inval_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_flush_dcache_all(void); extern P4_e_t psp_arm_flush_dcache_all_l1(void);
extern P4_e_t psp_arm_inval_icache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_inval_icache_all(void); extern P4_e_t psp_arm_inval_icache_all_core(void);
extern P4_e_t psp_arm_cache_user( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); extern P4_e_t psp_arm_cache_kern( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); extern void psp_arm_cache_tps( P4_bool_t flush_dcache, P4_bool_t inval_icache);
This does not support L2 cache on Cortex A9 as the L2 cache is an external IP. On Cortex A15, the level 2 cache is handled by the CPU directly so those functions will do the operations on both L1 and L2 caches.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
ARM/v7hf 93
A.3.12.3 L2C310 Level 2 Cache
This library provides routine functions to use the Level 2 cache L2C310 from ARM. It provides an initialization function to activate it and an operation function to do standard operations (invalidate, clean, flush, sync). The L2 cache handling is complex using this IP as the programmer has to maintain in software the coherency between L1 and L2 caches and the operations can become complex. You can have a look in the imx6 PSP to have an implementation example. To use it you must include the header l2c310.h which provides the following functions:
extern P4_uint32_t psp_arm_l2c310_init(P4_address_t, P4_uint32_t, P4_uint32_t, P4_uint32_t); extern P4_uint32_t psp_arm_l2c310_disable(P4_address_t); extern void psp_arm_l2c310_do_op(P4_uint32_t op, P4_uint32_t start, P4_uint32_t end);
extern P4_uint32_t l2c310_cache_size;
A.3.12.4 Switch from secure to unsecure world
This library provides routines to detect in which world the PSP was started and a function to switch from Secure World to Non-Secure World. To use it you must include the header psp_tz_switch_unsecure.h which provides the following functions:
static inline P4_cpureg_t psp_tz_current_mode(void); void psp_tz_switch_unsecure(P4_uint32_t addr);
A.3.13 Interrupt and Exception Handling
The ASP forwards all IRQ interrupts to the PSP intdispatch() entry point with the interrupt cause argument set to zero. The PSP is responsible for further dispatching of the interrupt. The ASP forwards all FIQ interrupts to the PSP machinecheck() entry point with the interrupt cause argument set to zero and the current register context. The PSP is responsible for further dispatching of the FIQ. If the machinecheck() callback returns with P4_E_OK, the ASP resumes execution of the provided register context, but the kernel cannot perform any rescheduling action. If the machinecheck() callback returns with another error code, the kernel panics and the system is halted. The ASP will also panic if the machinecheck() entry point is not implemented. The ASP forwards the following prefetch and data abort exceptions to the PSP machinecheck() entry point:
• All prefetch abort exceptions which happen in kernel mode
• All unrecoverable data abort exceptions which happen in kernel mode
• All synchronous external aborts in user mode
• All synchronous parity errors in user mode
Also, the machinecheck() entry point is called with the IFSR or DFSR register as interrupt cause argument and the current faulting register context. Note that the DFSR and the IFSR registers never contain a zero value, so this type of invocation can be distinguished from the FIQ case. Further, to distinguish prefetch and data abort exceptions,
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
94 Architecture Specific PSP Details
all prefetch abort exceptions reported to the PSP have bit 31 (value 0x80000000) set in the interrupt cause. In all cases, if the machinecheck() callback returns with P4_E_OK, the ASP resumes execution of the provided register context, but the kernel does not perform any rescheduling action. If the machinecheck() callback returns with a different error code or is not implemented at all, the ASP default action depends whether the exception was raised from kernel mode or user mode. Exceptions in kernel mode let the kernel panic, and exceptions in user mode let the ASP raise a P4_TRAP_BUS exception.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
A.4 ARM/v8hf
This section describes PSP details specific to ARMv8 64bit based platforms.
A.4.1 PSP Descriptor Fields (P4_psp_arch_t)
Field Description
arch.ram_phys_base Start address of the RAM , must be mapped at 0xffffff8000000000
arch.boot_pt1 Virtual address of kernel page table in TTBR1
arch.cpu_arch Must be set to 8 for ARMv8
Table 22: ARM/v8hf Architecture PSP Descriptor Fields
A.4.2 Early Initialization
ARM/v8hf may be a multi core architecture where the initialization starts with any core from hardware. A typical sequence of steps performed by the early initialization code is:
• Switch to EL1 mode (supervisor mode)
•
Create page tables• set TTBR0 and TTBR1
• set MAIR register
• invalidate level 1 cache
• invalidate icache and branch prediction
• turn on MMU and caches
• initialized and enable FPU
• Create C execution context
•
jump into init_board C code.• jump into psp_smp_entry C code.
A.4.3 UniversalisOS Virtual Memory Map
On ARM, the PSP is reponsible to create sufficient mappings (page table entries) for the kernel virtual address space starting at P4_MEM_KERN_BASE. The PSP can use any page table entry size which is available on the hardware. The kernel will not modify the kernel virtual address space, except for a small region of 2 MiB size at the end of the address space (from P4_MEM_KERN_RSVD to P4_MEM_KERN_END), which is reserved for the kernel.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
96 Architecture Specific PSP Details
The kernel expects that all system RAM is mapped in the kernel address space with a constant physical offset and in a memory area starting at the virtual start address P4_MEM_KERN_BASE. The PSP must provide the physical start of the RAM in arch.ram_phys_base in the architecture specific fields in the PSP descriptor. The system memory itself does not need to be contiguous and can be divided into several areas, as long as the memory is mapped with a constant physical offset. The PSP can map memory and I/O resources into the kernel address space up to the start of the reserved region at P4_MEM_KERN_RSVD. The maximum RAM size that can be mapped is limited by the size of the kernel virtual address space, the kernel reserved memory region, and any mappings to I/O devices the PSP needs. At boot time, an ARM PSP typically uses two page tables: a page table in TTBR0 typically contains temporary identity mappings in the lower virtual address space to set up virtual addressing on all processors, and TTBR1 contains page tables with kernel mappings (RAM and IO) starting at 0xffffff8000000000. Lastly, the kernel expects a valid page table covering the kernel reserved region from P4_MEM_KERN_RSVD to P4_MEM_KERN_END.
A.4.4 C Execution Context
The following assembly code fragment illustrates setting up of the C execution context: /* load stack table address / ldr x0, =stack_virt / retrieve cpu number: clusterid<<4|coreid / mrs x2, MPIDR_EL1 / coreid / and x1, x2, #0xff / clusterid<<8 / and x2, x2, #0xff00 orr x1, x1, x2, lsr #6 / stack + cpuid*8 / add x0, x0 , x1, lsl #3 / get table entry value / ldr x0, [x0] / put 0 in first stack entry / stp xzr, xzr, [x0] / setup stack */ mov sp, x0
A.4.5 Kernel variants and options available
On ARMv8 there is no kernel variant available.
A.4.6 Hardware virtualization
On ARMv8 cores, UniversalisOS can come with hardware virtualization support and this will influence PSP implementa- tion. All functions from this chapter are only available if hardware virtualization support is turned on in your PSP configuration.
Note: Hardware virtualization is only available in non-secure mode. If your board is starting in secure-mode, you will first need to switch the execution to non-secure mode to use hardware virtualization.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
ARM/v8hf 97
To support hardware virtualization, the PSP must be started in EL2 mode (hypervisor) to install a temporary exception handler before jumping down to EL1 mode. The following code will detect EL2 mode, install the exception handler and jump down to EL1 mode: /* get mode / mrs x1, CURRENTEL / isolate mode / and x25, x1, #0x3 << 2 / if not el2 go to end / cmp x25, #2 << 2 b.ne end / we are in el2 / / find phys to virt / bl 1f 1: ldr x0, =1b / lr contain physical and x0 virtual / sub x6, x0, x30 arm_install_hyp x0,x1,x6 / prepare jump back to el1 / ldr x0, =end sub x0, x0, x6 msr elr_el2, x0 / exception return to el1 */ eret end:
This code should be executed at the very beginning of your PSP or in hypervisor mode through possible ways available on your hardware. Some boards have a firmware running in monitor mode which can be used to execute something in EL2 mode. To finish hardware virtualization initialization, the PSP must call the function p4hwvirt_init on the primary core and p4hwvirt_init_secondary on secondary cores.
A.4.7 ARM PSP Library
Inside your PSP project, you will find selectable items providing some standard ARM implementations for specific functions.
A.4.7.1 ARMv8 Cache handling
This library provides routine functions to do cache maintenance operations (flushing, invalidate, sync) for a virtual memory area of for the complete cache. Those functions operate on the level 1 and 2 of the cache. To use it you must include the header cache.h which provides the following functions:
extern void psp_arm_flush_dcache_line(void *addr);
extern P4_e_t psp_arm_clean_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_flush_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_inval_dcache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_flush_dcache_all(void);
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
98 Architecture Specific PSP Details
extern P4_e_t psp_arm_flush_dcache_all_l1(void);
extern P4_e_t psp_arm_inval_icache_range(P4_address_t start, P4_size_t size); extern P4_e_t psp_arm_inval_icache_all(void); extern P4_e_t psp_arm_inval_icache_all_core(void);
extern P4_e_t psp_arm_cache_user( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); extern P4_e_t psp_arm_cache_kern( P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); extern void psp_arm_cache_tps( P4_bool_t flush_dcache, P4_bool_t inval_icache);
A.4.8 Cortex A5x PSP specifics
In opposite to most of the UniversalisOS PSP, the Cortex A5x PSP for ARMv8 provides some functionalities to have a more dynamic design. Some of those functionalities are explained here to make it easier to start development of your own PSP from this PSP.
A.4.9 Mapping
The page tables for the PSP are dynamically created during PSP boot. Memory start address is detected and mappings are created for a size depending on the psp/memory/size property. Several memory areas are mapped if the user configured them in the properties. Drivers in the PSP can call the function mmu_map_io to map an iomem area (this will only map a page, so the function must be called for each page required).
A.4.10 Dynamic design
Several serial drivers and several SMP (core start, target halt and target reset) drivers are included in the PSP. Each of them is using properties to detect if it has to be activated and where its registers are. When implementing your own PSP you can choose to either remove all unwanted drivers and have only the ones required by your board or just add your drivers using the same model.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
ARM/v8hf 99
A.4.11 Interrupt and Exception Handling
The ASP forwards all IRQ interrupts to the PSP intdispatch() entry point with the interrupt cause argument set to zero. The PSP is responsible for further dispatching of the interrupt. The ASP forwards all FIQ interrupts to the PSP machinecheck() entry point with the interrupt cause argument set to zero and the current register context. The PSP is responsible for further dispatching of the FIQ. If the machinecheck() callback returns with P4_E_OK, the ASP resumes execution of the provided register context, but the kernel cannot perform any rescheduling action. If the machinecheck() callback returns with another error code, the kernel panics and the system is halted. The ASP will also panic if the machinecheck() entry point is not implemented. The ASP forwards the following prefetch and data abort exceptions to the PSP machinecheck() entry point:
• All prefetch abort exceptions which happen in kernel mode
• All unrecoverable data abort exceptions which happen in kernel mode
• All synchronous external aborts in user mode
• All synchronous parity errors in user mode
• All external aborts on load/store exclusive instructions in user mode
Also, the machinecheck() entry point is called with the ESR register as interrupt cause argument and the current faulting register context. Note that the ESR register never contains a zero value, so this type of invocation can be distinguished from the FIQ case. In all cases, if the machinecheck() callback returns with P4_E_OK, the ASP resumes execution of the provided register context, but the kernel does not perform any rescheduling action. If the machinecheck() callback returns with a different error code or is not implemented at all, the ASP default action depends whether the exception was raised from kernel mode or user mode. Exceptions in kernel mode let the kernel panic, and exceptions in user mode let the ASP raise a P4_TRAP_BUS exception.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
B Reference
This Appendix provides reference pages.
B.1 PSP Declaration
This section describes the data structures used by the PSP to declare itself and system resources to the kernel.
B.1.1 Structure Definitions
B.1.1.1 struct P4_psp_descriptor_str
The PSP descriptor structure. This structure is passed up to the microkernel by the PSP. All fields are mandatory, however, some features can be disabled by setting them to zero.
Synopsis:
struct P4_psp_descriptor_str { P4_uint32_t api_version; P4_uint32_t max_interrupts; P4_uint64_t ts_calibration; P4_time_t ns_per_calibration; P4_time_t dyntick_res; P4_time_t dyntick_max; const char * psp_id; const char * psp_welcome; drv_config_header_t * romheader; P4_devcallback_t * dev_call_table; P4_address_t align_mask; P4_psp_api_t api; P4_psp_arch_t arch; P4_bool_t breakcon; P4_uint32_t num_cpu; P4_cpu_info_t cpu_info[P4_NUM_CPU]; P4_uint32_t num_devices; P4_uint32_t startmode; P4_haltmode_t haltmode; P4_uint8_t _pad[0]; };
Structure Element Description: api_version Version of the API interface. The API version is checked by the kernel at startup and must match the one the PSP is compiled with, otherwise the kernel main routine returns with an error.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Declaration 101
Note:
Intentionally, the API version is the first member of this structure.
max_interrupts Maximum number of interrupt vectors used by the PSP. Should not exceed the maximum number of supported interrupt (P4_NUM_INTERRUPT). ts_calibration Timestamp counter after calibration. Used for system time calibration: ts_calibration contains the number of time stamp counter increment during time ns_per_calibration in nanoseconds If one of ts_calibration and ns_per_calibration is zero, the kernel will ignore ts_calibration and ns_per_calibration at all and run another benchmark to detect system speed. If you know the speed of the time stamp counter (or time base), you can setup correct values in the PSP and the benchmark isn’t run. ns_per_calibration Nanoseconds spent in calibration (see ts_calibration). dyntick_res Resolution of the system dynamic ticker source in nanoseconds. If non-zero, dyntick_res shows the minimum timeout resolution of the dynamic ticker in nanoseconds. dyntick_max Maximum timeout resolution of the system dynamic ticker source in nanoseconds. If non-zero, dyntick_max shows the maximum timeout resolution of the dynamic ticker in nanoseconds. psp_id PSP signature string. psp_id points to a NUL terminated string identifying the PSP and exported in the kernel info pages. Only the first P4_NAMELEN - 1 characters are taken from the string provided by PSP. psp_welcome PSP welcome string. Points to nul-terminated string displayed at system startup. romheader Pointer to the UniversalisOS ROM header.
Note:
Although not explicitly mandatory, the pointer to the ROM header should be supplied to the kernel to do
something useful.
This has type drv_config_header_t and the top-level node has type P4_romboot_header_t.
It is the responsibility of the PSP to make sure that the romimage is valid, e.g. by checking via
drv_config_valid(). The kernel assumes that this has been done and does not check again, in order
to avoid multiple CRC checks of the same stuff.
Also note that since the kernel binary is a preheader of the romimage, it is excluded from the CRC
checksum of that drv_config_valid() checks. Instead, the kernel binary is CRC checksummed via the
P4_romheader::psp->resource struct. This is, again, a PSP responsibility.
dev_call_table PSP level device driver callback table. Array of function pointers to PSP level device driver callbacks. The array must contain num_device entries.
Note:
PSP level device drivers are deprecated and their support will be removed in future versions. Consider
using KDEV drivers instead.
align_mask Cache align mask.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
102 Reference
The cache align mask is used for systems with flawed cache designs, where the mask shows which bits
of the page address must the same in virtual and physical address space. The cache align mask is zero
on flawless systems.
api List of PSP calls exported by the PSP.
arch Architecture related information.
The architecture related information entries are defined by the ASP.
breakcon Debugger break condition. A non-zero value indicates a break condition to the internal kernel debugger. Typically set in cnspoll() and dbgpoll() functions. The kernel clears the break condition before trapping into the debugger. num_cpu SMP related bits: number of CPUs in the system Setting num_cpu to zero indicates a uniprocessor system. cpu_info SMP related bits: type information on CPUs in the system The first num_cpu entries in cpu_info must be initialized. On UP, content of this data structure is ignored. num_devices Maximum number of PSP level devices used by the PSP. The number of the highest PSP level device driver ID minus one used by the PSP. (deprecated) startmode Start mode of the target. This specifies if the target was started by a cold or a warm reset. The starting mode is normally stored in some permanent storage across reboot. The value must be set to P4_MODE_COLD_START if the PSP doesn’t support the propagation of this information. haltmode Halt mode with which the board was shut down before the last boot. _pad
Associated Data Type
P4_psp_descriptor_t The PSP descriptor structure.
B.1.2 Defines
P4_PSP_API_VERSION
P4_PSP_VERSION (api, arch)
B.1.3 Data Type Definitions
P4_psp_descriptor_t The PSP descriptor structure. This structure is passed up to the microkernel by the PSP. All fields are mandatory, however, some features can be disabled by setting them to zero.
B.1.4 Variables
psp_buildid PSP build ID.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Declaration 103
Build identification string used by the P4_ROMBOOT_ANCHOR_DATA macro and printed by kernel
during system startup.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
104 Reference
B.2 PSP Entry Points
This section describes the PSP entry points (callbacks) provided by the PSP to the kernel.
B.2.1 Structure Definitions
B.2.1.1 struct P4_psp_api_str
The PSP API, called by the kernel. The PSP API contains a list of optional and mandatory callbacks into the PSP the PSP must provide to the kernel. For each callback, the documentation states whether the kernel calls the callback with interrupts enabled or disabled. If scheduling or interrupts must be prevented, the call should disable interrupts. On SMP, the kernel may call the callbacks with a specific locks or not. The documentation states if the implemen- tation of the callbacks requires further synchronization, e.g., a spinlock, to protect for concurrent calls.
Synopsis: struct P4_psp_api_str { void(* init)(void); void(* init_cpu)(void); void(* init_late)(void); void(* board_halt)(P4_haltmode_t mode); P4_e_t(* ticker)(P4_time_t period_ns, P4_inthandler_t handler, void arg); void( dyntick_init)(P4_inthandler_t handler, void arg); P4_time_t( get_time)(void); void(* dyntick_expire)(P4_time_t time_ns); void(* dyntick_remote)(P4_cpuid_t cpuid); void(* intdispatch)(P4_cpureg_t cause); P4_e_t(* inthandle)(P4_intid_t intid, P4_int_mode_t mode, P4_inthandler_t handler, void arg); P4_e_t( intshareable)(P4_intid_t intid, P4_int_mode_t mode); void(* intdefault)(P4_inthandler_t handler, void arg); void( intmask)(P4_intid_t intid); void(* intunmask)(P4_intid_t intid, P4_cpuid_t cpuid); P4_e_t(* cache_user)(P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); P4_e_t(* cache_kern)(P4_cache_op_t op, P4_address_t start, P4_size_t size, P4_address_t alias, P4_uint32_t flags); void(* cache_tps)(P4_bool_t flush_dcache, P4_bool_t inval_icache); void(* cnsput)(char c); char(* cnsget)(void); unsigned int(* cnspoll)(void); void(* dbgput)(char c); char(* dbgget)(void); unsigned int(* dbgpoll)(void); void(* idle)(void); P4_e_t(* machinecheck)(P4_regs_t regs, P4_cpureg_t cause, P4_phys_addr_t addr, P4_cpureg_t aux); void( hm_panic)(P4_hm_info_t einfo); void( tp_switch)(const P4_tptable_t tp_window, P4_uint32_t flags); P4_e_t( start_cpu)(P4_cpuid_t cpuid, void(entry)(P4_cpuid_t cpu_id)); void( reschedule)(P4_cpuid_t cpuid);
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 105
void(* ipi_tlb_inval)(P4_cpumask_t cpumask);
void(* ipi_poll)(void);
P4_cpuid_t(* current_cpu)(void);
P4_uint64_t(* get_ts)(void);
P4_time_t(* tp_sync)(P4_time_t reference_time, P4_tp_sync_mode_t mode);
void *(* io_phys_to_kernel)(P4_phys_addr_t phys, P4_size_t size);
void *(* io_map_kernel)(P4_phys_addr_t phys, P4_size_t size, P4_access_t access);
void(* hm_notify)(const P4_hm_info_t *einfo);
};
Structure Element Description: init Board initializion. The kernel calls this function to perform secondary PSP specific board initialization during system startup. The PSP can utilize p4_kernel_balloc() (see section B.3.1.5) to allocate dynamic memory. Temporary storage used during boot (see p4_kernel_assign_tmp() (see section B.3.1.4)) is still reserved by the kernel.
Note:
The function is optional.
Note:
This function is called in the context and on the stack of the idle thread of the first processor with
interrupts disabled.
kglobal_info (P4_kglobal_info_t) boot_stage: P4_BOOT_STAGE_IDLE_TASK.
init_cpu Per-CPU board initialization.
The kernel calls this function to perform per-processor PSP specific board initialization during system
startup.
The PSP can utilize p4_kernel_balloc() (see section B.3.1.5) to allocate dynamic memory. Temporary
storage used during boot (see p4_kernel_assign_tmp() (see section B.3.1.4)) is still reserved by the
kernel.
Note:
The function is optional.
Note:
This function is called in the context and on the stack of the idle thread on each CPU with interrupts
disabled.
kglobal_info (P4_kglobal_info_t) boot_stage: P4_BOOT_STAGE_CPU_ONLINE
init_late Late board initializion.
The kernel calls this function to perform late PSP specific board initialization during system startup.
The PSP can utilize p4_kernel_balloc() (see section B.3.1.5) to allocate dynamic memory. Temporary
storage used during boot (see p4_kernel_assign_tmp() (see section B.3.1.4)) is still reserved by the
kernel, but will be reclaimed shortly after invocation of this callback.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
106 Reference
Note:
The function is optional.
Note:
This function is called in the context and on the stack of the idle thread of the first processor with
interrupts enabled.
kglobal_info (P4_kglobal_info_t) boot_stage: P4_BOOT_STAGE_LATE.
board_halt Halt, reset, shutdown, or power off the system. A call to this function will invoke the halting functionality of the PSP configurable via the mode parameter. Dedicated halting mode implementations are mandatory for this PSP callback and others are optional to be implemented. The rule for the implementation of the available halting modes is as follows (please refer to P4_haltmode_t for details on each mode):
• P4_PSP_HALT is mandatory and halts the system.
• P4_PSP_STOP is mandatory and stops the current CPU.
• P4_PSP_RESET is optional and resets the system.
• P4_PSP_POWEROFF is optional and will power off the system.
• P4_PSP_ASSERT is optional and halts the system.
• P4_PSP_HMRESET is optional and resets the system.
If the halting mode mode is not available, board_halt falls back to the mandatory P4_PSP_HALT as
selected halting mode. This fallback behavior must be explicitly provided by the implementation of the
board_halt PSP-callback.
The PSP may save the stopping reason mode propagated by board_halt on persistent storage and
make mode available to the kernel and to the users (exported in the haltmode field in the KINFO page)
across reset. To achieve this behavior, the PSP should report the stored mode in the haltmode field of
the psp_descriptor passed to p4_main() (see section B.4.1.2).
Note:
This function does not return.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
When invoked with this halting mode, on SMP the board_halt callback should take care of other pro-
cessors and halt them, if necessary. An implementation should provide internal locking, if necessary.
// PSP-callback implementation example for board_halt()
void board_halt(P4_haltmode_t mode) {
...
switch (mode) {
case P4_PSP_HALT:
...
// Call internal function to halt the system
system_halt();
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 107
break;
case P4_PSP_STOP:
...
// Call internal function to stop the current CPU
cpu_stop();
break;
default:
// The fallback must cover all P4_haltmode_t options that
// were not implemented. Here, e.g.,
// P4_PSP_RESET, P4_PSP_POWEROFF, P4_PSP_ASSERT, and
// P4_PSP_HMRESET
...
// Fallback: halt the system
system_halt();
break;
}
...
// Should not be reached.
}
ticker Setup a fixed period system ticker. A call to this function sets up the system ticker. The kernel registers the handler handler to be called with its argument arg each time interval of period_ns (in nanoseconds) length.
Returns:
Upon success, this function returns P4_E_OK, otherwise a return code of P4_E_NOENT denotes an
error.
Note:
The function is optional.
If no periodic ticker is implemented, the dynamic ticker must be implemented at least.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function affects ticker handling on the calling processor only. The kernel will call this
function once for each processor. The kernel will setup the same ticker mode for all processors, using
the same period, handler and argument. The kernel serializes calls to this function by other processors.
dyntick_init Setup a dynamic system ticker. A call to this function registers the kernel handler handler to be called with its argument arg on ticker interrupts. The time of the next ticker interrupt is set by dyntick_expire.
Note:
The function is optional. It is only called, when the two hooks dyntick_init and dyntick_expire exist and
dyntick_res is not zero.
If no dynamic ticker is implemented, the periodic ticker must be implemented at least.
Note:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
108 Reference
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function affects ticker handling on the calling processor only. The kernel will call this
function once for each processor. The kernel will setup the same ticker mode for all processors, using
the same handler and argument. The kernel serializes calls to this function by other processors.
get_time Get current time in nanoseconds. This function returns the current time in nanoseconds. Multiple invocation of the callback should provide a monotonic increasing time value. On SMP systems, the time provided by this function must be coherent across multiple CPUs, i.e., globally, the time must be monotonically increasing on all CPUs. On SMP systems, the possible CPU time-differences must be non-observable by threads migrating across CPUs. Typically, PSP implementations provide the current time starting from system boot. The PSP should explictly document if this is not the case.
Returns:
This function returns the current time in nanoseconds.
Note:
The function is mandatory in all modes.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary.
dyntick_expire Set next ticker expiry time. When implemented, this function sets the next ticker expiration time to time_ns in nanosecond resolution on the current CPU. Setting a time in the past will cause the interrupt to arrive as soon as possible. Setting a time in the future where the difference to the current time exceeds the maximum supported interval of the hardware timer, results in the saturation of the next ticker interval to this maximum supported by the timer hardware. On the following interrupt, the kernel reprograms the hardware timer and this proceeds iteratively until time_ns is in reach within the maximum supported hardware resolution. Consequently, this will break down the required ticker interval until time_ns into multiple sub-intervals.
Note:
The function is optional. It is only called, when the two hooks dyntick_init and dyntick_expire exist and
dyntick_res is not zero. On SMP systems, the hook dyntick_remote must also exist if this function is
provided.
If no dynamic ticker is implemented, the periodic ticker must be implemented at least.
Note:
See dyntick_remote to request a timer reprogramming on a remote CPU on SMP systems.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 109
Note:
The kernel calls this function with interrupts disabled. The kernel may hold a spinlock when invoking
this function on SMP systems.
Note:
It is recommended to use a maximum time interval of one second for the dynamic ticker.
dyntick_remote Request an immediate timer reprogramming on other processor. When implemented, this function sends an IPI to the CPU cpuid requesting this CPU to immediately call the kernel ticker interrupt handler upon reception of the IPI. The kernel ticker interrupt will invoke dyntick_expire with an appropriate expiry time on the CPU cpuid. cpuid is guaranteed to be a remote CPU and not the current CPU.
Note:
The function is optional, but it must be provided on SMP systems if the hooks dyntick_init and
dyntick_expire are implemented.
If no dynamic ticker is implemented, the periodic ticker must be implemented at least.
Note:
The kernel calls this function with interrupts disabled. The kernel may hold a spinlock when invoking
this function.
intdispatch Interrupt dispatcher routine. A call to this function dispatches the current interrupt and call the appropriate registered handlers. The argument cause depends on the ASP and may provide further information on the interrupt cause, typically it contains the current interrupt vector or ID signaled to the CPU. This function masks the interrupt source and acknowledges the interrupt in the interrupt controller. Afterwards, it serves the interrupt to the kernel by calling the kernel’s registered handler. The interrupt remains masked until the kernel unmasks it again. The interrupt dispatcher does not mask ticker interrupts, these remain unmasked all the time.
Note:
On platforms where edge triggered interrupts cannot be masked correctly or may get lost, intdispatch
should mark the interrupt source as pending and serve the interrupt immediately when the kernel
unmasks the interrupt source in intunmask.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function dispatches the interrupt on the calling processors. Because the kernel does not
know the interrupt yet, it calls this function without any per-interrupt locks. An implementation should
provide internal locking, if necessary.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
110 Reference
Note:
The PSP calls registered kernel handlers in intdispatch and intunmask only. The intdispatch handler
serves interrupts currently signaled by the interrupt controller.
inthandle Setup an interrupt handler. A call to this function registers the kernel handler handler to be called with its argument arg on interrupt intid. A call to this function with handler set to NULL will disable the interrupt again. mode specifies the requested configuration for the interrupt controller. If a mode different from P4_INT_MODE_ANY is provided and the PSP returns P4_E_OK, it is assumed that the mode has been either honored (and the interrupt controller has been programmed in the specified way) or ignored (the specified mode was irrelevant for the hardware). See also intshareable. P4_INT_MODE_FOR- WARD may be OR-ed with a controller’s mode to implement a virtual interrupt controller behavior.
Returns:
Upon success, this function returns P4_E_OK, otherwise one of the following error codes is returned to
the caller:
P4_E_NOENT Not implemented interrupt source.
P4_E_MISMATCH The mode is not supported by the interrupt controller for interrupt intid.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function affects interrupt handling on all processors. The kernel calls this function while
holding a per-interrupt lock.
intshareable Check if the interrupt is shareable. A call to this function check with the PSP if the interrupt intid is shareable with mode mode. The kernel calls this function only if an attach has been already performed for intid via the inthandle callback. P4_INT_MODE_FORWARD may be OR-ed with a controller’s mode to implement a virtual interrupt controller behavior.
Returns:
P4_E_OK Success. This informs the kernel that the interrupt intid can be shared among threads.
P4_E_NOENT Indicates that intid is not shareable.
P4_E_MISMATCH Indicates that the currently setup mode for intid does not match with the re-
quested mode.
Note:
The function is optional, if the function is not provided, the kernel assumes that the interrupt intid can
be shared.
Note:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 111
The kernel calls this function with interrupts disabled.
Note:
The kernel calls this function while holding a per-interrupt lock.
intdefault Setup a default interrupt handler. A call to this function registers the kernel handler handler to be called with its argument arg on all unhandled interrupts.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function affects interrupt handling on all processors. The kernel calls this function only
once at boot time.
intmask Mask (disable) interrupt sources. A call to this function masks interrupt sources in the system. The interrupt source number is given in intid.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function masks the interrupt on all processors. The kernel calls this function while holding
a per-interrupt lock. An implementation should provide internal locking, if necessary.
intunmask Unmask (enable) interrupt sources. A call to this function unmasks interrupt sources in the system. The interrupt source number is given in intid. The processor ID cpuid hints to the processor where the interrupt will be handled on.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function unmasks the interrupt one or all processors. The PSP should route the interrupt
to the designated processor. The kernel calls this function while holding a per-interrupt lock. An
implementation should provide internal locking, if necessary.
Note:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
112 Reference
The PSP calls registered kernel handlers in intdispatch and intunmask only. The PSP may report an
already pending interrupt intid to the kernel on calls to intunmask as well. Typically this is used to signal
previously recorded edge triggered interrupts or software generated interrupts.
cache_user Perform data and instruction cache operations on a given user space memory region, preemp- tively. A call to this function performs the following operations on the data and instruction caches, depending on op:
• P4_INVAL_ICACHE_RANGE Synchronize instruction cache content with data cache content for
application loading.
• P4_FLUSH_DCACHE_RANGE Write back and invalidate data cache content.
• P4_SYNC_DCACHE_RANGE Write back data cache content.
• P4_INVAL_DCACHE_RANGE Invalidate data cache content.
The memory region defined by start and size always refer to user space memory.
Depending on flags, cache content in the cache and memory hierarchy is affected differently by the
operation:
• P4_CACHE_FLAG_CPU affects caches on current processor only.
• P4_CACHE_FLAG_DMA for cache coherency to DMA bus masters.
• P4_CACHE_FLAG_ALL affects all levels of the cache hierarchy. If flags is zero,
P4_CACHE_FLAG_DMA applies.
The operations P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE, and P4_IN-
VAL_DCACHE_RANGE affect content in the cache related to the memory region defined by
start and size. Set alias to start for these operations.
The operation P4_INVAL_DCACHE_RANGE requires a writable memory region and additionally writes
back cache content at the beginning and the end of the memory area if the memory area is not properly
aligned. On some architectures or in some processor modes, the operation may be equivalent to
P4_FLUSH_DCACHE_RANGE and write back any data before invalidating caches. The calling code
should be written in a robust way that write back of previous data is acceptable.
The operation P4_INVAL_ICACHE_RANGE additionally handles potential aliases in the instruction
cache in cross address space memory accesses. The operation uses alias to derive the addresses
of potential instruction cache aliases in other address spaces. In this case, the caller should preform an
P4_INVAL_ICACHE_RANGE operation on the data memory region, i.e. the memory region or mapping
where data cache content was modified, and provide the memory region where instructions will be
executed from as alias. alias might refer to an address in a different address space. Set alias equal to
start if both regions equal.
Returns:
Upon success, this function returns P4_E_OK, otherwise one of the following error codes is returned to
the caller:
P4_E_NOTIMPL if op or flags are not supported by the PSP.
P4_E_PAGEFAULT if the memory region defined by start and size is not fully mapped in the
caller’s virtual address space.
Note:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 113
The function is mandatory. However, the implementation of the different operations depends on the
target architecture and the PSP implementation.
Note:
On SMP, operations P4_INVAL_ICACHE_RANGE, P4_FLUSH_DCACHE_RANGE,
P4_SYNC_DCACHE_RANGE, and P4_INVAL_DCACHE_RANGE must affect all processors.
Note:
Kernel code calls this function with preemption disabled and interrupts enabled. If interrupts must be
prevented, the function should disable interrupts.
Note:
This function is required to insert preemption points when cache operations lead to unbounded execu-
tion time.
Note:
The memory region specified by start and size always refers to a mapping in user space. The kernel
validates the memory region before calling this function.
cache_kern Perform data and instruction cache operations on a given kernel space memory region, non- preemptively. A call to this function performs the following operations on the data and instruction caches, depending on op:
• P4_INVAL_ICACHE_RANGE Synchronize instruction cache content with data cache content for
application loading.
• P4_FLUSH_DCACHE_RANGE Write back and invalidate data cache content.
• P4_SYNC_DCACHE_RANGE Write back data cache content.
• P4_INVAL_DCACHE_RANGE Invalidate data cache content.
The memory region defined by start and size always refer to kernel space memory.
Depending on flags, cache content in the cache and memory hierarchy is affected differently by the
operation:
• P4_CACHE_FLAG_CPU affects caches on current processor only.
• P4_CACHE_FLAG_DMA for cache coherency to DMA bus masters.
• P4_CACHE_FLAG_ALL affects all levels of the cache hierarchy. If flags is zero,
P4_CACHE_FLAG_DMA applies.
The operations P4_FLUSH_DCACHE_RANGE, P4_SYNC_DCACHE_RANGE, and P4_IN-
VAL_DCACHE_RANGE affect content in the cache related to the memory region defined by
start and size. Set alias to start for these operations.
The operation P4_INVAL_DCACHE_RANGE requires a writable memory region and additionally writes
back cache content at the beginning and the end of the memory area if the memory area is not properly
aligned. On some architectures or in some processor modes, the operation may be equivalent to
P4_FLUSH_DCACHE_RANGE and write back any data before invalidating caches. The calling code
should be written in a robust way that write back of previous data is acceptable.
The operation P4_INVAL_ICACHE_RANGE additionally handles potential aliases in the instruction
cache in cross address space memory accesses. The operation uses alias to derive the addresses
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
114 Reference
of potential instruction cache aliases in other address spaces. In this case, the caller should preform an
P4_INVAL_ICACHE_RANGE operation on the data memory region, i.e. the memory region or mapping
where data cache content was modified, and provide the memory region where instructions will be
executed from as alias. alias might refer to an address in a different address space. Set alias equal to
start if both regions equal.
Returns:
Upon success, this function returns P4_E_OK, otherwise one of the following error codes is returned to
the caller:
P4_E_NOTIMPL if op or flags are not supported by the PSP.
P4_E_PAGEFAULT if the memory region defined by start and size is not fully mapped in the
caller’s virtual address space.
Note:
The function is mandatory. However, the implementation of the different operations depend on the target
architecture and the PSP implementation.
Note:
On SMP, operations P4_INVAL_ICACHE_RANGE, P4_FLUSH_DCACHE_RANGE,
P4_SYNC_DCACHE_RANGE, and P4_INVAL_DCACHE_RANGE must affect all processors.
Note:
Kernel code calls this function with preemption disabled and interrupts are in an undefined state. If
scheduling or interrupts must be prevented, the function should disable interrupts.
Note:
This function must not have preemption points and is specified to be usable from interrupt handlers
context or an early boot context. The caller must ensure that the specified memory region is small to
prevent unbounded execution time.
Note:
The memory region specified by start and size always refers to a mapping in kernel space.
cache_tps Flush data cache and invalidate instruction cache on a time partition switch. A call to this function flushes the whole data cache if flush_dcache is set, and invalidates the whole instruction cache if inval_icache is set.
Note:
The function is mandatory.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this affects the caches on the calling processor only. An implementation should provide internal
locking, if necessary.
cnsput Print character on system console.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 115
A call to this function print the character on the system console. Before calling this function, the caller
must check with cnspoll if there is enough space in the transmitter.
Note:
The function is mandatory.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
cnsget Read character from system console. A call to this function reads a character from the system console. Before calling this function, the caller must check with cnspoll if there are characters available to receive.
Returns:
Upon success, this function the a character from the console input.
Note:
The function is optional.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
cnspoll Poll system console status. A call to this function returns the system console status. The console status is of type PSP_POLL_RETURN() (see section B.2.2) and contains the number of characters available in the re- ceive buffer and the free space available in the transmit buffer.
Returns:
Upon success, this function return the current console status encoded in PSP_POLL_RETURN() (see
section B.2.2).
Note:
The function is mandatory.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
dbgput Print character on debug console.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
116 Reference
A call to this function print the character on the debug console. Before calling this function, the caller
must check with dbgpoll if there is enough space in the transmitter.
Note:
The function is optional.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
dbgget Read character from debug console. A call to this function reads a character from the debug console. Before calling this function, the caller must check with dbgpoll if there are characters available to receive.
Returns:
Upon success, this function the a character from the console input.
Note:
The function is optional.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
dbgpoll Poll debug console status. A call to this function returns the debug console status. The console status is of type PSP_POLL_RETURN() (see section B.2.2) and contains the number of characters available in the re- ceive buffer, and the free space available in the transmit buffer.
Returns:
Upon success, this function return the current console status encoded in PSP_POLL_RETURN() (see
section B.2.2).
Note:
The function is optional.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking,
idle Idle CPU.
A call to this function idles the current CPU until the next interrupt.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 117
Note:
The function is optional.
Note:
The kernel calls this function with preemption and interrupts enabled.
Note:
On SMP, this function idles the calling processor only. The kernel calls this function without any locks.
machinecheck Dispatcher for machine check or other failure exceptions. A call to this function dispatches a critical failure exception like a machine check exception. The argument regs encodes the current register context. Depending on the ASP, the arguments cause, addr, and aux encode further information on the exception cause. cause is an architecture value and typically encodes the exception vector. Both addr and aux refer to the address and an exception status register. The address in addr can refer to a virtual or physical address. The handler shall dispatch the exception and halt the system or modify the supplied register context in regs to continue normal operation on return of this function. On unrecoverable errors, the function shall direcly halt the system.
Returns:
P4_E_OK The exception is handled and the kernel shall resume execution of the current register
context.
P4_E_STATE The exception cannot be handled by the PSP, but the kernel shall continue with
further exception handling, if possible.
Note:
The function is optional.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function handles the exception on the calling processors. An implementation should
provide internal locking, if necessary.
Note:
If this function is omitted and a critical exception takes place, the system is halted.
hm_panic System panic. The kernel Health-monitoring subsystem will call this function on a system configuration error or kernel panic at runtime. The argument einfo contains information about the error condition such as the register context at the time of the fault (which is NULL if no register context is available), and (if available) a textual description of the error. The handler shall dispatch the panic and halt the system. On SMP, this function should halt all the processors.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
118 Reference
On SMP, the function is called with interrupt disabled on the processor that triggered the panic. If a
panic concurrently happens on a different CPU than the one currently processing the panic, that CPU
will enter the callback board_halt with P4_PSP_STOP flag. (see also the board_halt callback).
If this function is provided, the kernel will not print any panic message. If this function is omitted, a panic
message will be printed and the system will be halted with a call to board_halt.
If provided, this function must not return.
Note:
The HM message in einfo may not be NUL terminated.
Note:
The function is optional. The KDEV driver drv_alert_panic_t callbacks are invoked only on the CPU
triggering the panic before calling this callback.
The PSP callback hm_notify (if provided) is invoked only on the CPU triggering the panic before calling
this callback.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, this function should halt all processors. An implementation should provide internal locking, if
necessary.
tp_switch Time partition switch hook. A call to this function is issued on switch to another time partition. The argument tp_window refers to the next time partition window that is switched to, and the argument flags denotes the flags from kernel’s time partition table flags field (P4_TPTABLE_FLAGs). flags contain the kernel-related window flags in the tp_window structure, extended with the switch-specific flags (e.g., P4_TPTABLE_FLAG_FIRST or P4_TPTABLE_FLAG_CHANGE).
Note:
If implemented, the PSP has to test for the bits in the flags field and perform the corresponding
cache/TLB flush/invalidation. For TLB invalidation, the PSP must invoke the p4arch_tlb_inval() call-
back provided by the ASP. This ensures proper locking of TLB management on some architectures.
Note:
The function is optional.
Note:
The kernel calls this function with interrupts disabled while holding a per-CPU spinlock. The function
must not block.
Note:
On SMP, this function handles the time partition switch on the calling processor.
start_cpu Start other processor.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 119
A call to this function starts another processor cpuid. The other processor calls the function entry on a
boot stack reserved by the PSP. The first argument to the called function is the processor ID itself.
Returns:
Upon success, this function returns P4_E_OK, otherwise a return code of P4_E_NOENT denotes an
error.
Note:
The function is mandatory for SMP PSPs.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, the caller is always the boot processor. Because the boot processor has ID 0, cpuid is always
>= 1. The boot processors starts one processor after the other.
reschedule Enforce rescheduling on other processor. A call to this function enforces rescheduling on processor cpuid. An implementation usually sends a rescheduling IPI to the target processor.
Note:
This function must make sure that any previous memory operations on the calling processors are visible
on processor cpuid before the target processor receives the interrupt.
Note:
The function is mandatory for SMP PSPs.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary. cpuid never references the
calling processor.
ipi_tlb_inval Send TLB-invalidation IPI to mask of processors. A call to this function broadcasts an IPI to all processors set in cpumask for remote processor TLB invalidation (TLB shootdown). All affected processors should invoke the p4arch_tlb_inval_handler() callback provided by the ASP. This function is called by the kernel during memory mapping operations.
Note:
This callback is required on x86 and on PowerPC Book-E architectures. The function is optional for
SMP PSPs, if the architecture supports efficient TLB invalidation on all processors.
Note:
The kernel calls this function with interrupts enabled.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
120 Reference
Note:
On SMP, an implementation should provide internal locking, if necessary. cpumask may reference the
calling processor as well.
ipi_poll Poll IPI state on current processor. A call to this function polls for pending IPI requests on the calling processor and handles them. When a TLB invalidation IPI is pending on the processor, this function should invoke the p4arch_tlb_inval_han- dler() callback provided by the ASP. This function is called by the kernel with interrupts disabled while trying to acquire locks.
Note:
The function is optional for SMP PSPs. However, if the architecture supports efficient TLB invalidation
on all processors, the function can be empty.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary.
current_cpu Retrieve the current CPU. A call to this function will return the ID of the current processor.
Returns:
The function is mandatory for SMP PSPs. On a uniprocessor system, this function shall return 0 as
CPU ID. On a SMP, this function shall return the ID of the current CPU.
Note:
The kernel may call this function with interrupts enabled or disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary.
get_ts Get CPU time stamp counter. When implemented, this function returns the current value of the CPU’s time stamp counter.
Returns:
This function returns the time stamp counter.
Note:
The function is mainly used for accurate time stamps in tracing and is only necessary when an architec-
ture does not support a user readable time stamp counter via P4_GET_TS().
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 121
tp_sync Synchronise time partition switching. A call to this function can wait for an event at which to synchronize time partition switching. This function is invoked by the kernel upon time partition schema switch and on every major time partition switch. When invoked, the function may perform a (re) synchronization to external hardware. Both time partition schema switch and major time partition switches happens synchronously (within hardware timer jitter) on all CPUs available in the system. The callback is only invoked on the CPU indicated as sync_cpu in the new schema. The other CPUs busy waits in kernel for the callback to return. The two types of switches are identified via the mode parameter, which is set to P4_TP_SYNC_INIT on schema switch, and to P4_TP_SYNC_MAJOR on major time partition switch. The function also passes the reference time which the kernel computed as start of the time partition window switching to. If synchronising, this function may alter this value to return a corrected conceptual start of the current window which the kernel will base subsequent switches on. Usage of this callback includes e.g., stalling the system to synchronize the time with an external time source. Note that UniversalisOS requires the PSP to always provide a monotonic non-decreasing time source on each CPU (i.e., one thread requesting a time value should always see a non-decreasing time value).
Returns:
This function returns the system time at which the new time partition the kernel currently switches to
has conceptually started. This is the time partition window start time that can also be read from user
space via p4_timepart_window_get_attr().
Note:
The function is optional.
Note:
The kernel calls this function with interrupts disabled and from ticker interrupt context.
Note:
On SMP systems: the kernel invokes this function only on one CPU (the "sync_cpu") while the other
CPUs waits in kernel for the callback to return.
io_phys_to_kernel Retrieve the mapped kernel virtual address of a given physical I/O resource. The PSP may have created mappings to I/O resources at boot time for later use by kernel KDEV drivers. KDEV drivers can gain access to these I/O resources using this call. This callback is used to ask the PSP for the kernel virtual address of the physical I/O resource described by phys and size. Depending on the architecture and on the PSP implementation, this callback returns a mapped kernel virtual address if the I/O resource is already accessible, or NULL if the I/O resource is not available. This function has no mapping attributes, since KDEV driver and PSP need to already have knowledge about the correct mapping required for the I/O resources to be accessed, which is typically strongly uncached (P4_M_C_UC). The returned mapping is at least readable and writeable, but may not be executable.
Returns:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
122 Reference
The virtual address in kernel space where phys is mapped. The mapped area is at least size bytes
large. In case of an error, this function return NULL. The indicates that the PSP is not able to retrieve
an existing mapping for a resource at address phys, or the available mapped area is smaller than the
given size, or no appropriate PSP setup took place at boot time.
Note:
The function is optional.
Note:
On SMP, an implementation should provide internal locking, if necessary.
io_map_kernel Map a given physical I/O resource into the kernel virtual address space. At boot time, a KDEV driver might need access to I/O resources. This callback is used to request the PSP to map the physical I/O resource described by phys and size into the kernel virtual address space with the cache attributes specified in access. Depending on the architecture and on the PSP implementation, this callback may use an existing mapping to the given I/O resource, or NULL if the I/O resource can not be mapped. The cache attributes in access comprise the P4_M_C attributes of the UniversalisOS mapping API. The cache attributes may be ignored. If P4_M_C_UPDATE is not set, e.g. access has the value zero, the PSP should assume a strongly uncached mapping (P4_M_C_UC). Access permissions may be ignored and do not need to be specified. If P4_M_UPDATE is not set, e.g. access has the value zero, the PSP should assume P4_M_READ and P4_M_WRITE, but not P4_M_EXEC.
Returns:
The virtual address in kernel space where phys is mapped. The mapped area is at least size bytes
large. In case of an error, this function return NULL. The indicates that the PSP is not able to retrieve an
existing mapping for a resource at address phys, or the available mapped area is smaller than the given
size, or no appropriate PSP setup took place at boot time, or the PSP failed to create an appropriate
mapping.
Note:
The function is optional.
Note:
This function is only called during boot time before other CPUs are online. Thus, no internal locking is
required for SMP correctness.
hm_notify Notification call to alert the PSP of any HM events. When implemented, the PSP will be notified of the Health-Monitoring events that are managed by the kernel’s health-monitoring subsystem. einfo is initialized by the kernel with the appropriate information on the currently managed error.
Note:
This function is optional. In case of a panic, this function is called before the panic callback only on the
CPU that triggers the panic.
Note:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 123
The HM message in einfo may not be NUL terminated.
Note:
The kernel calls this function with interrupts disabled.
Note:
On SMP, an implementation should provide internal locking, if necessary.
Associated Data Type
P4_psp_api_t The PSP API, called by the kernel.
B.2.2 Defines
P4_MODE_COLD_START
Description:
Doing initialization after cold boot.
PSP_POLL_RXAVAIL (x) No. of RX chars available.
PSP_POLL_TXSPACE (x) No. of TX chars available.
PSP_POLL_RETURN (rxrdy, txrdy) Macro to encode return value for cnspoll. Description: Return value of cnspoll and dbgpoll PSP functions is a bitwise OR of values returned by PSP_POLL_RXAVAIL and PSP_POLL_TXSPACE macros
B.2.3 Data Type Definitions
P4_psp_api_t The PSP API, called by the kernel. The PSP API contains a list of optional and mandatory callbacks into the PSP the PSP must provide to the kernel. For each callback, the documentation states whether the kernel calls the callback with interrupts enabled or disabled. If scheduling or interrupts must be prevented, the call should disable interrupts. On SMP, the kernel may call the callbacks with a specific locks or not. The documentation states if the implementation of the callbacks requires further synchronization, e.g., a spinlock, to protect for concurrent calls.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
124 Reference
B.2.4 Function Type Definitions
B.2.4.1 P4_inthandler_t
Interrupt handler data type.
Synopsis:
typedef void(* P4_inthandler_t)(void *priv, P4_intid_t intid)
Description: An interrupt handler data type. The first argument priv is opaque to the PSP and set by the kernel. Note that priv is deprecated and will be removed in future versions. The second argument intid refers to the interrupt ID of the processed interrupt. B.2.4.2 P4_devcallback_t
PSP level device driver callback data type.
Synopsis:
typedef P4_e_t(* P4_devcallback_t)(P4_devid_t devid, P4_uint32_t func, P4_cpureg_t arg1, P4_cpureg_t arg2, P4_cpureg_t arg3, P4_cpureg_t arg4)
Description: An PSP level device driver callback data type. The first argument devid refers to the PSP level device driver ID referenced with this callback. The second argument func defines the sub-function to execute. Up to four further arguments of type P4_cpureg_t can be specified by the corresponding callback. The kernel calls all callbacks with enabled preemption and no locks held, so preemptive scheduling can occur at any time. The callbacks are responsible for additional locking, for example by disabling preemption or interrupts.
Note: The locking model for PSP level device driver callbacks changed in UniversalisOS 3.5 to enable fine grained locking in the PSP for better synchronization on multi-processor systems.
Note: PSP level device drivers are deprecated and their support will be removed in future versions. Consider using KDEV drivers instead.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
PSP Entry Points 125
B.2.5 Enumerations
Enumeration type P4_tp_sync_mode_t
Synchronization Steps During Time Partition Switching. This enum is used for the tp_sync callback for indicating which type of time partition switch synchronization is currently being done. The callback is invoked on the synchronization CPU only. The other CPUs busy waits for the callback to return before proceeding.
Name Description P4_TP_SYNC_INIT INIT synchronization upon schema switch (either immediate or at major time frame).
P4_TP_SYNC_MAJOR Synchronization at major time frame (no schema switch).
P4_TP_SYNC_NOSYNC No Synchronization needed.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
126 Reference
B.3 Kernel Services
This section describes service API provided by the kernel to the PSP. The PSP can also call routines that are documented in libstand-reference-manual.pdf.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Services 127
B.3.1 Functions
B.3.1.1 p4_psp_int_is_granted
Check if an interrupt is granted to the current task.
Synopsis:
P4_bool_t p4_psp_int_is_granted(P4_intid_t intno)
Description: A call to this function will return TRUE if the interrupt intno is granted to the currently executing thread’s task.
Note: This function must not be called from interrupt context. Execution context: >=P4_BOOT_STAGE_COMPLETED, threaded
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
128 Reference
B.3.1.2 p4_kernel_rom_publish
Define additional ROM images at boot time.
Synopsis:
void p4_kernel_rom_publish(P4_phys_addr_t start, P4_size_t size, P4_bool_t cacheable)
Description: A call to this function will add the memory region at physical address start with non-zero size bytes of memory to the kernel’s list of ROM images exported in the kernel info page. The ROM image is either available as cacheable or uncacheable mapping.
Note: The kernel panics if too many ROM image are added. The boot ROM image is already included implicitly by the kernel, and the kernel info page accepts to P4_NUM_ROM_DESC-1 additional ROM images.
Note: In previous UniversalisOS versions, additional ROM images were published by P4_MRT_ROM entries in the memory list. Execution context: =P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Services 129
B.3.1.3 p4_kernel_assign_mem
Assign a free memory region to the kernel memory allocator at boot time.
Synopsis:
void p4_kernel_assign_mem(P4_phys_addr_t phys_addr, P4_size_t size)
Description: A call to this function will assign a free memory region at physical address phys_addr with non-zero size bytes of memory to the kernel’s memory allocator. phys_addr and size must refer to a mapped region in kernel space, and the memory region must be mapped cached. Further, the memory region must have a constant virtual to physical offset as defined by the ASP. The kernel panics if the memory region is already assigned to the kernel memory allocator, either as "free" or "temporarily used" memory.
Note: Assignment of memory to the memory allocator is only allowed during system startup. Assigned memory can be allocated afterwards via p4_kernel_balloc() (see section B.3.1.5).
Note: This call replaces memory region entries of type P4_MRT_URW. Execution context: =P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
130 Reference
B.3.1.4 p4_kernel_assign_tmp
Assign a temporarily used memory region to the kernel memory allocator at boot time.
Synopsis:
void p4_kernel_assign_tmp(P4_phys_addr_t phys_addr, P4_size_t size)
Description: A call to this function will assign a temporarily used memory region at physical address phys_addr with non-zero size bytes of memory to the kernel’s memory allocator. phys_addr and size must refer to a mapped region in kernel space, and the memory region must be mapped cached. Further, the memory region must have a constant virtual to physical offset as defined by the ASP. The kernel panics if the memory region is already assigned to the kernel memory allocator, either as "free" or "temporarily used" memory. The kernel panics if too many tmp regions are assigned. The maximum number is 4 entries.
Note: Assignment of memory to the memory allocator is only allowed during system startup. The temporarily used memory is later reclaimed by the kernel after the boot phase ends.
Note: This call replaces memory region entries of type P4_MRT_TMP. Execution context: =P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Services 131
B.3.1.5 p4_kernel_balloc
Allocates memory by a given size and alignment at boot time.
Synopsis:
void* p4_kernel_balloc(P4_size_t size, P4_address_t align)
Description: A call to this function will allocate size bytes of memory with an alignment of align. size must be greater than zero. The alignment must be a power of two, or zero. The kernel always applies the architectural minimum alignment. The kernel panics if the memory allocation fails, i.e. not enough memory is available, or the alignment requirement can not be satisfied.
Returns: This function returns the virtual start address of the allocated memory.
Note: Memory allocation is only allowed during system startup, after free memory has been assigned to the kernel’s memory allocator via p4_kernel_balloc_assign().
Note: The returned memory is mapped cached. Execution context: <=P4_BOOT_STAGE_CPU_ONLINE,
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
132 Reference
B.3.1.6 p4_kernel_set_debugger_attached
Notifies the kernel that a debugger was attached (or removed).
Synopsis:
void p4_kernel_set_debugger_attached(P4_bool_t attached)
Description: A call to this function sets kglobal_info.debugger_attached to the value provided as argument. Thus, the PSP is able to tell the kernel that a debugger is attached.
Note: This function may only be called from PSP before entering p4_main() (see section B.4.1.2). Execution context: <=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Entry 133
B.4 Kernel Entry
This section describes the kernel entry point used by the PSP to pass control to the kernel at startup.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
134 Reference
B.4.1 Functions
B.4.1.1 p4_early_init
Early initialization.
Synopsis:
P4_e_t p4_early_init(P4_psp_descriptor_t *psp)
Description: This early initialization is mandatory for PSPs to use any services offered by the kernel. The kernel will initialize some services to be usable by the PSP, e.g. drv_poke(_data), so that drivers can be invoked early. The function should not be invoked multiple times. Subsequent calls are ignored. The early boot phase takes place on the first CPU only, before the other CPUs are initialized. When this function is invoked, the following fields must be initialized in the PSP descriptor: • psp->api_version • psp->romheader • psp->arch.ram_phys_base (ASP specific) • psp->api.board_halt
The following function pointers may be initialized at any time before or after invocation of p4_early_init() (see section B.4.1.1).
• psp->api.cnspoll
• psp->api.cnsput
After p4_early_init() (see section B.4.1.1) is invoked and after these function are set to non-NULL, console printing will work. If p4_early_init() (see section B.4.1.1) is invoked and while .cnspoll is still NULL, console printing will print nothing. If .cnspoll is non-NULL, then .cnsput must be non-NULL, too. Other fields need only be initialized before calling p4_main. Subsystems available after static init include: drv_poke(), basic HM handling (panicking), p4_rom_get_header() (see section B.9.5.1), p4_rom_get_vmit() (see section B.9.5.2), p4_rom_file_find() (see section B.9.5.14), p4_prop_*() functions and the memory allocator.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Entry 135
B.4.1.2 p4_main
Kernel entry point function.
Synopsis:
P4_e_t p4_main(P4_psp_descriptor_t *psp)
Description: Function p4_main() (see section B.4.1.2) is the entry point of the kernel. It is the only function of the kernel referenced directly by the PSP. The PSP must pass the same descriptor as in p4_early_init.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
136 Reference
B.5 Atomic Operations
This section describes atomic operations, based on exported ASP atomics. The atomic operations provide the same interface than the user atomics, and it’s intended for use from both KDEV and PSP interfaces.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 137
B.5.1 Functions
B.5.1.1 p4_atomic_fetch_and_cas_relaxed
Atomic compare and swap operation, relaxed version without barriers.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_cas_relaxed(P4_atomic_t *atomic, P4_uint32_t oldval, P4_uint32_t newval)
Parameters: atomic IN: atomic data type oldval IN: old value which is expected in "atomic" newval IN: new value which is written to "atomic"
Returns: Returns the previous value of "atomic".
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
138 Reference
B.5.1.2 p4_atomic_fetch_and_cas
Atomic compare and swap operation (return previous value)
Synopsis:
P4_uint32_t p4_atomic_fetch_and_cas(P4_atomic_t *atomic, P4_uint32_t oldval, P4_uint32_t newval)
Parameters: atomic IN: atomic data type oldval IN: old value which is expected in "atomic" newval IN: new value which is written to "atomic"
Returns: Returns the previous value of "atomic".
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 139
B.5.1.3 p4_atomic_cas_relaxed
Atomic compare and swap operation, relaxed version without barriers.
Synopsis:
P4_bool_t p4_atomic_cas_relaxed(P4_atomic_t *atomic, P4_uint32_t oldval, P4_uint32_t newval)
Parameters: atomic IN: atomic data type oldval IN: old value which is expected in "atomic" newval IN: new value which is written to "atomic"
Returns: Returns TRUE if the CAS operation succeeded.
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
140 Reference
B.5.1.4 p4_atomic_cas
Atomic compare and swap operation.
Synopsis:
P4_bool_t p4_atomic_cas(P4_atomic_t *atomic, P4_uint32_t oldval, P4_uint32_t newval)
Parameters: atomic IN: atomic data type oldval IN: old value which is expected in "atomic" newval IN: new value which is written to "atomic"
Returns: Returns TRUE if the CAS operation succeeded.
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 141
B.5.1.5 p4_atomic_read
Atomic reading operation.
Synopsis:
P4_uint32_t p4_atomic_read(P4_atomic_t *atomic)
Parameters: atomic IN: atomic data type
Returns: Returns value in "atomic".
Note: Atomic read and write operations ensure that (on a given CPU) the compiler will preserve the order of dependent memory accesses and that the order of interleaved load store on the same memory location will be preserved.
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Note: Atomic operations are only guaranteed to work properly on cached mapped memory (memory mapped with P4_M_C_WB attributes).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
142 Reference
B.5.1.6 p4_atomic_swap
Atomic swap operation.
Synopsis:
P4_uint32_t p4_atomic_swap(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value assigned atomically to "atomic"
Returns: Returns value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 143
B.5.1.7 p4_atomic_write
Atomic assignment operation.
Synopsis:
void p4_atomic_write(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value assigned atomically to "atomic"
Returns: Nothing.
Note: Atomic read and write operations ensure that (on a given CPU) the compiler will preserve the order of dependent memory accesses and that the order of interleaved load store on the same memory location will be preserved.
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Note: Atomic operations are only guaranteed to work properly on cached mapped memory (memory mapped with P4_M_C_WB attributes).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
144 Reference
B.5.1.8 p4_atomic_add
Atomic ADD operation.
Synopsis:
void p4_atomic_add(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value added atomically to "atomic"
Returns: Nothing.
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Note: "Negative" values may be used as argument val to implement subtraction using unsigned int semantics, thus avoiding possible integer overflows.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 145
B.5.1.9 p4_atomic_inc
Atomic increment operation.
Synopsis:
void p4_atomic_inc(P4_atomic_t *atomic)
Parameters: atomic IN: atomic data type
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
146 Reference
B.5.1.10 p4_atomic_dec
Atomic decrement operation.
Synopsis:
void p4_atomic_dec(P4_atomic_t *atomic)
Parameters: atomic IN: atomic data type
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 147
B.5.1.11 p4_atomic_and
Atomic AND operation.
Synopsis:
void p4_atomic_and(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value AND-ed atomically to "atomic"
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
148 Reference
B.5.1.12 p4_atomic_bic
Atomic AND NOT (bit clear) operation.
Synopsis:
void p4_atomic_bic(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value AND NOT-ed atomically to "atomic"
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 149
B.5.1.13 p4_atomic_or
Atomic OR (bit set) operation.
Synopsis:
void p4_atomic_or(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value OR-ed atomically to "atomic"
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
150 Reference
B.5.1.14 p4_atomic_xor
Atomic XOR operation.
Synopsis:
void p4_atomic_xor(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value XOR-ed atomically to "atomic"
Note: If required, additional memory barriers should be used to ensure appropriate (cross CPU) memory ordering.
Returns: Nothing.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 151
B.5.1.15 p4_atomic_fetch_and_add
Atomic ADD operation.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_add(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value added atomically to "atomic"
Returns: Returns value in "atomic" before modification.
Note: "Negative" values may be used as argument val to implement subtraction using unsigned int semantics, thus avoiding possible integer overflows.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
152 Reference
B.5.1.16 p4_atomic_fetch_and_and
Atomic AND operation.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_and(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value AND-ed atomically to "atomic"
Returns: Returns value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 153
B.5.1.17 p4_atomic_fetch_and_bic
Atomic AND NOT (bit clear) operation.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_bic(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value AND NOT-ed atomically to "atomic"
Returns: Returns value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
154 Reference
B.5.1.18 p4_atomic_fetch_and_or
Atomic OR (bit set) operation.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_or(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value OR-ed atomically to "atomic"
Returns: Returns value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 155
B.5.1.19 p4_atomic_fetch_and_xor
Atomic XOR operation.
Synopsis:
P4_uint32_t p4_atomic_fetch_and_xor(P4_atomic_t *atomic, P4_uint32_t val)
Parameters: atomic IN: atomic data type val IN: value XOR-ed atomically to "atomic"
Returns: Returns value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
156 Reference
B.5.1.20 p4_atomic_barrier
Full memory barrier.
Synopsis:
void p4_atomic_barrier(void)
Returns: Nothing.
Note: This memory barrier enforces ordering of load and store operations: All memory operations before this barrier succeeded and are guaranteed to be globally visible. Any memory operation after this barrier will not be speculated before the barrier. This relates to a full load-load, load-store, store-load, and store-store barrier or stronger on the target hardware.
Note: This memory barrier is intended to synchronize data in cached memory only. For ordering of writes to I/O resources, see p4_io_write_barrier() (see section B.5.1.25).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 157
B.5.1.21 p4_atomic_acquire_barrier
Acquire barrier.
Synopsis:
void p4_atomic_acquire_barrier(void)
Returns: Nothing.
Note: Any memory operation after this barrier will not be speculated before the barrier. This relates to a load-load and load-store barrier or stronger on the target hardware. Specifically, all load opera- tions that occur before the barrier must have completed before any future memory operations commence.
Note: Memory operations that occur before the barrier may become globally visible after the barrier.
Note: This memory barrier is intended to synchronize data in cached memory only. For ordering of writes to I/O resources, see p4_io_write_barrier() (see section B.5.1.25).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
158 Reference
B.5.1.22 p4_atomic_release_barrier
Release barrier.
Synopsis:
void p4_atomic_release_barrier(void)
Returns: Nothing.
Note: All memory operations before this barrier succeeded and are guaranteed to be globally visible. This relates to a load-store and store-store barrier or stronger on the target hardware. Specifically, all memory operations that occur before the barrier must have completed before any future write operations commence.
Note: Memory operations that occur after the barrier may become globally visible before the barrier.
Note: This memory barrier is intended to synchronize data in cached memory only. For ordering of writes to I/O resources, see p4_io_write_barrier() (see section B.5.1.25).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 159
B.5.1.23 p4_atomic_read_barrier
Read memory barrier.
Synopsis:
void p4_atomic_read_barrier(void)
Returns: Nothing.
Note: The read memory barrier enforces ordering of load operations: All load operations before this barrier have completed. Any load operation after this barrier will not be speculated before the barrier. This relates to a load-load barrier or stronger on the target hardware.
Note: This memory barrier is intended to synchronize data in cached memory only. For ordering of writes to I/O resources, see p4_io_write_barrier() (see section B.5.1.25).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
160 Reference
B.5.1.24 p4_atomic_write_barrier
Write memory barrier.
Synopsis:
void p4_atomic_write_barrier(void)
Returns: Nothing.
Note: The write memory barrier enforces ordering of store operations: All store operations before this barrier succeeded and are guaranteed to be globally visible. Any store operation after this barrier will not be speculated before the barrier. This relates to a store-store barrier or stronger on the target hardware.
Note: This memory barrier is intended to synchronize data in cached memory only. For ordering of writes to I/O resources, see p4_io_write_barrier() (see section B.5.1.25).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 161
B.5.1.25 p4_io_write_barrier
Write memory barrier for I/O resources.
Synopsis:
void p4_io_write_barrier(void)
Returns: Nothing.
Note: The write memory barrier enforces ordering of store operations: All store operations to I/O resources before this barrier succeeded and are guaranteed to be globally visible. This relates to a store-load and store-store barrier or stronger on the target hardware.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
162 Reference
B.5.1.26 p4_atomic_ptr_write
Atomic assignment operation of pointer.
Synopsis:
void p4_atomic_ptr_write(P4_atomic_ptr_t *atomic, P4_address_t val)
Parameters: atomic IN: atomic pointer data type val IN: pointer assigned atomically to "atomic"
Returns: Nothing.
Note: Atomic operations are only guaranteed to work properly on cached mapped memory (memory mapped with P4_M_C_WB attributes).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 163
B.5.1.27 p4_atomic_ptr_read
Atomic reading operation of pointer.
Synopsis:
P4_address_t p4_atomic_ptr_read(P4_atomic_ptr_t *atomic)
Parameters: atomic IN: atomic pointer data type
Returns: Returns pointer value in "atomic".
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
164 Reference
B.5.1.28 p4_atomic_ptr_fetch_and_cas
Atomic compare and swap operation of pointers.
Synopsis:
P4_address_t p4_atomic_ptr_fetch_and_cas(P4_atomic_ptr_t *atomic, P4_address_t oldval, P4_address_t newval)
Parameters: atomic IN: atomic pointer data type oldval IN: old pointer value which is expected in "atomic" newval IN: new pointer value which is written to "atomic"
Returns: Returns the previous pointer value of "atomic".
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Atomic Operations 165
B.5.1.29 p4_atomic_ptr_cas
Atomic compare and swap operation of pointers.
Synopsis:
P4_bool_t p4_atomic_ptr_cas(P4_atomic_ptr_t *atomic, P4_address_t oldval, P4_address_t newval)
Parameters: atomic IN: atomic pointer data type oldval IN: old pointer value which is expected in "atomic" newval IN: new pointer value which is written to "atomic"
Returns: Returns TRUE if the CAS operation succeeded.
Note: The CAS operation compares the value of oldval with the value stored in "atomic". If values match, the newval is written to "atomic" and CAS operation succeeded. If oldval is different from the value stored in "atomic", CAS operation is unsuccessful and newval is not written. The compare and conditional store are executed as single atomic operation.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
166 Reference
B.5.1.30 p4_atomic_ptr_swap
Atomic swap operation of pointers.
Synopsis:
P4_address_t p4_atomic_ptr_swap(P4_atomic_ptr_t *atomic, P4_address_t val)
Parameters: atomic IN: atomic pointer data type val IN: pointer value assigned atomically to "atomic"
Returns: Returns pointer value in "atomic" before modification.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Spinlocks 167
B.6 Spinlocks
This section describes the spin lock implementation, based on exported ASP spinlocks. The interface is intended for both KDEV and PSP level. Similar interface than the user spin locks, except p4_spin_lock_irqsave() (see section B.6.3.4) and p4_spin_unlock_irqrestore() (see section B.6.3.5), which only exists at PSP and KDEV level.
B.6.1 Defines
P4_SPIN_INIT Spinlock initializer. Description: Static initializer for spinlocks, sets the lock to unlocked state.
B.6.2 Data Type Definitions
P4_spin_t Spinlock. This opaque type is used for spinlocks.
Note:
Spinlocks are used as synchronization means in multi-processor environments. Do not use in single-
processor environments. The spinlock implementation does not support nested acquiration of an al-
ready locked spinlock. To prevent deadlocks and lock-holder preemption problems, threads should
acquire locks only when having highest priority among other competing threads.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
168 Reference
B.6.3 Functions
B.6.3.1 p4_spin_init
Initialize a spinlock to unlocked state.
Synopsis:
void p4_spin_init(P4_spin_t *lock)
Parameters: lock INOUT: Spinlock object
Description: This function initializes spinlock lock to unlocked state. Execution context: >=P4_BOOT_STAGE_EARLY, inde- pendent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Spinlocks 169
B.6.3.2 p4_spin_lock
Acquire spin lock, spin in the contended case.
Synopsis:
void p4_spin_lock(P4_spin_t *lock)
Parameters: lock INOUT: Spinlock object
Description: This function tries to acquire the spinlock lock. If the lock is contended, the functions performs busy waiting until it acquires the lock.
Note: Spinlocks must be initialized before using. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
170 Reference
B.6.3.3 p4_spin_unlock
Release spin lock.
Synopsis:
void p4_spin_unlock(P4_spin_t *lock)
Parameters: lock INOUT: Spinlock object
Description: This function releases the previously acquired spinlock lock.
Note: Spinlocks must be initialized before using. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Spinlocks 171
B.6.3.4 p4_spin_lock_irqsave
Acquire spin lock, with interrupt disabled; spin in the contended case.
Synopsis:
P4_cpureg_t p4_spin_lock_irqsave(P4_spin_t *lock)
Parameters: lock INOUT: Spinlock object
Description: This function disables interrupts first and then tries to acquire the spinlock lock. If the lock is contended, the functions performs busy waiting until it acquires the lock.
Returns: The previous interrupt state.
Note: Spinlocks must be initialized before using. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
172 Reference
B.6.3.5 p4_spin_unlock_irqrestore
Release spin lock and restore interrupt state.
Synopsis:
void p4_spin_unlock_irqrestore(P4_spin_t *lock, P4_cpureg_t flags)
Parameters: lock INOUT: Spinlock object flags IN: previous interrupt state
Description: This function releases the previously acquired spinlock lock and restore the previous interrupt state flags on the calling CPU.
Note: Spinlocks must be initialized before using. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Linkage specific Definitions 173
B.7 Linkage specific Definitions
This section describes the linkage specific definitions.
B.7.1 Structure Definitions
B.7.1.1 struct P4_recovertable_str
exception recovery table entry On an exception in kernel space, the ASP scans the recovery table: if the exception’s program counter points to "faulter", it is set to the recovery routine "recover" by the ASP and the kernel can continue execution.
Synopsis: struct P4_recovertable_str { unsigned long faulter; unsigned long recover; };
Structure Element Description: faulter recover
Associated Data Type
P4_recovertable_t exception recovery table entry
B.7.2 Data Type Definitions
P4_recovertable_t exception recovery table entry On an exception in kernel space, the ASP scans the recovery table: if the exception’s program counter points to "faulter", it is set to the recovery routine "recover" by the ASP and the kernel can continue execution.
B.7.3 Variables
_recovertable_start start of exception recovery table _recovertable_end end (element after) exception recovery table _ftext start of text segment _etext end of text segment _fdata start of data segment _edata end of data segment _bss_start start of bss segment _end end of bss segment
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
174 Reference
B.8 Compiler specific Definitions
This section describes the compiler specific definitions.
B.8.1 Defines
_RECOVERTABLE (fault, recover) Add entry to recovery table, stringified version for inline assembly.
Parameters:
IN fault: address where exception can happen
IN recover: address of recovery code
Note:
Recovery table entries must be kept in program order, because the table is ordered with ascending
addresses.
__annotate (EXP) Annotate function declarations with an arbitrary string. As of now, this macro only expands to something meaningful under certain diagnostic conditions (= when compiled with llvm/clang).
P4_ANNOTATE_CFLOW (EXP) Annotate the control flow within a function with an arbitrary string. To be used at the beginning of a scope only! As of now, this macro only expands to something meaningful under certain diagnostic conditions (= when compiled with llvm/clang).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 175
B.9 Kernel Driver and PSP Common Functionalities
This section describes the data structures and services defined by the Kernel for use in both Kernel Drivers and PSPs. Throughout this API, invalid pointers to function will cause undefined behaviour unless otherwise specified. This explicitly holds for NULL pointers, which the KDEV service API forbids by default. Functions that do handle NULL pointers specially will explicitly contain documentation about the special case.
B.9.1 Structure Definitions
B.9.1.1 struct P4_hm_info_str
Health-Monitoring Error descriptor. This structure groups the information needed to manage a health-monitoring error. It’s accessible from both kernel, psp, and KDEV drivers.
Synopsis:
struct P4_hm_info_str { P4_uid_t faulter; P4_uint32_t domain; P4_hm_type_t type; P4_uint32_t id; P4_cpuid_t cpu; P4_panic_cause_t panic_cause_id; const void * msg; P4_size_t size; P4_uint32_t code; P4_hm_level_t level; P4_hm_mac_t mac; P4_hm_pac_t pac; P4_uint32_t module_notify; P4_uint32_t part_notify; struct P4_hm_info_str * unhandled; P4_regs_t * regs; P4_cpureg_t arg; };
Structure Element Description: faulter UID of the faulting thread domain Domain of the component that reported the error type Type of the error that was reported id Identifier of the error cpu CPU where the error occurred panic_cause_id Compact panic cause identifier in the case of a panic msg Pointer to HM message. The pointer is a kernel space pointer, allocated by either the psp/kdev caller invoking a hm_raise() or on the stack of the thread injecting the hm event via hm_inject. The msg may not be NUL terminated. The maximum size is P4_HM_MAX_MSG_SIZE. size Size of the message code Error code that is relayed to the user space exception handler. This is defined by the PartitionHMTable.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
176 Reference
level Determines the level on which an error is to be handled. P4_HM_LEVEL_USER: Relay the error to
the user space exception handler. P4_HM_LEVEL_PARTITION: Relay the error to the partition error
handler. P4_HM_LEVEL_MODULE: Handle the error in the kernel itself.
The level is determined by two steps:
Read from HM tables
Fall back to next lower level if the respective handler is not available
mac Module level action Is determined from the ModuleHMTable in case of a module scope injection and
from the partition’s MultiPartitionHMTable in the case of a partition scope injection. Even if a MultiPar-
titionHMTable defines an error level of VM_HM_EL_PARTITION, it still defines a module level action in
case it has to fall back.
pac Partition level action Is determined from the PartitionHMTable if an error is to be handled on partition
level or process level. Even if a PartitionHMTable defines an error level of VM_HM_EL_USER, it still
defines a partition level action in case it has to fall back.
module_notify Platform notification for module action. This notification is relayed to a kernel driver before the action is executed. A653P1-4 defines that a platform action is an alternative to the other partition level actions. We implement it as an additional action to be able to fallback to an action if the driver does nothing. If a strict compliant configuration is required, mac/pac should be set to VM_HM_MAC_IGNORE. part_notify Platform notification for partition action. This is just like module_notify, but for a partition level action. If a strict compliant configuration is required, pac should be set to VM_HM_PAC_IGNORE. unhandled Pointer to the original P4_hm_info_t that resulted in an unhandled hm error in the first place. regs Pointer to a CPU register context for kernel panic and userspace register debugging arg Kernel-Panic argument’s
Associated Data Type
P4_hm_info_t Health-Monitoring Error descriptor.
B.9.1.2 struct P4_kglobal_info_str
Kernel global information data. Global information data exported by the kernel.
Synopsis: struct P4_kglobal_info_str { P4_uint32_t num_timepart; P4_uint32_t num_respart; P4_uint32_t num_prio; P4_uint32_t num_kprio; P4_uint32_t num_task; P4_uint32_t num_thread; P4_uint32_t thrinfo_size; P4_uint32_t num_cpu; P4_uint32_t boot_message; P4_uint32_t log_level;
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 177
P4_uint32_t tps_sync;
P4_uint32_t test_flags;
P4_boot_stage_t boot_stage;
P4_bool_t debugger_attached;
};
Structure Element Description: num_timepart Number of configured time partitions. num_respart Number of configured resource partitions. num_prio Number of configured priorities. num_kprio Number of kernel priorities num_task Number of configured tasks. num_thread Number of configured threads per task. thrinfo_size kernel stack size. num_cpu Number of CPUs in the system. boot_message Cached value of property p4/kernel/boot_message log_level Cached value of property p4/kernel/log_level tps_sync Cached value of property p4/kernel/tps_strong_sync test_flags Cached value of property p4/kernel/test_flags boot_stage Booting stage level debugger_attached State variable if a kernel debugger or external hardware debugger is attached. Book-E based PowerPC processors will detect the presence of a hardware debugger at boot time. A hardware debugger or or a software-based kernel debugger should set this flag to TRUE to restrict the kernel from accessing a CPU’s debug registers.
Associated Data Type
P4_kglobal_info_t Kernel global information data.
B.9.2 Defines
p4_prop_read
P4_PROP_GET (no, na, pt, ct)
Convenience macro to query mandatory properties.
Parameters:
no IN: the node from where to search the property
na IN: the name of the property to find relative to no
pt IN: the P4_PROP_T_* type expected for the node
ct IN: the C base type of the pointer type return from this
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
178 Reference
B.9.3 Data Type Definitions
P4_hm_info_t Health-Monitoring Error descriptor. This structure groups the information needed to manage a health-monitoring error. It’s accessible from both kernel, psp, and KDEV drivers. P4_kglobal_info_t Kernel global information data. Global information data exported by the kernel.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 179
B.9.4 Enumerations
Enumeration type P4_thread_status_t
Set of pending status actions for a thread. The thread status set identify the set of possible pending status action that may be asynchonously signaled for a thread. Since these actions are enacted for a thread when the thread returns to userspace, PSP and KDEV drivers may inquire for pending actions in order to activate alternative paths in response to the signaled action (see p4_my_thread_status() (see section B.9.5.31) to retrieve this status information).
Name Description P4_THREAD_STATUS_KILL Pending deletion action
P4_THREAD_STATUS_PREEMPT Pending preemption action
P4_THREAD_STATUS_EXCEPTION Pending exception
P4_THREAD_STATUS_STOPPING Pending stopping
Enumeration type P4_boot_stage_t
Boot stages for kglobal_info.
Name Description P4_BOOT_STAGE_INITIAL Initial boot stage. Code executes on a boot stack in single-processor mode in an undefined context. UniversalisOS services are not available.
P4_BOOT_STAGE_EARLY Early boot stage after calling p4_early_init() (see section B.4.1.1). Code executes on a boot stack in single-processor mode in the context of the idle thread. First UniversalisOS services are available. KDEV poke callbacks are initialized and the KDEV init_drv() callback of each KDEV driver is invoked. The kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service can be used after memory was assigned to it.
P4_BOOT_STAGE_IDLE_TASK First part of main boot stage for kernel initialization after calling p4_main() (see section B.4.1.2). Code executes on the idle thread’s stack in single- processor mode. Most UniversalisOS subsystems are initialized. The PSP api.init() callback is invoked. No KDEV framework initialization callback is invoked here. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family are now available.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
180 Reference
P4_BOOT_STAGE_INIT_PROV First part of main boot stage for kernel initialization after calling p4_main() (see section B.4.1.2). Code executes on the idle thread’s stack in single- processor mode. Most UniversalisOS subsystems are initialized. The KDEV framework is brought up and the init_prov() callbacks for each KDEV provider are invoked. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family are available.
P4_BOOT_STAGE_INIT_PART First part of main boot stage for kernel initialization after calling p4_main() (see section B.4.1.2). Code executes on the idle thread’s stack in single-processor mode. Most UniversalisOS subsystems are initialized. The KDEV framework continues initialising and the init_part() callbacks for each KDEV provider are invoked. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family are available.
P4_BOOT_STAGE_PREPARE_GATE First part of main boot stage for kernel initialization after calling p4_main() (see section B.4.1.2). Code executes on the idle thread’s stack in single-processor mode. Most UniversalisOS subsystems are initialized. The KDEV framework continues initialising and the prepare_gate() callbacks for each KDEV gate are invoked. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family are available.
P4_BOOT_STAGE_INIT_GATE First part of main boot stage for kernel initialization after calling p4_main() (see section B.4.1.2). Code executes on the idle thread’s stack in single-processor mode. Most UniversalisOS subsystems are initialized. The KDEV framework continues initialising and the init_gate() callbacks for each KDEV gate are invoked. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family are available.
P4_BOOT_STAGE_CPU_ONLINE Starting of other processors. Code executes on the idle thread’s stack on each processor. Most UniversalisOS subsystems are initialized. The PSP api.init_cpu() callback is invoked on each processor. For KDEV drivers, the init_cpu() callbacks are invoked. Both the kernel’s boot alloca- tor p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family is now available.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 181
P4_BOOT_STAGE_LATE Late kernel initialization, shortly before scheduling user space. Code executes on the idle thread’s stack on each processor. Most UniversalisOS subsystems are initialized. On the boot CPU, the PSP api.init_late() and the KDEV driver’s init_com- plete() callbacks are invoked. Both the kernel’s boot allocator p4_kernel_balloc() (see section B.3.1.5) service and the partitioned drv_malloc() allocator family is now available. After the callbacks return, temporary memory used at boot time will be reclaimed.
P4_BOOT_STAGE_COMPLETED Boot completed. Code executes multi-threaded on each processor. Both the kernel’s boot allocator and the partitioned drv_malloc() allocator fam- ily can not be used any more, however, the runtime memory allocator is available. The kernel creates and schedules the first user space tasks.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
182 Reference
B.9.5 Functions
B.9.5.1 p4_rom_get_header
Get the romboot header for a given partition.
Synopsis:
const P4_romboot_header_t* p4_rom_get_header(P4_uint32_t respart, const drv_config_header_t **ch_p, const P4_romboot_partrom_t **pr_p)
Parameters: respart IN: the partition for which to get the romboot header, or 0 for the global header. ch_p OUT: a pointer to a pointer the pre-header in generic binary configuration format, with signature, size, CRC, etc., or NULL if the caller is not interested in this information. returned. pr_p OUT: a pointer to a pointer to the partrom where the romboot header is located, or NULL if the caller is not interested in this information.
Description: This will try to traverse the partrom entries to get the romboot header of the romimage for the given partition. If respart is 0, this function will simply return the global romboot header. In this case, *partrom will be NULL. If no romboot header is found for the given partition, this will return NULL. If the romboot header is found, but is invalid or is not for the given partition, and if the partition number is >0, then this function will hm_panic(P4_E_CONFIG). The function will not panic for the global romimage, but return NULL, because it is assumed that the HM panic system may not be up when access to the global romimage is needed at early boot time. This function is designed to be very fast by caching the result internally, so it is not necessary to cache the result of this function in local data structures: just invoke this function whenever needed. This function does not check the full integrity of the global ROM image, since that must have done by the PSP already. However, it does check (once) the full integrity of any partition local ROM images before returning a pointer to any of them. Any such failed integrity check will cause a kernel panic. A missing image, however, results in this function returning NULL.
Returns: NULL if no header is found, otherwise a pointer to a valid romboot header. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 183
B.9.5.2 p4_rom_get_vmit
Get the VMIT from the given resource partition, or the global one.
Synopsis:
const vmitConfiguration_t* p4_rom_get_vmit(P4_uint32_t respart)
Parameters: respart IN: 0 for the global VMIT, otherwis the resource partition of the VMIT to get.
Description: If no romimage or VMIT is found, NULL is returned. If an invalid romimage or an invalid VMIT is found, this will hm_panic(P4_E_CONFIG) if respart is not 0. For the global VMIT, this will not panic, but return NULL instead. This function is available at the very earliest boot time. This function is designed to be very fast by caching the result internally, so it is not necessary to cache the result of this function in local data structures: just invoke this function whenever needed.
Returns: NULL if no romimage for the partition or no VMIT in the RFS is found. Otherwise, returns a valid pointer to the VMIT top level node configuration node. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
184 Reference
B.9.5.3 p4_rom_get_partition
Get the Partition record from the VMIT(s) in the system.
Synopsis:
const vmitPartition_t* p4_rom_get_partition(P4_uint32_t respart)
Parameters: respart IN: The partition ID, with the valid range 1..62.
Description: This is a convenience wrapper that first looks for a partition record in the local VMIT for the given partition ID, and if there is no local VMIT, finds the entry in the global VMIT. If not Partition entry is found, this returns NULL. The returned Partition configuration record and the VMIT structure is tested to ensure that e.g. the Identifier matches and is in range (1..P4_NUM_RESPART-1). Otherwise, a HM event is raised. This function is available at the very earliest boot time. This function is designed to be very fast by caching the result internally, so it is not necessary to cache the result of this function in local data structures: just invoke this function whenever needed.
Returns: NULL if no Partition entry is found in the VMIT, or the Partition entry p with p->Identifier == respart. If respart is out of range, will also return NULL. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 185
B.9.5.4 p4_rom_get_prop
Returns the property file system root node.
Synopsis:
const P4_prop_node_t* p4_rom_get_prop(P4_uint32_t respart)
Parameters: respart IN: The partition ID, with the valid range 0..62.
Description: For a given partition, this function retrieves the root node of the property file system. This can then be used to iterate or find entries with p4_prop_find() (see section B.9.5.5). If the given partition has no property file system (i.e., if there is no partition rom image for that partition), then this functions returns NULL. The returned node, if non-NULL, always has type P4_PROP_T_DIR. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
186 Reference
B.9.5.5 p4_prop_find
Find a node in a directory of the property file system.
Synopsis:
const void* p4_prop_find(const P4_prop_node_t *parent, const char *path, P4_prop_type_t wanted, P4_size_t *size_p)
Parameters: parent IN: the directory node to search. Root nodes may be found using p4_rom_get_prop() (see section B.9.5.4). The result of a previous p4_prop_find() (see section B.9.5.5) with type P4_PROP_T_DIR (or P4_PROP_T_ANY) may be passed back in to search subdirectories. This may be NULL, indicating an empty directory, in which case NULL is returned and size is set to 0. path IN: the name of the file. This may contain ’/’ characters to select subdirectories. wanted IN: The desired type of node to find. size_p OUT: the size of the data. This pointer may be NULL, in which case no size is passed out. Passing NULL makes sense for all wanted settings which return a fixed size payload, and for type P4_PROP_T_STRING, whose payload is NUL-terminated.
Description: This function can search in the root node as returned by p4_rom_get_prop() (see section B.9.5.4) as well as in subdirectories in the property file system. For that, the result of a previous call to p4_prop_find() (see section B.9.5.5) with a type of P4_PROP_T_ANY or P4_PROP_T_DIR can be passed into the function again as the parent pointer. If type is not P4_PROP_T_ANY, this function will check the node type and return NULL unless it is equal. If the node has the desired type, this function will then return a pointer to the payload of the selected type. For wanted == P4_PROP_T_ANY, the returned object has type P4_prop_node_t. This pointer can in turn be used as parent for a subsequent call to p4_prop_find() (see section B.9.5.5) to recurse into that subdirectory. For wanted != P4_PROP_T_ANY, this returns exactly the payload pointer returned by p4_rom_get_data() for the given node. If wanted == P4_PROP_T_BIN, this function also transparently returns data of type P4_PROP_T_IPV4 and P4_PROP_T_MAC for backward compatibility. If this returns NULL, size will be set to 0. If this return non-NULL, size will be set according to p4_prop_get_size() (see section B.9.5.9) for the given node. This funtion is the most generic property reading function and especially good for reading a property path in multiple steps, postponing the error or default value usage until the last step: P4_prop_node_t *n = p4_prop_find(p4_rom_get_prop(0), "foo/device", P4_PROP_T_DIR, NULL, NULL); n = p4_prop_find(n, dev_name, P4_PROP_T_DIR, NULL, NULL); P4_uint32_t baud_rate = 38400; (void)p4_prop_read(&baud_rate, n, "baud_rate", P4_PROP_T_UINT32);
Returns:
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 187
NULL if no file with the given name is found or if the node type does not match. Otherwise, the pointer to the payload of the node, or, for wanted equals P4_PROP_T_DIR or P4_PROPT_T_ANY), the pointer to the node itself. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
188 Reference
B.9.5.6 p4_prop_find_or_panic
Synopsis:
const void* p4_prop_find_or_panic(const P4_prop_node_t *parent, const char *path, P4_prop_type_t wanted, P4_size_t *size_p)
Parameters: parent IN: the directory node to search. Root nodes may be found using p4_rom_get_prop() (see section B.9.5.4). The result of a previous p4_prop_find() (see section B.9.5.5) with type P4_PROP_T_DIR (or P4_PROP_T_ANY) may be passed back in to search subdirectories. This may be NULL, indicating an empty directory, in which case NULL is returned and sz is set to 0. path IN: the name of the file. For the property file system, this may contain ’/’ characters to select subdirec- tories. wanted IN: The desired type of node to find. size_p OUT: the size of the data. This pointer may be NULL, in which case no size is passed out. Passing NULL makes sense for all wanted settings which return a fixed size payload, and for type P4_PROP_T_STRING, whose payload is NUL-terminated.
Description: This is just like p4_prop_find() (see section B.9.5.5), but never returns NULL. Instead of returning NULL in case of a missing property, this will fail with a panic. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 189
B.9.5.7 _p4_prop_read
Alternative API to read fixed size non-generic properties.
Synopsis:
P4_e_t _p4_prop_read(void *buff, const P4_prop_node_t *node, const char *name, P4_prop_type_t wanted)
Parameters: buff OUT: Buffer to write the result to node IN: The parent directory to access a property in name IN: The path in the property directory relative to the passed parent directory. If this is "", then ’node’ is tried to be accessed. wanted IN: The desired type of node to find
Description: This is similar to p4_prop_find() (see section B.9.5.5), but instead of returning a pointer (or NULL) to the property, it will copy the property value to a given buffer. The advantage of this function is that no conditional is required when using this to set the default value: the default value can be written to the buffer before this function is invoked, and if the property does not exist, the buffer will not be overwritten. In contrast to that, when using p4_prop_find() (see section B.9.5.5), an if() is required when handing the default value, and also, the pointer that is returned may be unhandy in the given programming context. The disadvantage of this function compared to p4_prop_find() (see section B.9.5.5) is that only a subset of types are allowed. The first group of allowed types is fixed-size types, for which the value will be directly written to the output value: P4_PROP_T_ADDR, P4_PROP_T_BOOL, P4_PROP_T_DEVICE, P4_PROP_T_INTERRUPT, P4_PROP_T_IPV4, P4_PROP_T_MAC, P4_PROP_T_MEMMAP, P4_PROP_T_PORTMAP, P4_PROP_T_SIZE, P4_PROP_T_UINT32, or P4_PROP_T_UINT64. Furthermore, P4_PROP_T_STRING values can be read, in which case the first argument is assumed to have type ’const char **’, i.e, the string pointer is written to the output buffer. Moreover, P4_PROP_T_ANY and P4_PROP_T_DIR vaues can be read, in which case the first argument is assumed to have type ’const P4_prop_node_t **’, i.e., the property node pointer is written to the output buffer. Like p4_prop_find() (see section B.9.5.5), this will trigger an config error if the node is found but has the wrong type. Usually, this function is the most convenient option to read optional values from the property file system. Using the macro p4_prop_read() is recommended in this case, which adds a type checking assert() when compiling with gcc. For required properties, the macro P4_PROP_GET() (see section B.9.2) is usually the most convenient option for reading the property. The function p4_prop_find() (see section B.9.5.5) is the most generic function, and is also usually the best option to read longer property paths in multiple steps without error checking, because it can be used to simply set the node to NULL, postponing the ’missing property’ error until the final query of the actual property value (usually with p4_prop_read() or P4_PROP_GET() (see section B.9.2)).
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
190 Reference
Example to read an optional property without conditional: P4_uint32_t console_port = 0; (void)p4_prop_read(&console_port, p4_rom_get_prop(0), "board/console", P4_UINT32_T);
Returns: P4_E_OK if the value was found and written into the output buffer. P4_E_NOENT if the value was not found and nothing was written into the output buffer. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 191
B.9.5.8 p4_prop_get_data
Return the pointer to the payload data of the given node.
Synopsis:
const void* p4_prop_get_data(const P4_prop_node_t *n)
Parameters: n IN: The property node get the data from
Description: Return the pointer to the data embedded into the given node. This works exactly like the mechanism that p4_prop_find() (see section B.9.5.5) uses to return the payload pointer, based on the node type. For a node of type P4_PROP_T_DIR, the returned object has type P4_prop_node_dir_t. Since P4_prop_node_t is a union including P4_prop_node_dir_t, pointers to P4_prop_node_dir_t can be interpreted as P4_prop_node_t safely and also be passed back as dir into p4_prop_find() (see section B.9.5.5) to recurse into that subdirectory. For a node of type P4_PROP_T_UINT32, P4_PROP_T_INTERRUPT, or P4_PROP_T_DEVICE, the returned object has type P4_uint32_t. For a node of type P4_PROP_T_UINT64 and P4_PROP_T_ADDR, the returned object has type P4_uint64_t. For a node of type P4_PROP_T_SIZE, the returned object has type P4_size_t. For a node of type P4_PROP_T_MEMMAP, the returned object has type P4_prop_memmap_t. For a node of type P4_PROP_T_PORTMAP, the returned object has type P4_prop_portmap_t. For a node of type P4_PROP_T_STRING, P4_PROP_T_FILE, P4_PROP_T_CONFIG, P4_PROP_T_IPV4, P4_PROP_T_MAC, the returned object is the start of the payload data and the returned type is the length (in- cluding the ’\0’ for P4_PROP_T_STRING) of the binary data. IPv4 always has size 4, and MAC always has size 6, but there are no explicit typedefs for the payload data of those types. The addresses are stored in netword byte order (high byte first). Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
192 Reference
B.9.5.9 p4_prop_get_size
Returns the size of the object embedded into the given node.
Synopsis:
P4_size_t p4_prop_get_size(const P4_prop_node_t *n)
Parameters: n IN: The property node get the data from
Description: For nodes of type P4_PROP_T_DIR, returns the number of directory entries. For other types, returns exactly the size of the object returned by p4_rom_get_data(). For constant sized object, that’s just the sizeof() of the type listed there. For dynamic data, it is the size if bytes of the payload. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 193
B.9.5.10 p4_prop_get_type
Returns the type of a given property node Execution context: >=P4_BOOT_STAGE_EARLY, independent.
Synopsis:
__forceinline P4_prop_type_t p4_prop_get_type(const P4_prop_node_t *p)
Parameters: p IN: The property node get the data from
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
194 Reference
B.9.5.11 p4_prop_get_name
Returns the name of a given property node Execution context: >=P4_BOOT_STAGE_EARLY, independent.
Synopsis:
__forceinline const char* p4_prop_get_name(const P4_prop_node_t *p)
Parameters: p IN: The property node get the data from
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 195
B.9.5.12 p4_prop_iterate
Iterate through a directory in the property file system.
Synopsis:
const P4_prop_node_t* p4_prop_iterate(const P4_prop_node_t *node, P4_size_t index)
Parameters: node IN: the directory node to start the search in. This must be the result of p4_rom_get_prop() (see section B.9.5.4), p4_prop_find() (see section B.9.5.5), or p4_rom_iterate(). index IN: the sequence number of the child, starting at 0.
Description: This can be used for iterating a directory in the property file system, i.e., to find a given sub-node. The pointer that is passed must have been returned by p4_prop_find or p4_prop_find_sub. A typical iteration looks as follows: const P4_prop_node_t *n; for (P4_size_t i = 0; (n = p4_prop_iterate(dir, i)) != NULL; ++i) { ... use n, e.g. to get the sub-node using p4_prop_find_sub ... }
The entries will have consecutive numbers, so once this returns NULL, no more indices need to be tried. This function returns also P4_PROP_T_LINK nodes, which are hidden by functions p4_prop_find*(). This function is experimental. The API may be subject to changes without prior deprecation phase and without compatibility layer.
Returns: non-NULL pointer to a P4_prop_node_t if an entry is found NULL if no entry found or if the node is not of type P4_PROP_T_DIR Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
196 Reference
B.9.5.13 p4_prop_follow_link
Follow link nodes and resolve into a non-link node.
Synopsis:
const P4_prop_node_t* p4_prop_follow_link(const P4_prop_node_t *n)
Parameters: n IN: The link property node to follow.
Description: Nodes returned by p4_prop_iterate() (see section B.9.5.12) may be links. To follow them, this function can be used to get a non-link node. Other function like p4_prop_find() (see section B.9.5.5) do not require this, because they do the resolving internally.
Returns: n if it is not a link node. NULL if n is NULL or points to nothing. non-NULL pointer to resolved property node, following all links up to a level of P4_PROP_MAX_LINK_LEVEL. NULL if more than P4_PROP_MAX_LINK_LEVEL would need to be followed. This is considered a configu- ration error. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 197
B.9.5.14 p4_rom_file_find
Find a file in the rom file system.
Synopsis:
const void* p4_rom_file_find(const P4_romboot_header_t *rb, const char *name, P4_size_t *size)
Parameters: rb IN: the romboot header containing the rom file system to search. If this is NULL; the function will return NULL and size will be set to 0. name IN: the name of the file. If this is a string longer than P4_NAMELEN-1, this function will assert-fail. size OUT: the size of the data. This pointer must not be NULL.
Description: Searches the rom files system of the given romboot header and returns the payload data if found. The function also returns the size of the payload data in bytes via the size pointer.
Returns: NULL if no file with the given name is found, otherwise returns a poitner to the payload data. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
198 Reference
B.9.5.15 drv_config_find_ext
Find the an extended binary configuration header by ID.
Synopsis:
const void* drv_config_find_ext(const drv_config_header_t *h, P4_size_t id)
Parameters: h IN: the pointer to the preheader of the romimage or any other binary configuration from which to read the given extended header. id IN: the tag ID of the extended header to find. See DRV_EXT_TAG_* constants. Note that DRV_EXT_TAG_NULL (which is 0) is not allowed here, as it is the terminating ID that never has as- sociated data.
Description: The binary configuration format used by UniversalisOS has an optional section that may contain additional arbitrary data. That data is addressed by an integer ID. The payload is stored in the normal data area of the binary, and this function returns a pointer to the payload. The interpretation of the payload, i.e., its type, depends on the ID of the header field, i.e., there is no predefined format. Thus, callers need to know how to interpret the result of this function correctly. See the DRV_EXT_TAG_* function for a list of UniversalisOS system IDs. This function will assert fail if the passed header does not have the correct signature (the header CRC will not be checked, though, because it will break during relocation, in case that was done to the header using drv_config_get_data or drv_config_import).
Returns: NULL if the extended header with the given ID is not found, or if ID is 0, or if the pointer to the payload stored under that ID is NULL. non-NULL otherwise, i.e., the ID is found with a non-NULL pointer to payload. This function will always return a normal absolute pointer even if the header is not relocated and thus contains relative pointers. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 199
B.9.5.16 p4_hm_raise
Trigger a Health-Monitor Event.
Synopsis:
void p4_hm_raise(P4_uid_t uid, P4_uint32_t domain, P4_hm_type_t type, P4_uint32_t error_id, const void *msg, P4_size_t size)
Parameters: uid IN: the UID for which to raise the HM event domain IN: HM domain to use for the HM event type IN: the type of the HM event error_id IN: the error ID of the HM event msg IN: additional error message to pass along with the HM event size IN: number of bytes in msg
Description: A call to this function will trigger a Health-Monitor Event of type type for the HM domain domain, with error identifier error_id. If uid is invalid (P4_UID_INVALID), a module-level hm-event of type type is triggered. If uid is valid, an health-monitor event of type type is delivered to the resource partition associated with the thread uid. Note that the HM Event will be always managed at least at partition level. The HM domain domain must be P4_HM_DOMAIN_PSP or a KDEV driver domain (see P4_HM_DO- MAIN_DRV_BASE). KDEV drivers can identify the relevant HM domain via the service drv_prov_get_hm_do- main(). The caller can specify additional information that led to the event using the msg string parameter. If not NULL, the msg will be propagated together with the type event. size must match the size of the msg (or be set to 0 if the msg is NULL). msg is not assumed to be NUL terminated.
Returns: None.
Note: This function can be called from interrupt context. This function can not be called from MCE (critical exception) contexts. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
200 Reference
B.9.5.17 p4_hm_panic
Trigger a Health-Monitor Panic.
Synopsis:
void p4_hm_panic(P4_uint32_t domain, P4_hm_type_t type, P4_uint32_t error_id, const char *msg, P4_regs_t *regs, P4_cpureg_t arg) __noreturn
Parameters: domain IN: HM domain to use for the HM event type IN: Type of the health-monitoring reported error error_id IN: the error ID of the HM event msg IN: error message regs IN: register context arg IN: parameter number
Description: A call to this function will trigger a Health-Monitor Panic for the HM domain domain, with error identifier error_id. The HM domain domain must be P4_HM_DOMAIN_PSP or a KDEV driver domain (see P4_HM_DO- MAIN_DRV_BASE). KDEV drivers can identify the relevant HM domain via the service drv_prov_get_hm_do- main(). The caller can specify additional information that led to the event using the msg, regs, arg. The size of the msg is truncated to P4_HM_MAX_MSG_SIZE.
Returns: Does not return.
Note: This function can be called from interrupt context. This function can not be called from MCE (critical exception) contexts. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 201
B.9.5.18 p4_my_cpuid
Retrieve current thread’s CPU id.
Synopsis:
P4_cpuid_t p4_my_cpuid(void)
Description: This function returns the ID of the calling thread’s processor.
Note: This service is equivalent to the p4_my_cpuid() (see section B.9.5.18) functionality available to userspace appli- cations.
Returns: Upon success, a call to this function returns the current thread’s CPU ID. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
202 Reference
B.9.5.19 p4_kinfopage
Retrieve a pointer to the kernel info page.
Synopsis:
P4_kinfopage_t* p4_kinfopage(void)
Description: Utility function to get a pointer to the kernel internal virtual address of the P4_kinfopage_t kinfopage structure, which is exposed read-only to userspace.
Returns: Upon success, a call to this function returns a pointer to the kernel info page. Execution context: >=P4_BOOT_STAGE_IDLE_TASK, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 203
B.9.5.20 p4_my_uid
Retrieve current thread’s UID.
Synopsis:
P4_uid_t p4_my_uid(void)
Description: This function returns the UID of the calling thread.
Returns: Upon success, a call to this function returns the current thread’s UID. Execution context:
=P4_BOOT_STAGE_IDLE_TASK, threaded
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
204 Reference
B.9.5.21 p4_my_prio
Retrieve current thread’s priority.
Synopsis:
P4_prio_t p4_my_prio(void)
Description: This function retrieves the thread’s priority.
Note: In case of races with an HM exception (called by the PSP/KDEV in one of the HM-callback/path, the function may return MCPprio.
Returns: Upon success, a call to this function returns the current thread’s priority. Execution context: >=P4_BOOT_STAGE_IDLE_TASK, threaded
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 205
B.9.5.22 p4_kernel_get_thread_size
Utility function to get the thread’s kernel stack size.
Synopsis:
P4_uint32_t p4_kernel_get_thread_size(void)
Note: This is available to userspace via the kernel_stack_size field in the kinfopage. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
206 Reference
B.9.5.23 p4_kernel_preempt_point
Preemption point.
Synopsis:
void p4_kernel_preempt_point(void)
Description: A call to this function will preempt the currently running thread if a request for rescheduling is pending in the kernel. The service allows higher priority threads to execute. Pre-emption is disabled again on return. Since a higher priority thread may also invoke the same PSP driver entry point, this kernel service should not be called from inside critical regions in the PSP driver, and should not be called from inside an interrupt handler.
Returns: None.
Note: Must be called with preemption disabled and interrupts enabled.
Note: Pending deletions will be enacted upon returning to userspace (the caller will be deleted during the return to userspace path). Execution context: >=P4_BOOT_STAGE_COMPLETED, yielding
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 207
B.9.5.24 p4_kernel_preempt_enable
Enable preemption state while executing in a kernel driver.
Synopsis:
void p4_kernel_preempt_enable(void)
Description: The kernel executes non-preemptively. A call to this function will enable preemption while executing in a kernel driver. p4_kernel_preempt_disable() (see section B.9.5.25) must be called before invoking any service provided by the kernel. p4_kernel_preempt_point() (see section B.9.5.23) is in most cases a sufficient alternative to provide controlled preemption within a driver. Nesting of preemption is not supported.
Note: This kernel service must be called with interrupts enabled. This kernel service must be called with preemption disabled. Execution context: >=P4_BOOT_STAGE_COM- PLETED, yielding
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
208 Reference
B.9.5.25 p4_kernel_preempt_disable
Disable preemption before re-entering the kernel.
Synopsis:
void p4_kernel_preempt_disable(void)
Description: A call to this function will disable preemption (e.g., before re-entering the kernel from within a driver). If preemption was enabled via p4_kernel_preempt_enable() (see section B.9.5.24), this function must be called before invoking any service provided by the kernel. Nesting of preemption is not supported.
Note: This kernel service must be called with interrupts enabled. This kernel service must be called with preemption enabled. Execution context: >=P4_BOOT_STAGE_COM- PLETED, yielding
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 209
B.9.5.26 p4_kernel_is_preempt_enabled
Check if the current thread is preemptible.
Synopsis:
P4_bool_t p4_kernel_is_preempt_enabled(void)
Returns: TRUE if preemption is enabled FALSE if preemption is disabled Execution context: >=P4_BOOT_STAGE_COMPLETED, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
210 Reference
B.9.5.27 p4_kernel_is_preempt_pending
Check if the current thread has a preemption request pending.
Synopsis:
P4_bool_t p4_kernel_is_preempt_pending(void)
Returns: TRUE if preemption request is pending FALSE if preemption request is not pernding Execution context: >=P4_BOOT_STAGE_COMPLETED, inde- pendent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 211
B.9.5.28 p4_kernel_in_irq
Check whether the thread is in IRQ handling.
Synopsis:
P4_uint32_t p4_kernel_in_irq(void)
Description: This will check the state of the caller thread and return to the caller whether it is currently in interrupt handling or not.
Returns: 0 if not in interrupt handling 1 if in interrupt handling Execution context: >=P4_BOOT_STAGE_LATE, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
212 Reference
B.9.5.29 p4_kernel_notify_cpu
Send inter processor interrupt to another CPU to enforce rescheduling.
Synopsis:
void p4_kernel_notify_cpu(P4_cpuid_t cpuid)
Parameters: cpuid IN: ID of the CPU to send a rescheduling interrupt to.
Description: This function will send a rescheduling interrupt to CPU cpuid. cpuid must be a valid CPU ID.
Note: This kernel service is intended for use cases in hardware virtualization. Execution context:
=P4_BOOT_STAGE_COMPLETED, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 213
B.9.5.30 p4_mem_get_attr
Get memory attributes of a user address page.
Synopsis:
P4_e_t p4_mem_get_attr(P4_task_t target, P4_address_t virt_addr, P4_phys_addr_t *phys_addr_p, P4_access_t *access_p)
Parameters: target IN: ID of the user task whose page access permissions and cache attributes shall be retrieved. virt_addr IN: Address of the virtual memory area in target address space. The address is rounded down to the nearest page boundary. phys_addr_p OUT: Physical address of the referenced memory page. If phys_addr_p is NULL, no physical address is returned. access_p OUT: Access and cache attributes of the referenced memory page. If access_p is NULL, no memory attributes are returned.
Description: This function returns the physical address phys_addr_p and the memory attributes access_p of a mapped memory page at virtual address virt_addr in the address space of task target. The page in the virtual address must be mapped.
Returns: Upon success, this function returns P4_E_OK, otherwise one of the following error codes is returned to the caller: P4_E_INVAL if virt_addr does not describe a valid memory page in the target virtual address space. P4_E_INVAL if target is not a valid task id. P4_E_STATE if target does not exist. P4_E_BADMAP if the memory area is not completely mapped.
Note: The results of a call to this functions are undefined if task 0 (the kernel task) is referenced in target.
Note: Since this service is implicitly bound to the currently running task, it is safe to invoke from a p4_dev_call() context, but it is not safe to invoke from interrupt context.
Note: This kernel service must be called with interrupts enabled. Execution context: >=P4_BOOT_STAGE_COM- PLETED, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
214 Reference
B.9.5.31 p4_my_thread_status
Check for pending status actions for the current thread.
Synopsis:
P4_thread_status_t p4_my_thread_status(void)
Description: A call to this function will retrieve the pending thread_status for the current thread; a thread status flag indicates a pending status action for the current thread. Multiple pending flags may be ORed in the P4_thread_status_t return value. This kernel service should not be called from inside an interrupt handler.
Returns: Multiple pending flags may be ORed in the P4_thread_status_t return value.
Note: Must be called with preemption disabled and interrupts enabled. Execution context: >=P4_BOOT_STAGE_COM- PLETED, threaded
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 215
B.9.5.32 p4_kernel_tp2rp_list
Get the array of resource partitions associated to time partition tp_id.
Synopsis:
const P4_uint8_t* p4_kernel_tp2rp_list(P4_uint32_t tp_id)
Parameters: tp_id IN: time partition identifier
Description: The function retrieves the pointer to the first element of the array containing the uint8 resource partitions IDs associated with the time partition tp_id. The array can be iterated to retrieve the resource partition IDs. The value P4_NUM_RESPART marks the end of the list.
Returns: The pointer to the first element of the array for tp_id. Execution context: >=P4_BOOT_STAGE_COMPLETED, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
216 Reference
B.9.5.33 p4_kernel_get_mem_type
Get the physical memory region type and resource partition associated with the physical address phys_addr.
Synopsis:
P4_mem_type_t p4_kernel_get_mem_type(P4_phys_addr_t phys_addr, P4_uint32_t *respart)
Parameters: phys_addr IN: physical address respart OUT: resource partition ID
Description: The function search phys_addr in the data structure storing the physical memory regions specified by the user in the configuration. respart will contain the resource partition ID corresponding to a found phys_addr. respart must be not NULL. The function does not block and may be called in interrupt or critical context (MCE) environments. Note that, depending on the architecture, raising a HM event from critical context environments may lead to unpredictable locking effects, since the MCE context may interrupt the system in an unknown locking state. Callers of this function that require a P4_mem_type_t dependent HM-reaction from MCE contexts should serialize the p4_hm_raise() (see section B.9.5.16) call with the UniversalisOS kernel. E.g., via interrupt handling.
Returns: If phys_addr is found, the type associated to the containing physical memory region is returnd. The respart is set to the resource partition ID associated with phys_addr. If phys_addr is not found (or if no memory regions have been setup), the function returns 0 (meaning that the region has a privileged type). The respart is set to 0 as well. Execution context: >=P4_BOOT_STAGE_LATE, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 217
B.9.5.34 p4_kernel_get_global_info
Get a pointer to the kglobal_info kernel global information data.
Synopsis:
const P4_kglobal_info_t* p4_kernel_get_global_info(void)
Description: The information in kglobal_info_t is fully initialized only after kglobal_info.boot_stage >= P4_BOOT_STAGE_COM- PLETED. kglobal_info.boot_message and kglobal_info.log_level are initialized when kglobal_info.boot_stage >= P4_BOOT_STAGE_EARLY.
Returns: Return a pointer to the kglobal information data exported by the kernel for KDEV and PSPs. Execution context:
=P4_BOOT_STAGE_IDLE_TASK, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
218 Reference
B.9.5.35 p4_prop_get_kernel_boot_message
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_boot_message(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Enable kernel boot message: 0=disable kernel boot message, 1=enable kernel boot message, 2=detailed kernel boot message, including configuration limits, 3=verbose kernel boot message showing system startup. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 219
B.9.5.36 p4_prop_get_kernel_log_level
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_log_level(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Global log message verbosity, applicable to whole system (kernel and SSW): 0=Logging off, 1=Log only fatal errors and startup message, 2=Show errors, 3=Show errors (default), 4=Show errors and system information, 5=Show errors, system information and debug messages. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
220 Reference
B.9.5.37 p4_prop_get_kernel_num_respart
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_respart(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of resource partitions supported by the kernel Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 221
B.9.5.38 p4_prop_get_kernel_num_timepart
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_timepart(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of time partitions supported by the kernel Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
222 Reference
B.9.5.39 p4_prop_get_kernel_num_cpu
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_cpu(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of processors supported by the kernel: 0=uniprocessor kernel, 0=autodetection on SMP, 1..32/64 is the number of processors. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 223
B.9.5.40 p4_prop_get_kernel_num_prio
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_prio(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of priorities supported by the kernel, must be either 32, 64, 128, or 256. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
224 Reference
B.9.5.41 p4_prop_get_kernel_num_task
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_task(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of tasks supported by the kernel. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 225
B.9.5.42 p4_prop_get_kernel_num_thread
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_thread(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of threads supported by the kernel. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
226 Reference
B.9.5.43 p4_prop_get_kernel_num_mem_region
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_num_mem_region(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Maximum number of memory regions per partition. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 227
B.9.5.44 p4_prop_get_kernel_thrinfo_size
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_thrinfo_size(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Default kernel-stack size for threads. 0 selects the default minimum as defined by the ASP. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
228 Reference
B.9.5.45 p4_prop_get_kernel_ticker_mode
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_ticker_mode(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Kernel ticker mode implementation. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 229
B.9.5.46 p4_prop_get_kernel_ns_per_tp_tick
Synopsis:
__forceinline P4_uint64_t p4_prop_get_kernel_ns_per_tp_tick(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Time partition base duration (in nanoseconds). Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
230 Reference
B.9.5.47 p4_prop_get_kernel_ns_per_tick
Synopsis:
__forceinline P4_uint64_t p4_prop_get_kernel_ns_per_tick(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: System tick duration (in nanoseconds). Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 231
B.9.5.48 p4_prop_get_kernel_ns_tp_watchdog
Synopsis:
__forceinline P4_uint64_t p4_prop_get_kernel_ns_tp_watchdog(const P4_prop_node_t *prop)
Parameters: prop IN: property node
Description: Time partition switch watchdog (in nanoseconds). The default value of 0 let the kernel use the system tick duration set by UK_NS_PER_TP_TICK to play safe on architectures without fine granular timing. Execution context:
=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
232 Reference
B.9.5.49 p4_prop_get_kernel_tps_strong_sync
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_tps_strong_sync(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Strong time partition switching synchronization at major time frame. The property controls the use of strong time partition synchronization at every major time frame occurrence. When enabled (set to 1), CPUs will synchronize time partition switching at every major time frame using a strong busy-wait barrier semantics. Such strong behavior is needed when using the alarm_timepart() KDEV callbacks. When disabled (set to 0, default), UniversalisOS will strongly synchronize time partition switching only during a schema switch. Major time frames are implicitly synchronized by relying on synchronized timing across CPUs. In both synchronization mode, time partition switching does not drift. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 233
B.9.5.50 p4_prop_get_kernel_tptable_max_windows
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_tptable_max_windows(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of entries in the TP switcher table, 256 entries fit in one page. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
234 Reference
B.9.5.51 p4_prop_get_kernel_respart0_pages
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_respart0_pages(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Number of pages in the page pool for resource partition 0, zero means auto refill, in all other cases the number of pages is assigned to respart 0 and further dynamic allocation is impossible. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Kernel Driver and PSP Common Functionalities 235
B.9.5.52 p4_prop_get_kernel_test_flags
Synopsis:
__forceinline P4_uint32_t p4_prop_get_kernel_test_flags(const P4_prop_node_t *prop)
Parameters: prop IN: The property node get the data from
Description: Kernel setting for testing purposes. Default value: 0 - disabled Possible values:
• Value 1: the value allows installation of "userspace" level handlers for the PSSW/sigma0 partition. Without the flag, errors in the PSSW/sigma0 partition are always handled at module level. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
236 Reference
B.10 Logging
B.10.1 Defines
drv_put_e (X) Print an error code in human readable form (as a string).
Parameters:
X IN: error code
Note:
Printing after boot should be avoided to prevent timing interference via the console.
Note:
Boot_message and log_level properties control the verbosity setup in the integration project.
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 237
B.10.2 Functions
B.10.2.1 drv_put_c
Put a character on the console.
Synopsis:
void drv_put_c(char c)
Parameters: c IN: character to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
238 Reference
B.10.2.2 drv_put_s
Put a character stream on the console.
Synopsis:
void drv_put_s(const char *str)
Parameters: str IN: pointer to character stream to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 239
B.10.2.3 drv_put_x
Put a P4_uint32_t in hexadecimal format on the console.
Synopsis:
void drv_put_x(P4_uint32_t num)
Parameters: num IN: address to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
240 Reference
B.10.2.4 drv_put_xp
Put an address in hexadecimal format on the console.
Synopsis:
void drv_put_xp(P4_address_t num)
Parameters: num IN: address to be written on the console
Description: On 32 bit, prints exactly 8 digits. On 64 bit, prints exactly 16 digits.
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 241
B.10.2.5 drv_put_xx
Put a physical address in hexadecimal format on the console.
Synopsis:
void drv_put_xx(P4_phys_addr_t num)
Parameters: num IN: address to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
242 Reference
B.10.2.6 drv_put_xb
Put a byte in hexadecimal format on the console.
Synopsis:
void drv_put_xb(unsigned char num)
Parameters: num IN: hexadecimal character sequence to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 243
B.10.2.7 drv_put_d
Put the characters of a decimal value on the console. This is for 32 bit integers.
Synopsis:
void drv_put_d(unsigned int u)
Parameters: u IN: decimal value to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
244 Reference
B.10.2.8 drv_put_dd
Put the characters of a decimal value on the console. This is for 64 bit integers.
Synopsis:
void drv_put_dd(P4_uint64_t u)
Parameters: u IN: decimal value to be written on the console
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 245
B.10.2.9 drv_put_uid
Put task uid and thread uid on the console.
Synopsis:
void drv_put_uid(P4_uid_t uid)
Parameters: uid IN: Thread uid.
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: None. Execution context: >=P4_BOOT_STAGE_EARLY, independent
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
246 Reference
B.10.2.10 drv_try_put_c
Try to print a character, do not spin to wait.
Synopsis:
int drv_try_put_c(char c)
Parameters: c IN: Character to be put to the console
Description: This is a version of drv_put_c() (see section B.10.2.1) that will, instead of spinning to wait for the console to be ready to receive new characters, return FALSE immediately if the console is not ready. The caller can then decide what to do, e.g., insert a preemption point and try again, or return with an error. In contrast to drv_put_c() (see section B.10.2.1), this function does not translate LF to printing CR+LF i.e., the caller must take care of that. drv_put_c() (see section B.10.2.1) is roughly equivalent to spinning on drv_try_put_c() (see section B.10.2.10) until it returns non-0. It is not exactly the same, because drv_try_put_c() (see section B.10.2.10) cannot translate LF to CR+LF, because it would need to spin to do that atomically. Execution context: >=P4_BOOT_STAGE_EARLY, independent
Note: Printing after boot should be avoided to prevent timing interference via the console.
Note: Boot_message and log_level properties control the verbosity setup in the integration project.
Returns: 1 if the character was printed -1 if the PSP has no printing capability 0 if the character was not printed because the console would block
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.
Logging 247
B.10.2.11 drv_try_get_c
Synopsis:
int drv_try_get_c(char *c)
Parameters: c OUT: Character received from console if no character received the content will not be changed
Description: Try to read a character, do not spin to wait. This function is similar to the drv_try_put_c() (see section B.10.2.10). It tries to read a character from console and returns FALSE immediately if the console is not ready to provide a character. The caller can then decide what to do, e.g., insert a preemption point and try again, or return with an error. Execution context: >=P4_BOOT_STAGE_EARLY, independent
Returns: 1 if the character was received -1 if the PSP has no reading capability 0 if the character was not read because the console would block
c Copyright 2005 – 2019 Portugal Futurista GmbH, all rights reserved.