This article introduces how the NVMEM Linux® framework manages BSEC OTP data and how to read/write from/to it.
1. Framework purpose[edit | edit source]
The NVMEM Linux® framework provides a generic interface for non-volatile memory data devices such as:
- OTP (one-time programmable) fuses
- EEPROM
It offers kernel space and user space interfaces to read/write data such as analog calibration data or MAC address.
2. System overview[edit | edit source]
2.1. Component description[edit | edit source]
- NVMEM user (user space)
The user can use the NVMEM sysfs interface from a user terminal or a custom application to read/write data from/to NVMEM devices, and from user space.
- NVMEM user (kernel space)
The 'foo_driver' could be any driver in kernel space that uses the NVMEM API to read/write data from/to NVMEM devices (such as the analog calibration data used by an ADC driver).
- NVMEM framework (kernel space)
The NVMEM core provides sysfs interface and NVMEM API. They can be used to implement NVMEM user and NVMEM controller drivers.
- NVMEM drivers (kernel space)
Provider drivers such as STM32 ROMEM Linux® driver that exposes BSEC OTP data to the core.
- TEE framework (kernel space)
The TEE framework provides TEE client API to communicate with secure services, as the services provided by the OP-TEE Linux® driver.
- OP-TEE (Secure)
The OP-TEE secure OS is running on the Cortex-A in secure mode and exposes secure service with Trusted Applications (TA), as BSEC PTA .
- NVMEM hardware
NVMEM controllers such as the BSEC internal peripheral[1]
2.2. API description[edit | edit source]
The NVMEM kernel documentation[2] describes:
- Kernel space API for NVMEM providers and NVMEM consumers.
- User space binary interface (sysfs).
See also sysfs-bus-nvmem[3] ABI documentation.
3. Configuration[edit | edit source]
3.1. Kernel configuration[edit | edit source]
Activate NVMEM framework in the kernel configuration through the Linux® menuconfig tool (see Menuconfig or how to configure kernel) with CONFIG_NVMEM = y:
Device Drivers ---> [*] NVMEM Support ---> <*> STMicroelectronics STM32 factory-programmed memory support
3.2. Device tree configuration[edit | edit source]
The NVMEM data device tree bindings describe:
The BSEC internal peripheral[1] device tree bindings are described in the BSEC device tree configuration article.
4. How to use the framework[edit | edit source]
4.1. How to use NVMEM with sysfs interface[edit | edit source]
4.1.1. How to list NVMEM devices[edit | edit source]
The available NVMEM devices can be listed in sysfs directory /sys/bus/nvmem/devices.
Example of listing nvmem devices: BSEC is stm32-romem0
ls /sys/bus/nvmem/devices/ stm32-romem0
4.1.2. How to read OTP areas using NVMEM[edit | edit source]
The user space can read/write the raw NVMEM file located at /sys/bus/nvmem/devices/*/nvmem.
For BSEC, the NVEM is the stm32-romem0 device. The content of non-secure OTP areas can be read but the secured OTP areas are masked, and their values replaced by 0.
Normally only the 32 lower OTP data can be accessed, the upper OTP data being restricted to security. If the user needs more than the 32 lower OTP data, this can be managed by an exception described in BSEC device tree configuration.
- Example of reading all nvmem data on stm32-romem0 devices
dd if=/sys/bus/nvmem/devices/stm32-romem0/nvmem of=/tmp/file
- Example of displaying all nvmem data
hexdump -C -v /sys/bus/nvmem/devices/stm32-romem0/nvmem
4.1.3. How to write BSEC OTP data using NVMEM[edit | edit source]
The BSEC OTP areas can be written by 32-bit word starting at OTP N, as follows:
# write OTP N word by word dd if=/tmp/file of=/sys/bus/nvmem/devices/stm32-romem0/nvmem bs=4 seek=N
or
# write OTP N, all the file in one request dd if=/tmp/file of=/sys/bus/nvmem/devices/stm32-romem0/nvmem seek=4*N oflag=seek_bytes
With a file /tmp/file containing the OTP data to write, its size is 32-bit word aligned, for example:
# Create a 4 bytes length file filled with ones, e.g. 0xffffffff) dd if=/dev/zero count=1 bs=4 | tr '\000' '\377' > file # Create a 4 bytes length file, here 0x00000001 to update one OTP echo -n -e '\x01\x00\x00\x00' > /tmp/file # Create a 8 bytes length file, here 0x67452301 0xEFCDAB89 to update two OTPs echo -n -e '\x01\x23\x45\x67\x89\xAB\xCD\xEF' > /tmp/file
A lower OTP area can be written several times for a bit per bit update if it is not locked.
An upper OTP area can be written only if it is allowed in secure world device tree, and only once. When the upper OTP is written, it is permanent locked at the end of the NVMEM request to an avoid ECC issue on the second update. For the first example with bs = 4, this lock is performed after each OTP update, while for the second example with oflag = seek_bytes, it is done when all the OTP data in the input file are updated.
Below a compete example of writing the upper OTP 60:
echo -n -e '\x01\x23\x45\x67' > /tmp/file hexdump -C /tmp/file 00000000 01 23 45 67 |.#Eg| 00000004 dd if=/tmp/file of=/sys/bus/nvmem/devices/stm32-romem0/nvmem bs=4 seek=60 reboot << >> hexdump -C -v /sys/bus/nvmem/devices/stm32-romem0/nvmem .... 000000f0 01 23 45 67 00 00 00 00 00 00 00 00 00 00 00 00 |.#Eg............| ....
The associated output in STM32CubeProgrammer is:
OTP REGISTERS:
---------------------------------------------------------------------------
ID | value | status
---------------------------------------------------------------------------
...
060 | 0x67452301 | 0x40000000
|_[30] Permanent write lock
or in U-Boot
> fuse read 0 0 96 ... Word 0x0000003c: 67452301 00000000 00000000 00000000 ...
5. How to trace the framework[edit | edit source]
Ftrace can be used to trace the NVMEM framework:
cd /sys/kernel/debug/tracing cat available_filter_functions | grep nvmem # Show available filter functions rtc_nvmem_register rtc_nvmem_unregister nvmem_reg_read bin_attr_nvmem_read ...
Enable the kernel function tracer, then start using nvmem and display the result:
echo function > current_tracer echo "*nvmem*" > set_ftrace_filter # Trace all nvmem filter functions echo 1 > tracing_on # start ftrace hexdump -C -v /sys/bus/nvmem/devices/stm32-romem0/nvmem # dump nvmem 00000000 17 00 00 00 01 80 00 00 00 00 00 00 00 00 00 00 |................| ... echo 0 > tracing_on # stop ftrace cat trace # tracer: function # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / delay # TASK-PID CPU# |||| TIMESTAMP FUNCTION # | | | |||| | | hexdump-478 [000] .... 423.502278: bin_attr_nvmem_read <-sysfs_kf_bin_read hexdump-478 [000] .... 423.502290: nvmem_reg_read <-bin_attr_nvmem_read hexdump-478 [000] .... 423.515804: bin_attr_nvmem_read <-sysfs_kf_bin_read
6. References[edit | edit source]
- ↑ 1.0 1.1 BSEC internal peripheral
- ↑ Documentation/driver-api/nvmem.rst , NVMEM subsytem kernel documentation
- ↑ Documentation/ABI/stable/sysfs-bus-nvmem , NVMEM ABI documentation
- ↑ Documentation/devicetree/bindings/nvmem/nvmem.yaml , NVMEM device tree bindings
- ↑ Documentation/devicetree/bindings/nvmem/nvmem-consumer.yaml , NVMEM consumer device tree bindings