268 lines
10 KiB
Plaintext
268 lines
10 KiB
Plaintext
Introduction
|
|
============
|
|
|
|
The PIL (Peripheral Image Loader) driver loads peripheral images into memory
|
|
and interfaces with the Peripheral Authentication Service (PAS) to
|
|
authenticate and reset peripherals embedded in the SoC.
|
|
|
|
The PAS could either be running under secure mode in the application
|
|
processor (secure boot support) or be running as a non-secure kernel driver
|
|
(non-secure boot support).
|
|
|
|
The PIL driver also does housekeeping to handle cases where more than one
|
|
client driver is using the same peripheral.
|
|
|
|
Some examples of peripherals are modem, DSP and sensors.
|
|
|
|
Hardware description
|
|
====================
|
|
|
|
The memory used by the peripherals for code and data storage will be
|
|
accessible as normal memory to the application processor.
|
|
|
|
The non-secure code (Linux kernel) will have read/write permissions to the
|
|
peripheral memory by default.
|
|
|
|
The PAS will have access to a MPU (memory protection unit) that can lock away
|
|
the pages of memory from the Linux kernel. It will also have access to
|
|
registers that can reset each peripheral.
|
|
|
|
Software description
|
|
====================
|
|
|
|
The PAS provides the following three APIs:
|
|
|
|
* Init image - Takes as input the peripheral id and firmware metadata and
|
|
returns a status indicating the authenticity of the firmware metadata. The
|
|
firmware metadata consists of a standard ELF32 header followed by a program
|
|
header table and an optional blob of data used to authenticate the metadata
|
|
and the rest of the firmware.
|
|
|
|
* Verify segment - Takes as input the firmware segment id and the length of
|
|
the segment. Authenticates whatever amount (specified by the "length"
|
|
parameter) of the firmware segment that has been loaded and removes
|
|
non-secure mode read/write permissions for the pages belonging to the
|
|
firmware segment. Allows multiple calls for the same firmware segment to
|
|
allow partial loading and authentication.
|
|
|
|
* Auth and Reset - Verifies all the necessary firmware segments have been
|
|
loaded and authenticated and then resets the peripheral.
|
|
|
|
The user space is expected to provide the firmware metadata and firmware
|
|
segments as separate files on persistent storage. See "Interface" section for
|
|
further details.
|
|
|
|
The PIL driver will use the request_firmware API provided by the Linux kernel
|
|
to read the firmware and firmware metadata from persistent storage.
|
|
|
|
When a client driver requests for a peripheral to be enabled, the PIL driver
|
|
increments the reference count for that peripheral, loads the firmware
|
|
metadata and calls the PAS Init Image API that initializes the authentication
|
|
state machine using the firmware metadata.
|
|
|
|
If the initialization succeeds, the PIL driver loads the appropriate firmware
|
|
segments into their respective memory locations and call the PAS Verify
|
|
segment API on each of the loaded segments to authenticate and lock it.
|
|
|
|
After all the firmware segments have been successfully loaded and
|
|
authenticated, the PAS Auth and Reset API is called to reset the peripheral
|
|
and initiate its boot sequence.
|
|
|
|
A peripheral enable request to the PIL driver will block until it succeeds
|
|
(or fails) to initiate the peripheral boot sequence but will NOT block until
|
|
the peripheral is ready. It is not possible to block until a peripheral is
|
|
ready since the semantics of "ready" is subjective to the caller.
|
|
|
|
The PIL driver will maintain a reference count for each of the peripherals.
|
|
So, if a peripheral is already under use and another client driver requests
|
|
for the peripheral to be enabled, the PIL driver will immediately return a
|
|
value to indicate success.
|
|
|
|
When all the client drivers of a particular peripheral no longer need the
|
|
peripheral and the reference count reaches zero, the PIL driver can cleanly
|
|
shut down the peripheral. Since a lot of drivers in their current state can't
|
|
handle a peripheral restart, the PIL driver will never let the reference
|
|
count go back to zero.
|
|
|
|
All information about a peripheral, like firmware filenames, peripheral ID
|
|
passed to PAS, etc, will be hard coded in the PIL driver.
|
|
|
|
All the PIL APIs will execute in the context of the caller. This includes
|
|
calls from the PIL driver to the PAS driver. The PAS driver might decide to
|
|
switch into secure mode from a separate workqueue or in the same context as
|
|
the caller, but that shouldn't have any implications for the PIL API callers
|
|
since all the PIL APIs are blocking calls.
|
|
|
|
Dependencies:
|
|
-------------
|
|
* Firmware class (CONFIG_FW_LOADER) for using the request_firmware API to
|
|
load firmware from persistent storage.
|
|
* PAS to authenticate firmware and bring a peripheral out of reset.
|
|
|
|
Error cases:
|
|
------------
|
|
The PIL driver could fail to enable a peripheral for several reasons like not
|
|
having enough memory to load firmware and metadata, being unable to
|
|
communicate with the PAS, the PAS returning with an error, etc. For all
|
|
possible error cases, the PIL driver does not perform any retries and returns
|
|
an appropriate error code. The client drivers should always check for success
|
|
before trying to access the peripheral.
|
|
|
|
Design
|
|
======
|
|
|
|
Design goals:
|
|
-------------
|
|
* The PIL driver must be agnostic to the actual format and method used to
|
|
authenticate the firmware.
|
|
* Allow for future expansion to support demand loading of parts of firmware
|
|
for each peripheral.
|
|
* Move most of the work into the preprocessing/building stage of the firmware.
|
|
* Provide an API to the client drivers that absolves them from having to know
|
|
the structure or names of the firmware in persistent storage.
|
|
* Handle multiple client drivers wanting to enable the same peripheral.
|
|
|
|
|
|
Design reasons:
|
|
---------------
|
|
The user space is expected to provide the firmware metadata and segments as
|
|
separate files for the following reasons:
|
|
* Don't need to load the whole ELF file if the authentication info is
|
|
invalid.
|
|
* Works better during low memory conditions since the amount of memory used
|
|
at any given instant when loading one segment at a time is smaller than
|
|
loading the whole ELF file.
|
|
* Since an ELF segment in memory can be much bigger than on file, having a
|
|
flat binary would waste a lot of space due to zero-fills.
|
|
* Allows for future enhancements to the loading procedure.
|
|
|
|
Design tradeoffs:
|
|
-----------------
|
|
* With appropriate changes to the request_firmware API, the firmware blobs
|
|
could be directly loaded into the right memory location. But due to the
|
|
additional work and community approval that would be needed for modifying
|
|
the request_firmware API, we load the firmware blobs into kernel memory and
|
|
then copy them into the appropriate locations.
|
|
|
|
Alternate designs:
|
|
------------------
|
|
One of the alternate designs that were considered required the firmware to be
|
|
a flat binary. Although this design would simplify the PIL driver, it would
|
|
result in the waste of a lot of persistent storage space (due to large
|
|
zero-fills), prevent demand loading of segments in the future and use a lot
|
|
more memory while loading the firmware.
|
|
|
|
Software layering:
|
|
------------------
|
|
The peripheral authentication, reset and shutdown implementation is factored
|
|
away into a Peripheral Authentication Service driver to allow the PIL driver
|
|
to be agnostic of secure vs. non-secure boot and the mechanisms needed for
|
|
communicating with any code that might be running in secure mode.
|
|
|
|
Power Management
|
|
================
|
|
|
|
Some of the peripherals might support being turned off when not in use.
|
|
Support for this might be disabled in the initial implementation of the PIL
|
|
driver since many of the existing drivers can not handle peripheral restart.
|
|
|
|
SMP/multi-core
|
|
==============
|
|
|
|
Will use mutexes to protected data that might be shared (reference count,
|
|
etc).
|
|
|
|
Security
|
|
========
|
|
|
|
The PIL driver must validate the physical memory addresses specified in the
|
|
ELF and program header table before loading firmware segments to make sure
|
|
it's not overwriting any memory used by the kernel and possibly PMEM regions
|
|
(if it can be done without being an ugly hack). The PIL driver might need to
|
|
maintain a white list or black list of physical memory address ranges to
|
|
perform the address validation.
|
|
|
|
Performance
|
|
===========
|
|
|
|
As mentioned in the design section, the loading of firmware segments is not
|
|
optimal and has room for improvement.
|
|
|
|
Interface
|
|
=========
|
|
|
|
In kernel APIs:
|
|
void * pil_get(char *peripheral_name)
|
|
- Enables (if not already enabled) a peripheral and returns a handle
|
|
that can be used to disable the peripheral at a later time. If
|
|
peripheral can't be enabled successfully, then returns an error
|
|
(use IS_ERR) indicating the reason.
|
|
|
|
void pil_put(void *peripheral_handle)
|
|
- Inform PIL that this client no longer needs the peripheral to be
|
|
active. Does not necessarily mean that the peripheral would be
|
|
disabled or powered off.
|
|
|
|
|
|
User space APIs:
|
|
All firmware must be located in the path that is expected by the hotplug (or
|
|
compatible) daemon. A hotplug (or compatible) daemon should be running and be
|
|
able to handle events from the kernel requesting for a firmware file.
|
|
|
|
The basename of the firmware files will depend on the peripheral. For a given
|
|
peripheral, the metadata filename should end with a ".mdt" and the firmware
|
|
segment files should end with ".bXX" where XX denotes the index of the
|
|
firmware segment starting from 0.
|
|
|
|
Android hotplug compatible daemon expects the firmware files to be under
|
|
/etc/firmware.
|
|
|
|
Driver parameters
|
|
=================
|
|
|
|
No module or kernel command line parameters supported.
|
|
|
|
Config options
|
|
==============
|
|
|
|
This driver is enabled using the MSM_PIL kernel config option and will
|
|
depend on the CONFIG_FW_LOADER being available.
|
|
|
|
Dependencies
|
|
============
|
|
|
|
Depends on firmware class module for the request_firmware API.
|
|
|
|
Interacts with the PAS to authenticate the firmware and to initiate the boot
|
|
sequence of a peripheral.
|
|
|
|
Doesn't communicate with other processors since the secure code, if any, will
|
|
be running on the application processor cores.
|
|
|
|
User space utilities
|
|
====================
|
|
|
|
None.
|
|
|
|
Other
|
|
=====
|
|
|
|
The firmware_class driver might be changed in the future to directly load the
|
|
firmware into memory locations provided by the caller of request_firmware().
|
|
|
|
Known issues
|
|
============
|
|
|
|
Since support for cleanly shutting down peripherals is yet to be added, the
|
|
reference count of peripherals will never be allowed to go to zero once it
|
|
becomes non-zero.
|
|
|
|
To do
|
|
=====
|
|
|
|
* Add support for turning off peripherals when they are not in use.
|
|
* Modify request_firmware() to directly copy firmware blobs into the
|
|
appropriate memory locations.
|
|
* Add support for demand loading of firmware segments.
|
|
* Add support for forced peripheral restarts.
|