Using The One Time Programmable Memory On Raspberry Pi Single Board Computers
Using The One Time Programmable Memory On Raspberry Pi Single Board Computers
Colophon
© 2020-2023 Raspberry Pi Ltd (formerly Raspberry Pi (Trading) Ltd.)
This documentation is licensed under a Creative Commons Attribution-NoDerivatives 4.0 International (CC BY-ND 4.0)
licence.
build-date: 2023-02-10
build-version: githash: c65fe9c-clean
RPL reserves the right to make any enhancements, improvements, corrections or any other modifications to the
RESOURCES or any products described in them at any time and without further notice.
The RESOURCES are intended for skilled users with suitable levels of design knowledge. Users are solely responsible for
their selection and use of the RESOURCES and any application of the products described in them. User agrees to
indemnify and hold RPL harmless against all liabilities, costs, damages or other losses arising out of their use of the
RESOURCES.
RPL grants users permission to use the RESOURCES solely in conjunction with the Raspberry Pi products. All other use of
the RESOURCES is prohibited. No licence is granted to any other RPL or other third party intellectual property right.
HIGH RISK ACTIVITIES. Raspberry Pi products are not designed, manufactured or intended for use in hazardous
environments requiring fail safe performance, such as in the operation of nuclear facilities, aircraft navigation or
communication systems, air traffic control, weapons systems or safety-critical applications (including life support
systems and other medical devices), in which the failure of the products could lead directly to death, personal injury or
severe physical or environmental damage (“High Risk Activities”). RPL specifically disclaims any express or implied
warranty of fitness for High Risk Activities and accepts no liability for use or inclusions of Raspberry Pi products in High
Risk Activities.
Raspberry Pi products are provided subject to RPL’s Standard Terms. RPL’s provision of the RESOURCES does not
expand or otherwise modify RPL’s Standard Terms including but not limited to the disclaimers and warranties expressed
in them.
Scope of document
This document applies to the following Raspberry Pi Ltd products:
* * * * * * * * * * * * * * * * *
Introduction
All Raspberry Pi single-board computers (SBCs) have an inbuilt area of one-time programmable (OTP) memory, which is
actually part of the main system on a chip (SoC). As its name implies, OTP memory can be written to (i.e. a binary 0 can
be changed to a 1) only once. Once a bit has been changed to 1, it can never be returned to 0. One way of looking at the
OTP is to consider each bit as a fuse. Programming involves deliberately blowing the fuse — an irreversible process as you
cannot get inside the chip to replace it!
Some of this memory is used for various items of system data, as described later, but there are also eight rows of 32 bits
(256 bits in total) available for the customer to program. This can be used for any purpose the customer requires: for
example, product identification, storage of cryptographic keys, etc.
This white paper assumes that the Raspberry Pi is running the Raspberry Pi operating system (OS), and is fully up to date
with the latest firmware and kernels.
Introduction 3
Using the One-Time Programmable Memory on Raspberry Pi Single-Board Computers
NOTE
There is a tool available from the Raspberry Pi OS command line to list the content of the rows in the OTP:
vcgencmd otp_dump
On a fresh Raspberry Pi with Raspberry Pi OS many of these rows are 0, but there are some values that are
preprogrammed at the factory when the boards are manufactured.
Row Meaning
28 Serial number
30 Revision code; also contains bits to disable overvoltage, OTP programming, and OTP reading
• Compute Module 4
Bit 30: 0 if Wi-Fi® module is fitted, 1 if not fitted
Bit 31: 0 if EMMC module is fitted, 1 if not fitted
• Raspberry Pi 400
Bits 0-7: The default keyboard country code used by piwiz (Raspberry Pi OS startup wizard)
Row Meaning
64/65 MAC address; if set, the system will use this in preference to the automatically generated address
based on the serial number. This is factory-set on Raspberry Pi 4 and later.
Customer OTP
The customer OTP area is defined as rows 36 to 43. No other areas in the OTP, except for the device-specific private key,
can be changed by the end user.
WARNING
Changing customer OTP bits from 0 to 1 is irreversible. They are called one-time programmable for a reason!
Reading and writing customer OTP values require the vcmailbox command line application. This is installed by default on
Raspberry Pi OS.
Because the vcmailbox system is a generic way of communicating with the graphics processing unit (GPU), it is not
immediately obvious how it works when using it to write to the OTP.
The general form for communicating with the GPU using OTP commands is:
where
For example, to program OTP customer rows 4, 5, and 6 to 0x11111111, 0x22222222, 0x33333333 respectively, you
would use:
This is programming three rows, so our calculation for the third and fourth arguments of the command is 8 + (3 * 4) = 20.
This actually represents the number of bytes of value data plus the 8 bytes for the start_num and number at 4 bytes each.
Our command will then program OTP rows 40, 41, and 42.
NOTE
The vcmailbox command is actually of the form vcmailbox <command> <data to send in bytes> <data to receive
in bytes> [command-specific data], which goes some way to explaining need for the calculation above.
$vcmailbox 0x00030021 20 20 4 3 0 0 0
0x0000002c 0x80000000 0x00030021 0x00000014 0x80000014 0x00000000 0x00000003
0x11111111 0x22222222 0x33333333
The last three 32-bit values returned should be those of the three rows beginning with customer row 4 — here, the values
written to the OTP by the example command in the previous section.
Again, this is achieved using a mailbox call, but in this case with a very specific set of parameters that are used only for
this purpose:
WARNING
These rows can be programmed and read using similar vcmailbox commands to those used for managing customer OTP
rows. If secure boot / file system encryption is not required, then the device private key rows can be used to store general
purpose information.
• The device private key rows can only be read via the vcmailbox command, which requires access to /dev/vcio,
which in turn is restricted to the video group on Raspberry Pi OS.
• Raspberry Pi computers do not have a hardware-protected key store. It is recommended that this feature be used in
conjunction with secure boot in order to restrict access to this data.
The rpi-otp-private-key script wraps the device private key vcmailbox APIs in order to make it easier to read/write a key in
the same format as OpenSSL.
rpi-otp-private-key
Example output:
f8dbc7b0a4fcfb1d706e298ac9d0485c2226ce8df7f7596ac77337bd09fbe160
WARNING
vcmailbox support
The rpi-otp-private-key script uses the following vcmailbox calls in much the same way as described in the customer
OTP section.
vcmailbox 0x00030081 40 40 0 8 0 0 0 0 0 0 0 0
Example output:
Write an entire the row (replace the eight trailing zeros with the key data):
vcmailbox 0x00038081 40 40 0 8 0 0 0 0 0 0 0 0
It is envisaged that the private key will be loaded by an init-script in an initramfs, which in turn is used to mount a LUKS
encrypted file system.
For this to be secure, and prevent reading of the key by a third-party software installation, it is important that the system
uses the secure/signed boot procedure that is available on Raspberry Pi 4 and CM4. This ensures there is a chain of trust
up to and including the initramfs, which ensures that only code signed to run on the system has access to the OTP. Past
this stage, any further security is up to the customer — for example, ensuring SSH or similar remote access protocols are
sufficiently protected.
The following example set of bash commands will generate a random key, and create a LUKS encrypted file system based
on that key using cryptsetup.
# Generate 256 bit random number for key and write it to OTP. Raspberry Pi specific
rpi-otp-private-key -w $(openssl rand -hex 32)
# For debug
sudo cryptsetup luksDump ${BLK_DEV}
• Company name
You can read this back for a very basic security check.