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 void *data; 183 struct fwk_module_elements elements; 184}; 185``` 186 187The declaration for the *fwk_module_elements* structure is given below: 188 189``` 190struct fwk_module_elements { 191 enum fwk_module_elements_type type; 192 193 union { 194 const struct fwk_element *(*generator)(fwk_id_t module_id); 195 const struct fwk_element *table; 196 }; 197}; 198 199``` 200 201The framework uses the the static table given in the *table* pointer to 202access the table of elements that the product has provided for the module 203in case the type is *FWK_MODULE_ELEMENTS_TYPE_STATIC*. 204 205The framework uses the *generator* function pointer if the *type* is 206*FWK_MODULE_ELEMENTS_TYPE_DYNAMIC*. 207 208Each of the entries in the element table is a pointer to a *struct fwk_element* 209structure. Elements are made available to the module during the *element 210initialization* stage. 211 212The second member of the structure is an optional void pointer that points to 213module-specific configuration data. The format of this configuration data 214is defined by the module itself. This data is made available to the module 215during the *module initialization* stage. 216 217### Elements 218 219An element represents a resource that is owned or governed by a module. Each 220module may have many associated elements, a single element, or no elements at 221all. 222 223Element descriptions complement the *module configuration*. There is one 224description per element the module contains. In turn, each element description 225holds element configuration data, the type of which is defined by the module. 226Generally, each element will represent an object that the module interacts 227with and/or is responsible for. For example, a driver-type module may have 228elements which represent the hardware devices that it controls. Because the 229element configuration data is provided as part of a product specification, the 230module itself does not need to contain any product-specific data and it can be 231written in a way that is as generic as possible. 232 233Elements are defined by a structure containing a pointer to a name string, the 234number of sub-elements associated with the element, and a void pointer to data 235that is in a module-defined format. The declaration for the *fwk_element* 236structure is given below: 237 238``` 239struct fwk_element { 240 const char *name; 241 size_t sub_element_count; 242 const void *data; 243}; 244``` 245 246### Sub-elements 247 248A sub-element represents a resource that is owned or governed by an element. 249Unlike elements, however, sub-elements do not have a structure within the 250framework that defines them. Instead, sub-elements are represented by their 251indices and/or identifiers alone. 252 253### Indices and Identifiers 254 255Since the framework is designed to be modular there is a need for a standardized 256method of identifying and referring to modules, elements, sub-elements, events, 257notifications and APIs. The framework defines two components for this purpose: 258*indices* and *identifiers*. 259 260#### Indices 261 262Indices are unsigned integers that uniquely identify items within their parent 263context. That is, to identify an element, event, notification or API within the 264context of a module, a sub-element within the context of an element, or a module 265within the context of a firmware. 266 267Module indices are generated for each firmware by the build system and are 268placed in the *fwk_module_idx.h* header file. 269 270#### Identifiers 271 272Identifiers are used to uniquely identify items from a global firmware context 273(which may either be inside or outside of the items' parent context). 274 275Examples of identifier uses include: 276 277- Identifying a module's elements, events or APIs from the context of another 278 module. 279- Identifying modules within the context of the firmware as a whole. 280- Identifying sub-elements from the context of a module. 281 282Identifiers have a type and this determines the information that is contained 283within the identifier. Internally, identifiers always contain the index of a 284module and may contain additional indices that identify an item within the 285context of that module. 286 287The available identifier types are: 288 289- Module: Consists of the module index alone 290- Element: Consists of a module index and an index of an element within the 291 module 292- Sub-element: Consists of a module index, an index of an element within the 293 module, and an index of the sub-element owned by the element. 294- API: Consists of a module index and an index of an API provided by the module 295- Event: Consists of a module index and an index of an event that may be 296 generated by the module 297- Notification: Consists of a module index and an index of a notification that 298 may be generated by the module. 299 300### APIs 301 302Modules that offer functionality to other modules will do so by defining one 303or more Application Programming Interfaces (APIs). Other modules may then bind 304to these APIs in order to use the provided functionality. This approach ensures 305that interactions between modules are well-defined and that there is a low 306degree of coupling across the modules within a firmware. 307 308#### Declaring and Defining APIs 309 310An API is declared in the module's public header as a structure containing one 311or more function pointers. Modules may declare multiple APIs, each offering 312different functionality. 313 314Within the module's source files each declared API is then defined, with each of 315the API's declared function pointers pointing to a function within the module. 316 317#### Advertising APIs 318 319When one module wishes to use an API from another it must first construct an 320API identifier so that it can uniquely identify which of the target module's 321APIs it intends to use. 322 323The API identifier consists of a *module index* and an *API index*. The former 324is used to specify the target module that provides the desired API, while the 325latter is used to specify which of the target module's APIs is requested. 326 327A module may offer some affordances so that the *API index* is always 328well-defined. For example, the module may provide an enumeration in its public 329header that lists the APIs it offers, giving the API indices in a structured 330way: 331 332``` 333enum mod_modulename_api { 334 MOD_MODULENAME_API_A, 335 MOD_MODULENAME_API_B, 336}; 337``` 338 339Alternatively, the module may define these values individually: 340 341``` 342#define MOD_MODULENAME_API_IDX_A 0 343#define MOD_MODULENAME_API_IDX_B 1 344``` 345 346Finally, the module may offer its API identifiers directly using the appropriate 347macros to construct the identifiers itself. This approach has the benefit that 348modules using the API do not need to create the API identifiers themselves. 349 350``` 351static const fwk_id_t mod_modulename_api_id_a = FWK_ID_API_INIT( 352 FWK_MODULE_IDX_MODULENAME, MOD_MODULENAME_API_IDX_A); 353static const fwk_id_t mod_modulename_api_id_b = FWK_ID_API_INIT( 354 FWK_MODULE_IDX_MODULENAME, MOD_MODULENAME_API_IDX_B); 355``` 356 357### Events 358 359A module may optionally define events - structured messages that are passed from 360one module to another. Events are issued by a source towards a target, the 361source and target being a module, element, or sub-element. 362 363When an entity receives and processes an event, it may need to respond to the 364entity that issued the event. The event contains a *response_requested* 365property that indicates whether or not the source entity expects a response to 366its event or not. To respond to this event, the receiving entity fills out the 367response parameters and the framework issues an event that targets the entity 368which issued the original event. The *is_response* property of the event is 369used to indicate that the newly-generated event is in response to the original 370event. 371 372Events contain a block of memory to store parameters that are used to pass 373information between the source and target entity. This memory is intended to be 374written to and read through a C structure. The size (bytes) of this space is 375defined by *FWK_EVENT_PARAMETERS_SIZE* in fwk_event.h. 376 377### Light Events 378 379As described in *Events* above, Objects of type ```struct fwk_event``` 380contains a block of memory to store the parameters. However, this block 381is not always needed for simple use cases. 382If ```struct fwk_event``` object is allocated and initialized partially 383on the stack, the parameter block also gets initialized which may not 384be efficient for performance sensitive use cases. The framework 385provides an additional event type ```struct fwk_event_light``` which 386does not contain this block of memory for parameters and few other 387fields(cookie, is_response, is_notification, and is_delayed_response). 388Apart from this, from the user's view the *Light Events* behaves as same as 389*Events*. Also, note that these type of events can not be used in notifications 390and delayed response use cases. 391 392#### Notifications 393 394Notifications are used when a module wants to notify other modules of a change 395of its own state. A notification is broadcast to a set of modules or elements 396that have subscribed beforehand to this notification. 397 398To the framework, a notification is just a special type of event. 399 400Like an event, a notification may require a response from the target entities. 401In this case, the same mechanism as events is used. 402 403When a notification is broadcast, the number of subscribers is given to the 404notifier by the framework. This information can be used by the notifier, for 405instance, to know when all the subscribers have responded to this notification 406in the case where a response was required. 407 408## Framework Concepts 409 410This section explains concepts that relate to the framework itself and to the 411components that the framework provides, specifically the initialization of the 412framework and its components, and the way in which modules and elements are 413bound together during this process. 414 415### Phases 416 417During the *pre-runtime phase* the framework directs the execution flow, 418configuring components in several stages, outlined in the following section, 419until all modules, elements, and sub-elements are initialized, bound together, 420and started. 421 422In the *runtime phase* the execution flow is directed primarily by interactions 423between modules, by events and by received interrupts. The framework is used to 424facilitate, validate, and govern these interactions. 425 426#### Pre-Runtime Stages 427 428The pre-runtime phase is divided into into five stages that occur in a fixed 429order: 430 431- Module initialization 432- Element initialization 433- Post-initialization 434- Bind 435- Start 436 437Each stage is executed for each module before moving onto the next stage, and 438modules are processed in the order they are given in the *SCP_MODULES* 439list. 440 441Once these stages have all been completed the firmware as a whole is considered 442to be fully initialized and execution enters the *runtime phase*. The stages are 443described in the following sub-sections. 444 445##### Module Initialization 446 447Each module receives its module configuration from the firmware that it will be 448built into. The framework invokes the function that the module provides to 449satisfy the *init()* function pointer of the framework's module API. During this 450stage the module does not have access to any elements and cannot interact with 451other modules. 452 453##### Element Initialization 454 455The framework invokes the function that the module provides to satisfy the 456*element_init()* function pointer of the framework's module API. This function 457is invoked once for each element that is defined in the firmware's element table 458for the module. 459 460In the element initialization stage the module receives information about the 461elements that have been provided to it via its configuration in the firmware. 462 463During this stage the module may interact with elements as they are 464provided to it. It cannot interact with other modules, even if these modules are 465referred to in an element's descriptor. 466 467Modules that do not have any elements provided via their configuration in the 468firmware are not required to participate in this stage. 469 470**Note:** Participation in this stage is optional if the module has no elements. 471 472##### Post-Initialization 473 474The post-initialization stage is intended to be used by modules to perform any 475actions that are required after all its elements have been initialized, yet 476before any module-to-module interaction is possible. An example would be some 477initialization that requires comparisons between the module's elements as this 478is the first stage in which the module has received configuration for all of its 479elements. 480 481**Note:** Participation in this stage is optional. 482 483##### Bind 484 485Each module and element has the opportunity to bind to other modules and 486elements so that their interfaces can be used in the start stage and during the 487runtime phase. 488 489The binding stage is the stage during which modules request access to each 490other's APIs. Until modules are bound they have no direct way to call functions 491from each other. 492 493**Note:** Participation in this stage is optional. 494 495##### Start 496 497Modules perform initialization that depends on using resources from other 498modules and elements, now that binding is complete and these resources are 499available. This is the final pre-runtime stage. 500 501**Note:** Participation in this stage is optional. 502 503#### Runtime phase 504 505Once the pre-runtime stages have been successfully completed, the firmware will 506start to process events raised by modules or interrupts. By default, the 507firmware will loop forever waiting for new events to process at the end of the 508pre-runtime stages but it is possible to return after processing pending events 509when the event list is empty. 510 511##### Sub system runtime mode 512 513When SCP_ENABLE_SUB_SYSTEM_MODE is set, fwk_arch_init() will return after 514processing the pending events. Then, it's up to the system to call the function 515fwk_process_event_queue() when new events have been added into the list. Such 516behavior is useful when the SCP-firmware is a sub part of a larger system like 517being an application running in an execution environnement (i.e RTOS or TEE). 518 519#### Error Handling 520 521Errors that occur during the pre-runtime phase (such as failures that occur 522during module and element initialization, memory management initialization, or 523interrupt initialization) are passed from the framework layer into the arch 524layer. It is therefore architecture-specific code that determines the ultimate 525response to these types of errors. 526 527### Binding 528 529Binding is the process in which modules and elements can form associations with 530each other and request access to APIs that are declared by modules within the 531framework. Along with issuing events, these should be the two methods by which 532a module can invoke functionality from another module. 533 534#### Module-Level and Element-Level Binding 535 536A module or element may bind to another module or element within a module. The 537goal is the same - to obtain a pointer to an API that can be used during later 538stages. 539 540When attempting to bind to an element within a module (instead of the module 541itself) the main difference is that the module that receives and processes the 542bind request has the ability to change its behavior depending on the targeted 543element. For example, a module that is requesting binding may be permitted to 544bind to only a subset of elements within the module processing the request. 545 546 547#### Processing Binding Requests 548 549When a module receives a binding request it is not required to accept it. The 550framework allows a module to reject a binding request if the module's criteria 551for correct binding are not met. 552 553For example, a power supply driver may restrict the types of modules that are 554allowed to bind to it, so that only a Hardware Abstraction Layer (HAL) module 555can utilize the driver. 556 557Alternatively, a driver that is of the *service* type may choose to restrict the 558service it provides to only a allowlisted set of modules within the firmware. In 559this case the driver module can compare the identifier of the module that is 560attempting binding with its allowlist and accept or reject the bind request as 561appropriate. 562 563If a binding request is rejected then the framework will consider that an error 564has occurred and the binding process as a whole will fail. The handling of this 565overall condition is ultimately architecture specific. 566 567### Logging 568 569The framework contains a log component to ensure that logging functionality is 570always available and is not tied to the availability of any particular 571module. The framework defines and implements the public interface for this 572component. Documentation for this interface can be found in fwk_log.h. 573 574To ensure that the framework is platform-independent, the log component relies 575on a small set of functions to do platform-specific work like flushing the 576buffer and outputting characters. These functions make up the log driver 577interface and are forward declared in fwk_log.h: 578``` 579int fwk_log_init(void); 580void fwk_log_printf(const char *format, ...); 581void fwk_log_flush(void); 582``` 583 584The logging framework uses *filter levels* to rank the criticality of messages, 585and to filter them if desired. Filtering happens at preprocessing-time, and 586consequently filtered messages do not contribute to the image. This component 587provides five filter levels for logging messages. Log messages are assigned a 588filter level based on the logging macro used. These macros (listed in order of 589decreasing verbosity) are as follows: 590- FWK_LOG_DEBUG 591- FWK_LOG_INFO 592- FWK_LOG_WARN 593- FWK_LOG_ERR 594- FWK_LOG_CRIT 595 596The value of the `FWK_LOG_LEVEL` macro, which can be set through the build 597system configuration options, determines the minimum level a log message 598must be for it to be included in the binary. If a build sets log level to 599FWK_LOG_LEVEL_DISABLED, all logs are disabled. 600 601If buffering has been enabled then log messages may be buffered to reduce 602overall firmware response latency; these buffered log messages will be flushed 603once the system has reached an idle state. This definition dictates the size of 604the buffer, and can be overridden by each individual firmware through a 605definition in a `<fmw_log.h>` header. This definition has a default value of 606four kilobytes in release builds and zero in debug builds. Setting this 607definition to a value of `0` will disable buffering. If buffering is disabled, 608messages will be transmitted immediately. 609``` 610#define FMW_LOG_BUFFER_SIZE 0 611``` 612 613The framework only implements weakly-linked "stub" versions of these functions. 614It is expected that platform-specific code (a driver module) will implement 615these functions properly to complete the logging functionality of the 616framework. Every module may provide an optional stream adapter, which allows it 617to service input/output requests to and from other modules in a fashion similar 618to standard file operations in the standard library. This adapter handles 619adapter requests to the module, its elements, and its sub-elements. 620```C 621const struct fwk_module module_xxx = { 622 .adapter = 623 (struct fwk_io_adapter) { 624 .open = mod_xxx_io_open, 625 .getch = mod_xxx_io_getch, 626 .putch = mod_xxx_io_putch, 627 .close = mod_xxx_io_close, 628 } 629}; 630``` 631 632#### Enable marked list feature 633 634When `SCP_ENABLE_MARKED_LIST` is set, the maximum size of linked list will be 635traced and marked. 636 637#### Module-level logging 638To enable module-level logging `FWK_LOG_LOCAL_ENABLE` should be defined. 639Below is a configuration for CMake that should be included in 640`CMakeLists.txt` of every module that uses this feature. This configuration 641 requires `SCP_LOG_LOCAL_ENABLE_MOD_<module name>` to be defined as a build flag 642 parameter. 643 644``` 645include(SCPModuleLogLocal) 646 647... 648 649scp_module_log_local(${SCP_MODULE}) 650``` 651