1# Framework Guide 2 3Copyright (c) 2011-2023, Arm Limited. All rights reserved. 4 5This guide covers the framework that is used to implement the SCP/MCP Software 6and which can also be used to extend the provided implementation. Each of the 7key components that are used by the framework will be defined and explained and, 8following this, the way that these components interact within the constraints of 9the framework will be explored. 10 11The key components are explained at a relatively high level; for more detailed 12descriptions of the framework components, refer to the source-level Doxygen 13documentation. If this has not yet been built then the readme.md file provides 14information on getting started from scratch. This file can be found in the 15root of the SCP/MCP Software directory. 16 17Arm welcomes any feedback on the SCP/MCP Software, including on this 18documentation. To provide feedback or to request support please contact Arm by 19email at support@arm.com. Arm licensees may also contact Arm via their partner 20managers. 21 22## Framework Components 23 24The framework defines several component types that are combined to produce a 25complete product. These components have different roles within the product and 26different limitations and guidelines that govern their use. This guide explains 27the concept of a product itself and then examines each of the components that 28can be used when constructing a product using the framework. 29 30### Products 31 32A product is a representation of a system that one or more firmware images 33are being produced and built for. For example, a system that implements 34both a System Control Processor (SCP) and a Manageability Control Processor 35(MCP) may require a separate firmware image for each processor. Alternatively, 36a product may be designed such that software for a single processor is split 37into multiple images. 38 39A product always consists of a *product.mk* file that defines one or more 40firmware targets. Each of these firmware targets is built, producing a binary 41image, when building the product as a whole. Refer to the Build System document 42for further information on the makefiles that are used by the framework. 43 44In addition to these targets a product may also contain header files that are 45used for product-specific definitions. 46 47The directory structure of a project can be seen below, note that there is one 48directory per product under the product directory. 49 50``` 51root/ 52 product/ 53 productname/ 54 include/ 55 src/ 56 module/ 57 firmware_a/ 58 firmware_b/ 59 product.mk 60``` 61 62### Firmware 63 64Within a product there will always be at least one firmware. A firmware 65represents a software image that is built as part of a product. Each firmware 66lists the modules that will be built into its image and provides configuration 67data for each of these modules. 68 69For each firmware, linker information must be provided in a *fmw_memory.h* file: 70 71- FMW_MEM_MODE: The desired memory region configuration. Can be one of the 72 following: 73 - ARCH_MEM_MODE_SINGLE_REGION 74 - ARCH_MEM_MODE_DUAL_REGION_RELOCATION 75 - ARCH_MEM_MODE_DUAL_REGION_NO_RELOCATION 76- FMW_MEM0_BASE: The base address of the MEM0 region, which is always 77 used regardless of the memory region configuration given by *FMW_MEM_MODE*. 78- FMW_MEM0_SIZE: The size of the MEM0 region in bytes. 79 80 81If a dual-region memory configuration is used then *FMW_MEM1_BASE* and 82*FMW_MEM1_SIZE* must also be defined. 83 84It is the responsibility of the firmware to define - in its Toolchain-*.cmake - 85file the architecture target for the image 86(using set(CMAKE_SYSTEM_PROCESSOR "<processor-name>")) and/or has 87notification support in Firmware.cmake file 88(using set(SCP_ENABLE_NOTIFICATIONS_INIT TRUE/FULSE)). The firmware can 89optionally choose to use standard newlib or newlib-nano (optimized for code 90size) as prebuilt C library (using set(SCP_ENABLE_NEWLIB_NANO TRUE/FALSE)) in 91the same Firmware.cmake file. 92 93An example of a simple firmware directory which contains configuration files for 94two modules, 3 CMake files and the header file fmw_memory.h which defines values 95consumed by the linker script. 96 97``` 98firmware_root/ 99 CMakeLists.txt 100 config_module_a.c 101 config_module_b.c 102 Firmmware.cmake 103 fmw_memory.h 104 Toolchain-GNU.cmake 105``` 106 107### Modules 108 109A module is a unit of code that performs a well-defined operation, or set of 110operations. 111 112It is recommended that modules are written and organized so that the 113functionality provided by each module is properly encapsulated, with 114well-defined interfaces to other modules. Generally, each module should be 115responsible for a single piece of functionality and the overall functionality 116required by the firmware should arise from the interaction between modules. 117 118Modules declare themselves as a particular type, depending on the operations 119that they perform. The type is informational only as the framework 120does not treat a module differently depending on its type, however the types 121are used as a guide to the way in which the framework expects operations to be 122divided between modules. 123 124The module types are: 125- Hardware Abstraction Layer: A module that provides functionality for one or 126 more types of hardware device through standardized interfaces that 127 abstract away differences between the supported devices. Generally, a HAL 128 module will depend on other modules to perform work on its behalf at a lower 129 level. 130 131- Driver: A module that controls a specific device, or class of device. Driver 132 modules will often implement one or more APIs that are defined by a HAL 133 module and the functionality of their device(s) can be used through these 134 common interfaces. It is also possible for a driver module to be standalone, 135 and to function without an associated HAL module. 136 137- Protocol: Protocol modules implement a protocol and provide one or more APIs 138 so that other modules can make use of it. The module will implement any 139 protocol-specific handling (arbitration of messaging channels, for example). 140 141- Service: A service module performs work and/or provides functionality that is 142 not related to hardware devices. This distinction separates it from the HAL 143 and Driver-type modules. A service module does not necessarily have to 144 provide services to other modules through APIs, it may perform some work 145 that is entirely self-contained. 146 147Every module must declare and define a structure of type *struct fwk_module* and 148it is this structure that describes the module within the context of the 149framework. The framework uses information from this structure to perform 150validation during certain operations such as binding to a module's API. 151 152An example of a module description is given below. This example module defines 153itself as a service that provides one API for other modules to use. Because it 154provides an API it implements the *process_bind_request* function of the 155framework's module interface so that other modules can bind to it. It does not 156generate any events nor any notifications. 157 158``` 159const struct fwk_module mod_modulename = { 160 .type = FWK_MODULE_TYPE_SERVICE, 161 .api_count = 1, 162 .event_count = 0, 163 .notification_count = 0, 164 .init = modulename_init, 165 .element_init = modulename_element_init, 166 .bind = modulename_bind, 167 .process_bind_request = modulename_process_bind_request, 168}; 169``` 170 171#### Module Configuration 172 173For each module that is built as part of a product, a corresponding module 174configuration must be provided. This configuration takes the form of a 175*struct fwk_module_config* structure that is defined in a configuration file 176within the firmware that the module will be built into. 177 178The declaration for the *fwk_module_config* structure is given below: 179 180``` 181struct fwk_module_config { 182 const struct fwk_element *(*get_element_table)(fwk_id_t module_id); 183 const void *data; 184}; 185``` 186 187The framework uses the *get_element_table* function pointer to access the table 188of elements that the product has provided for the module. If the pointer is 189NULL then the framework assumes that no elements will be provided. 190 191Each of the entries in the element table is a pointer to a *struct fwk_element* 192structure. Elements are made available to the module during the *element 193initialization* stage. 194 195The second member of the structure is an optional void pointer that points to 196module-specific configuration data. The format of this configuration data 197is defined by the module itself. This data is made available to the module 198during the *module initialization* stage. 199 200### Elements 201 202An element represents a resource that is owned or governed by a module. Each 203module may have many associated elements, a single element, or no elements at 204all. 205 206Element descriptions complement the *module configuration*. There is one 207description per element the module contains. In turn, each element description 208holds element configuration data, the type of which is defined by the module. 209Generally, each element will represent an object that the module interacts 210with and/or is responsible for. For example, a driver-type module may have 211elements which represent the hardware devices that it controls. Because the 212element configuration data is provided as part of a product specification, the 213module itself does not need to contain any product-specific data and it can be 214written in a way that is as generic as possible. 215 216Elements are defined by a structure containing a pointer to a name string, the 217number of sub-elements associated with the element, and a void pointer to data 218that is in a module-defined format. The declaration for the *fwk_element* 219structure is given below: 220 221``` 222struct fwk_element { 223 const char *name; 224 size_t sub_element_count; 225 const void *data; 226}; 227``` 228 229### Sub-elements 230 231A sub-element represents a resource that is owned or governed by an element. 232Unlike elements, however, sub-elements do not have a structure within the 233framework that defines them. Instead, sub-elements are represented by their 234indices and/or identifiers alone. 235 236### Indices and Identifiers 237 238Since the framework is designed to be modular there is a need for a standardized 239method of identifying and referring to modules, elements, sub-elements, events, 240notifications and APIs. The framework defines two components for this purpose: 241*indices* and *identifiers*. 242 243#### Indices 244 245Indices are unsigned integers that uniquely identify items within their parent 246context. That is, to identify an element, event, notification or API within the 247context of a module, a sub-element within the context of an element, or a module 248within the context of a firmware. 249 250Module indices are generated for each firmware by the build system and are 251placed in the *fwk_module_idx.h* header file. 252 253#### Identifiers 254 255Identifiers are used to uniquely identify items from a global firmware context 256(which may either be inside or outside of the items' parent context). 257 258Examples of identifier uses include: 259 260- Identifying a module's elements, events or APIs from the context of another 261 module. 262- Identifying modules within the context of the firmware as a whole. 263- Identifying sub-elements from the context of a module. 264 265Identifiers have a type and this determines the information that is contained 266within the identifier. Internally, identifiers always contain the index of a 267module and may contain additional indices that identify an item within the 268context of that module. 269 270The available identifier types are: 271 272- Module: Consists of the module index alone 273- Element: Consists of a module index and an index of an element within the 274 module 275- Sub-element: Consists of a module index, an index of an element within the 276 module, and an index of the sub-element owned by the element. 277- API: Consists of a module index and an index of an API provided by the module 278- Event: Consists of a module index and an index of an event that may be 279 generated by the module 280- Notification: Consists of a module index and an index of a notification that 281 may be generated by the module. 282 283### APIs 284 285Modules that offer functionality to other modules will do so by defining one 286or more Application Programming Interfaces (APIs). Other modules may then bind 287to these APIs in order to use the provided functionality. This approach ensures 288that interactions between modules are well-defined and that there is a low 289degree of coupling across the modules within a firmware. 290 291#### Declaring and Defining APIs 292 293An API is declared in the module's public header as a structure containing one 294or more function pointers. Modules may declare multiple APIs, each offering 295different functionality. 296 297Within the module's source files each declared API is then defined, with each of 298the API's declared function pointers pointing to a function within the module. 299 300#### Advertising APIs 301 302When one module wishes to use an API from another it must first construct an 303API identifier so that it can uniquely identify which of the target module's 304APIs it intends to use. 305 306The API identifier consists of a *module index* and an *API index*. The former 307is used to specify the target module that provides the desired API, while the 308latter is used to specify which of the target module's APIs is requested. 309 310A module may offer some affordances so that the *API index* is always 311well-defined. For example, the module may provide an enumeration in its public 312header that lists the APIs it offers, giving the API indices in a structured 313way: 314 315``` 316enum mod_modulename_api { 317 MOD_MODULENAME_API_A, 318 MOD_MODULENAME_API_B, 319}; 320``` 321 322Alternatively, the module may define these values individually: 323 324``` 325#define MOD_MODULENAME_API_IDX_A 0 326#define MOD_MODULENAME_API_IDX_B 1 327``` 328 329Finally, the module may offer its API identifiers directly using the appropriate 330macros to construct the identifiers itself. This approach has the benefit that 331modules using the API do not need to create the API identifiers themselves. 332 333``` 334static const fwk_id_t mod_modulename_api_id_a = FWK_ID_API_INIT( 335 FWK_MODULE_IDX_MODULENAME, MOD_MODULENAME_API_IDX_A); 336static const fwk_id_t mod_modulename_api_id_b = FWK_ID_API_INIT( 337 FWK_MODULE_IDX_MODULENAME, MOD_MODULENAME_API_IDX_B); 338``` 339 340### Events 341 342A module may optionally define events - structured messages that are passed from 343one module to another. Events are issued by a source towards a target, the 344source and target being a module, element, or sub-element. 345 346When an entity receives and processes an event, it may need to respond to the 347entity that issued the event. The event contains a *response_requested* 348property that indicates whether or not the source entity expects a response to 349its event or not. To respond to this event, the receiving entity fills out the 350response parameters and the framework issues an event that targets the entity 351which issued the original event. The *is_response* property of the event is 352used to indicate that the newly-generated event is in response to the original 353event. 354 355Events contain a block of memory to store parameters that are used to pass 356information between the source and target entity. This memory is intended to be 357written to and read through a C structure. The size (bytes) of this space is 358defined by *FWK_EVENT_PARAMETERS_SIZE* in fwk_event.h. 359 360### Light Events 361 362As described in *Events* above, Objects of type ```struct fwk_event``` 363contains a block of memory to store the parameters. However, this block 364is not always needed for simple use cases. 365If ```struct fwk_event``` object is allocated and initialized partially 366on the stack, the parameter block also gets initialized which may not 367be efficient for performance sensitive use cases. The framework 368provides an additional event type ```struct fwk_event_light``` which 369does not contain this block of memory for parameters and few other 370fields(cookie, is_response, is_notification, and is_delayed_response). 371Apart from this, from the user's view the *Light Events* behaves as same as 372*Events*. Also, note that these type of events can not be used in notifications 373and delayed response use cases. 374 375#### Notifications 376 377Notifications are used when a module wants to notify other modules of a change 378of its own state. A notification is broadcast to a set of modules or elements 379that have subscribed beforehand to this notification. 380 381To the framework, a notification is just a special type of event. 382 383Like an event, a notification may require a response from the target entities. 384In this case, the same mechanism as events is used. 385 386When a notification is broadcast, the number of subscribers is given to the 387notifier by the framework. This information can be used by the notifier, for 388instance, to know when all the subscribers have responded to this notification 389in the case where a response was required. 390 391## Framework Concepts 392 393This section explains concepts that relate to the framework itself and to the 394components that the framework provides, specifically the initialization of the 395framework and its components, and the way in which modules and elements are 396bound together during this process. 397 398### Phases 399 400During the *pre-runtime phase* the framework directs the execution flow, 401configuring components in several stages, outlined in the following section, 402until all modules, elements, and sub-elements are initialized, bound together, 403and started. 404 405In the *runtime phase* the execution flow is directed primarily by interactions 406between modules, by events and by received interrupts. The framework is used to 407facilitate, validate, and govern these interactions. 408 409#### Pre-Runtime Stages 410 411The pre-runtime phase is divided into into five stages that occur in a fixed 412order: 413 414- Module initialization 415- Element initialization 416- Post-initialization 417- Bind 418- Start 419 420Each stage is executed for each module before moving onto the next stage, and 421modules are processed in the order they are given in the *BS_FIRMWARE_MODULES* 422list. 423 424Once these stages have all been completed the firmware as a whole is considered 425to be fully initialized and execution enters the *runtime phase*. The stages are 426described in the following sub-sections. 427 428##### Module Initialization 429 430Each module receives its module configuration from the firmware that it will be 431built into. The framework invokes the function that the module provides to 432satisfy the *init()* function pointer of the framework's module API. During this 433stage the module does not have access to any elements and cannot interact with 434other modules. 435 436##### Element Initialization 437 438The framework invokes the function that the module provides to satisfy the 439*element_init()* function pointer of the framework's module API. This function 440is invoked once for each element that is defined in the firmware's element table 441for the module. 442 443In the element initialization stage the module receives information about the 444elements that have been provided to it via its configuration in the firmware. 445 446During this stage the module may interact with elements as they are 447provided to it. It cannot interact with other modules, even if these modules are 448referred to in an element's descriptor. 449 450Modules that do not have any elements provided via their configuration in the 451firmware are not required to participate in this stage. 452 453**Note:** Participation in this stage is optional if the module has no elements. 454 455##### Post-Initialization 456 457The post-initialization stage is intended to be used by modules to perform any 458actions that are required after all its elements have been initialized, yet 459before any module-to-module interaction is possible. An example would be some 460initialization that requires comparisons between the module's elements as this 461is the first stage in which the module has received configuration for all of its 462elements. 463 464**Note:** Participation in this stage is optional. 465 466##### Bind 467 468Each module and element has the opportunity to bind to other modules and 469elements so that their interfaces can be used in the start stage and during the 470runtime phase. 471 472The binding stage is the stage during which modules request access to each 473other's APIs. Until modules are bound they have no direct way to call functions 474from each other. 475 476**Note:** Participation in this stage is optional. 477 478##### Start 479 480Modules perform initialization that depends on using resources from other 481modules and elements, now that binding is complete and these resources are 482available. This is the final pre-runtime stage. 483 484**Note:** Participation in this stage is optional. 485 486#### Runtime phase 487 488Once the pre-runtime stages have been successfully completed, the firmware will 489start to process events raised by modules or interrupts. By default, the 490firmware will loop forever waiting for new events to process at the end of the 491pre-runtime stages but it is possible to return after processing pending events 492when the event list is empty. 493 494##### Sub system runtime mode 495 496When SCP_ENABLE_SUB_SYSTEM_MODE is set, fwk_arch_init() will return after 497processing the pending events. Then, it's up to the system to call the function 498fwk_process_event_queue() when new events have been added into the list. Such 499behavior is useful when the SCP-firmware is a sub part of a larger system like 500being an application running in an execution environnement (i.e RTOS or TEE). 501 502#### Error Handling 503 504Errors that occur during the pre-runtime phase (such as failures that occur 505during module and element initialization, memory management initialization, or 506interrupt initialization) are passed from the framework layer into the arch 507layer. It is therefore architecture-specific code that determines the ultimate 508response to these types of errors. 509 510### Binding 511 512Binding is the process in which modules and elements can form associations with 513each other and request access to APIs that are declared by modules within the 514framework. Along with issuing events, these should be the two methods by which 515a module can invoke functionality from another module. 516 517#### Module-Level and Element-Level Binding 518 519A module or element may bind to another module or element within a module. The 520goal is the same - to obtain a pointer to an API that can be used during later 521stages. 522 523When attempting to bind to an element within a module (instead of the module 524itself) the main difference is that the module that receives and processes the 525bind request has the ability to change its behavior depending on the targeted 526element. For example, a module that is requesting binding may be permitted to 527bind to only a subset of elements within the module processing the request. 528 529 530#### Processing Binding Requests 531 532When a module receives a binding request it is not required to accept it. The 533framework allows a module to reject a binding request if the module's criteria 534for correct binding are not met. 535 536For example, a power supply driver may restrict the types of modules that are 537allowed to bind to it, so that only a Hardware Abstraction Layer (HAL) module 538can utilize the driver. 539 540Alternatively, a driver that is of the *service* type may choose to restrict the 541service it provides to only a allowlisted set of modules within the firmware. In 542this case the driver module can compare the identifier of the module that is 543attempting binding with its allowlist and accept or reject the bind request as 544appropriate. 545 546If a binding request is rejected then the framework will consider that an error 547has occurred and the binding process as a whole will fail. The handling of this 548overall condition is ultimately architecture specific. 549 550### Logging 551 552The framework contains a log component to ensure that logging functionality is 553always available and is not tied to the availability of any particular 554module. The framework defines and implements the public interface for this 555component. Documentation for this interface can be found in fwk_log.h. 556 557To ensure that the framework is platform-independent, the log component relies 558on a small set of functions to do platform-specific work like flushing the 559buffer and outputting characters. These functions make up the log driver 560interface and are forward declared in fwk_log.h: 561 562``` 563int fwk_log_driver_init(void); 564int fwk_log_driver_putchar(char c); 565int fwk_log_driver_flush(void); 566``` 567 568The framework only implements weakly-linked "stub" versions of these functions 569that simply return an error code. It is expected that platform-specific code (a 570driver module) will implement these functions properly to complete the logging 571functionality of the framework. 572 573If this driver module requires configuration data to be used in the log driver 574functions, the usual method of module configuration will not suffice. This is 575because the log driver functions could be called before the module receives its 576configuration data in the initialization stage of the framework. To allow the 577passing of configuration data to this module, the log component in the framework 578externally declares a pointer to configuration data: (fwk_log.h) 579 580``` 581extern void *fwk_log_driver_config; 582``` 583 584It is expected that the firmware-specific module configuration code for the 585driver module (config_mod_xxx.c) will concretely declare this variable and 586initialize it to point to some configuration structure: 587 588``` 589struct mod_xxx_fwk_log_config cfg = { 590 .x = 1, 591 .y = 2, 592 .z = 3, 593}; 594 595void *fwk_log_driver_config = &cfg; 596``` 597 598The driver module can then access its log framework related configuration data 599at any time. It is expected that the driver module performs initialization using 600this configuration data in the fwk_log_driver_init() function. 601 602#### Tracing 603To enable tracing functionality `FWK_TRACE_ENABLE` should be defined. 604There is an example configuration for CMake that should be included in 605`CMakeLists.txt` in every module that uses this feature. This configuration 606 requires `SCP_TRACE_ENABLE_MOD_<module name>` to be defined as a build flag 607 parameter. 608 609``` 610include(SCPModuleTrace) 611 612... 613 614scp_module_trace(${SCP_MODULE}) 615``` 616