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