Firmware update on A+M systems

Concept

Some platforms have both Cortex-A and Cortex-M subsystems and implement the PSA RoT on the M class core. On such systems the firmware store is usually read-only or not accessible to the Cortex-A cores due to security reasons. In such cases it may still be beneficial to implement the cloud update client on the Cortex-A subsystem, and a solution is needed to bridge the Cortex-A and the Cortex-M firmware update implementations. For this purpose TS implements a special firmware update agent which is documented below.

../../_images/psa-fwu-m-block.svg

Components on an A+M system.

Standards and specifications

Update agent implementation

The update agent implements the Platform Security Firmware Update for the A-profile Arm Architecture API (prefixed with fwu_) and acts as a PSA Certified Firmware Update API 1.0 client (prefixed with psa_fwu_). The update agent has to align the slightly different APIs by maintaining an internal registry of image states and image handles. The differences between the two firmware update protocols are:

The solutions to these differences:

  • A static UUID to component ID mapping is used. This has to be passed to the agent init function.

  • Gathering per image based calls and then make the actual call to the M class side when the operation was called on each image.

  • Implement internal image handle registry.

  • Convert the image query result returned by FWU-M to FWU-A format. There are similar field, but this imposes some limitations.

Initialization

The initial image and agent state is determined based on the image state returned by psa_fwu_query().

fwu_discover()

This function returns the list of implemented features.

fwu_begin_staging()

Clients can only write images in staging state. In order to switch to staging state, the client must call fwu_begin_staging(). This results in psa_fwu_start() calls on all selected images. The client can pass a list of the selected image UUIDs or select all images for staging.

If the agent is already in staging state when fwu_begin_staging() is called, then the agent first discards all transient state by calling fwu_cancel_staging() and psa_fwu_clean() on all image.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_begin_staging()

alt state == Staging
	agent -> agent: fwu_cancel_staging()
	agent --> agent

	loop on all images
		agent -> service: psa_fwu_clean(image)
		service --> agent
	end
end

loop on selected images
	agent -> service: psa_fwu_start(image)
	service --> agent
end

hnote over agent: state = Staging

agent --> client

@enduml

fwu_end_staging()

Finishes the staging state when all images that were selected for the update have been updated. If all selected images are also accepted the agent switches to regular state and the update is completed. If there are not accepted images, the agent switches to trial state, so the client can validate the new set of images and accept or reject them.

On calling fwu_end_staging() the agent calls psa_fwu_finish() on each selected image, then calls psa_fwu_install(). If all images have been accepted (see fwu_commit()) it also calls psa_fwu_accept().

Since FWU-M may return PSA_SUCCESS_REBOOT or PSA_SUCCESS_RESTART for A+M systems, the FWU PSA IPC client coerces these statuses to PSA_SUCCESS because FWU-A does not define an equivalent return code.

In an A+M system the M class side shouldn’t restart the system, so calling psa_fwu_request_reboot() does not fit the system. There’s also no PSA FWU A return code for inidicating the restart request to the normal world. If the normal world has to restart the system after ending the staging phase, it has to do it in an implementation defined way.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_end_staging()

alt state == Staging
	loop on all selected images
		agent -> service: psa_fwu_finish(image)
		service --> agent
	end

	agent -> service: psa_fwu_install()
	service --> agent

	alt all images accepted
		agent -> service: psa_fwu_accept()
		service --> agent
		hnote over agent: state = Regular
	else
		hnote over agent: state = Trial
	end
end

agent --> client

@enduml

fwu_cancel_staging()

Cancels staging state and reverts to original regular state. The function calls psa_fwu_cancel() on each selected image.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_cancel_staging()

alt state == Staging
	loop on all selected images
		agent -> service: psa_fwu_cancel(image)
		service --> agent
	end

	hnote over agent: state = Regular
end

agent --> client

@enduml

fwu_open()

Platform Security Firmware Update for the A-profile Arm Architecture has a concept of image handles. An image can be opened via fwu_open(), then accessed by calling fwu_write_stream() or fwu_read_stream(), and finally it can be closed by invoking fwu_commit(). However PSA Certified Firmware Update API 1.0 does not have a similar concept and once the staging process was started for an image by calling psa_fwu_start(), the image can be written through psa_fwu_write().

The update agent provides an internal registry of opened images and their access rights so it can handle subsequent calls on the image handle. The open call can open an image for either write or read operations but not for both. The image is identified by its UUID. Only images which were selected for staging can be opened for write.

There is no actual call made to the M class side on opening a image.

PSA Certified Firmware Update API 1.0 does not provide a function for reading images, so opening images will fail except for opening the image directory. Only the image directory supports read operations.

fwu_write_stream()

This function writes data into the opened image. The image handle has to be opened for write operations. The agent calls psa_fwu_write() for doing the actual write and the write offset is tracked internally.

fwu_read_stream()

This function read data from the opened image. The image handle has to be opened for read operations.

This call is only implemented for the image directory which returns the available image list as specified in Platform Security Firmware Update for the A-profile Arm Architecture. It does not support the partial reading of the image directory.

fwu_commit()

The commit call closes the image handle. The client can also mark the image as accepted on commit and this the method for accepting all images before calling fwu_end_staging().

There is no actual call made to the M class side on comiting an image.

fwu_accept_image()

PSA Certified Firmware Update API 1.0 only provides a psa_fwu_accept() function which accepts the whole set of selected images. In order to align with the fwu_accept_image() API, it only marks the given image as accepted and calls psa_fwu_accept() when all images have been accepted. This results in a state transition to regular state.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_accept_image()

alt state == Trial
	agent -> agent: mark image accepted
	agent --> agent

	alt all images accepted
		agent -> service: psa_fwu_accept()
		service --> agent
		hnote over agent: state = Regular
	end
end

agent --> client

@enduml

fwu_select_previous()

Selects previous working state (i.e. rejects the firmware update) and transitions back to regular state after calling psa_fwu_reject(). The implementation treats PSA_SUCCESS_REBOOT and PSA_SUCCESS_RESTART status values as error. In an A+M system the M class side shouldn’t restart the system, so calling psa_fwu_request_reboot() does not fit the system. There’s also no PSA FWU A return code for inidicating the restart request to the normal world. If the normal world has to restart the system when rejecting the installed firmware, it has to do it in an implementation defined way.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_select_previous()

alt state == Trial
	agent -> service: psa_fwu_reject(0)
	service --> agent
	hnote over agent: state = Regular
end

agent --> client

@enduml

Image directory

The client can read the image directory by opening and reading an image with dedicated UUID (deee58d9-5147-4ad3-a290-77666e2341a5). On image directory read, the agent will call psa_fwu_query() on each image and convert the value of similar fields.

  • The UUID is based on the UUID - component ID mapping passed upon agent initialization.

  • The images only support write operation due to FWU-M limitation.

  • The image maximal size is copied from the component info structure.

  • The lowest accepted version is set to 0.

  • The image version is converted from the fields of the component info structure into a single 32 bit value. The build field is dropped due to lack of space in the 32 bit field.

  • The images is marked accepted if its state in the component info structure is PSA_FWU_UPDATED.

'-------------------------------------------------------------------------------
' Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.
'
' SPDX-License-Identifier: BSD-3-Clause
'
'-------------------------------------------------------------------------------

@startuml
participant "PSA FWU A Update Client" as client
participant "PSA FWU A Update Agent" as agent
participant "PSA FWU M Service" as service

autoactivate on

client -> agent: fwu_read_stream(image directory)

agent -> agent: fill image_directory structure
agent --> agent

loop on all images
	agent -> service: psa_fwu_query(image)
	service --> agent: psa_fwu_component_info_t

	agent -> agent: convert psa_fwu_component_info_t to img_info_entry
	agent --> agent: img_info_entry
end

agent --> client

@enduml


Copyright (c) 2024, Arm Limited and Contributors. All rights reserved.

SPDX-License-Identifier: BSD-3-Clause