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