Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ jobs:
with:
submodules: recursive

- run: sudo apt install -y gcc-${{ matrix.target}} build-essential
- run: |
sudo apt update
sudo apt install -y gcc-${{ matrix.target}} build-essential
- run: ./autogen.sh
- run: ./configure --host=${{ matrix.target }} --prefix=/usr
- run: make binary-dist
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ AM_CFLAGS = -std=c99 -pedantic -W -Wall -Wextra -Wno-unused-parame
AM_CFLAGS += -Werror

sbin_PROGRAMS = bootcount
bootcount_SOURCES = bootcount.c am33xx.c stm32mp1.c i2c_eeprom.c dm_eeprom.c memory.c \
bootcount_SOURCES = bootcount.c am33xx.c stm32mp1.c i2c_eeprom.c dm_eeprom.c dm_rtc.c memory.c \
dt.c imx8m.c imx93.c

6 changes: 6 additions & 0 deletions src/bootcount.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
#include "stm32mp1.h"
#include "i2c_eeprom.h"
#include "dm_eeprom.h"
#include "dm_rtc.h"

struct platform {
const char *name;
Expand Down Expand Up @@ -71,6 +72,11 @@ static const struct platform platforms[] = {
.read_bootcount = dm_eeprom_read_bootcount,
.write_bootcount = dm_eeprom_write_bootcount
},
{.name = DM_RTC_NAME,
.detect = dm_rtc_exists,
.read_bootcount = dm_rtc_read_bootcount,
.write_bootcount = dm_rtc_write_bootcount
},
{.name = EEPROM_NAME,
.detect = eeprom_exists,
.read_bootcount = eeprom_read_bootcount,
Expand Down
39 changes: 24 additions & 15 deletions src/dm_eeprom.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,28 +147,23 @@ static bool discover_dm_eeprom(void)
return true;
}

static int dm_eeprom_open(void)
int dm_eeprom_open_path(const char *path, off_t offset)
{
if (!discover_dm_eeprom())
if (path == NULL)
return E_DEVICE;
int fd = open(g_eeprom_sysfs_path, O_RDWR);
int fd = open(path, O_RDWR);
if (fd < 0)
return E_DEVICE;
if (lseek(fd, g_offset, SEEK_SET) == -1) {
if (lseek(fd, offset, SEEK_SET) == -1) {
close(fd);
return E_DEVICE;
}
return fd;
}

bool dm_eeprom_exists(void)
{
return discover_dm_eeprom();
}

int dm_eeprom_read_bootcount(uint16_t *val)
int dm_eeprom_read_path(const char *path, off_t offset, uint8_t magic, uint16_t *val)
{
int fd = dm_eeprom_open();
int fd = dm_eeprom_open_path(path, offset);
if (fd < 0)
return fd;

Expand All @@ -179,7 +174,7 @@ int dm_eeprom_read_bootcount(uint16_t *val)
}
close(fd);

if (bytes[1] != DM_I2C_MAGIC) {
if (bytes[1] != magic) {
/* Upstream DM driver resets counter to 0 on invalid magic.
We have a reset command, so do not write on a read operation
*/
Expand All @@ -189,14 +184,14 @@ int dm_eeprom_read_bootcount(uint16_t *val)
return 0;
}

int dm_eeprom_write_bootcount(uint16_t val)
int dm_eeprom_write_path(const char *path, off_t offset, uint8_t magic, uint16_t val)
{
int fd = dm_eeprom_open();
int fd = dm_eeprom_open_path(path, offset);
if (fd < 0)
return fd;
unsigned char bytes[2];
bytes[0] = (unsigned char)(val & 0xff);
bytes[1] = DM_I2C_MAGIC;
bytes[1] = magic;
ssize_t written = write(fd, bytes, sizeof(bytes));
if (written != (ssize_t)sizeof(bytes)) {
close(fd);
Expand All @@ -206,4 +201,18 @@ int dm_eeprom_write_bootcount(uint16_t val)
return 0;
}

bool dm_eeprom_exists(void)
{
return discover_dm_eeprom();
}

int dm_eeprom_read_bootcount(uint16_t *val)
{
return dm_eeprom_read_path(g_eeprom_sysfs_path, g_offset, DM_I2C_MAGIC, val);
}

int dm_eeprom_write_bootcount(uint16_t val)
{
return dm_eeprom_write_path(g_eeprom_sysfs_path, g_offset, DM_I2C_MAGIC, val);
}

3 changes: 3 additions & 0 deletions src/dm_eeprom.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
bool dm_eeprom_exists(void);
int dm_eeprom_read_bootcount(uint16_t *val);
int dm_eeprom_write_bootcount(uint16_t val);

int dm_eeprom_read_path(const char *path, off_t offset, uint8_t magic, uint16_t *val);
int dm_eeprom_write_path(const char *path, off_t offset, uint8_t magic, uint16_t val);
193 changes: 193 additions & 0 deletions src/dm_rtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/* Support u-boot rtc bootcount devices via DM RTC.
* ref: u-boot/drivers/bootcount/rtc.c
*
* Example device tree fragment:
*
* chosen {
* // see: u-boot/drivers/bootcount/bootcount-uclass.c
* u-boot,bootcount-device = &bootcount_rv3028;
* };
*
* // Phycore contains an RV-3028-C7 RTC
* // ensure CONFIG_RV3028 is enabled in U-Boot config
* bootcount_rv3028: bc_rv3028 {
* // see: u-boot/drivers/bootcount/rtc.c
* compatible = "u-boot,bootcount-rtc";
* rtc = <&i2c_som_rtc>;
* offset = <0x1F>; // registers 0x1F-0x20 are "User RAM"
* // In linux, the rtc-rv3028 driver creates a two-byte nvmem. So in linux the offset is not the same
* // as the I2C register offset. So we use another property to specify the linux,nvmem-offset:
* linux,nvmem-offset = <0x00>;
* // The rtc-rv3028 driver creates two nvmem devices, one for "User RAM" with type "Battery backed"
* // and one for "EEPROM" with type "EEPROM". We want the "Battery backed" one because the rv3028
* // driver for u-boot does not support the EEPROM. Use this nvmem-type property to select the correct
* // nvmem device:
* linux,nvmem-type = "Battery backed"
* };
*
* The underlying RTC device should expose an nvmem provider in Linux
* which results in a sysfs file:
* /sys/bus/nvmem/devices/<device name>/nvmem
* We store the bootcount (magic + value) at the specified offset.
*/

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <sys/types.h>

#include "constants.h"
#include "dm_eeprom.h"
#include "dt.h"

#define NVMEM_SYSFS_DEVICES "/sys/bus/nvmem/devices"
#define RTC_MAGIC 0xbc

/* Cached discovery state */
static bool g_inited = false;
static char g_rtc_nvmem_path[128];
static off_t g_offset = 0;

static bool discover_dm_rtc(void)
{
if (g_inited)
return true;
DEBUG_PRINTF("Discovering DM RTC bootcount device...\n");

char bc_node[PATH_MAX];
if(!dt_get_chosen_bootcount_node("u-boot,bootcount-rtc", bc_node, sizeof(bc_node))) {
return false;
}
DEBUG_PRINTF(" Found bootcount node %s\n", bc_node);

// if there is a `linux,nvmem-type` property, use it later to select the correct nvmem device:
char nvmem_type[64];
int nvmem_type_len = dt_node_read_str(bc_node, "linux,nvmem-type", nvmem_type, sizeof(nvmem_type));
if (nvmem_type_len > 0) {
DEBUG_PRINTF(" found linux,nvmem-type '%s'\n", nvmem_type);
}

uint32_t offset = 0;
dt_node_read_u32(bc_node, "offset", &offset); /* ignore failure => 0 */
g_offset = (off_t)offset;

// if there is a `linux,nvmem-offset` property, use it instead of `offset`:
uint32_t linux_nvram_offset = 0;
if (dt_node_read_u32(bc_node, "linux,nvmem-offset", &linux_nvram_offset)) {
g_offset = (off_t)linux_nvram_offset;
DEBUG_PRINTF(" found linux,nvmem-offset 0x%lx\n", (unsigned long)g_offset);
}
DEBUG_PRINTF(" using offset 0x%lx\n", (unsigned long)g_offset);

/* rtc phandle */
uint32_t rtc_phandle;
if (!dt_node_read_u32(bc_node, "rtc", &rtc_phandle))
return false;
DEBUG_PRINTF(" rtc phandle %u\n", rtc_phandle);

char rtc_device_path[PATH_MAX];
if (!dt_find_phandle_node(rtc_phandle, rtc_device_path, sizeof(rtc_device_path)))
return false;
DEBUG_PRINTF(" rtc node %s\n", rtc_device_path);

/* Iterate nvmem devices to find matching of_node */
DIR *dev_dir = opendir(NVMEM_SYSFS_DEVICES);
if (!dev_dir)
return false;
struct dirent *de;
bool matched = false;
while ((de = readdir(dev_dir))) {
if (de->d_name[0] == '.')
continue; /* skip dot entries */

// construct the base path to the nvmem device: /sys/bus/nvmem/devices/<device name>
char nvmem_path[PATH_MAX];
if (snprintf(nvmem_path, sizeof(nvmem_path), NVMEM_SYSFS_DEVICES "/%s", de->d_name) >= (int)sizeof(nvmem_path)) {
DEBUG_PRINTF(" ERROR Path truncated constructing nvmem path\n");
continue;
}

// find the device under /sys/bus/nvmem/devices whose of_node symlink
// matches the rtc_device_path we resolved above:
char link_path[PATH_MAX];
if (snprintf(link_path, sizeof(link_path), "%s/of_node", nvmem_path) >= (int)sizeof(link_path)) {
DEBUG_PRINTF(" ERROR Path truncated constructing of_node path\n");
continue;
}
if (!same_fs_node(link_path, rtc_device_path)) {
continue;
}
DEBUG_PRINTF(" Matched device %s\n", link_path);

// if the dt definition included a `linux,nvmem-type` property, check it matches the nvmem "type"
// example: /sys/bus/nvmem/devices/rv3028_nvram0/type -> "Battery backed"
if (nvmem_type_len > 0) {
char dev_nvmem_type[64];
int dev_nvmem_type_len = dt_node_read_str(nvmem_path, "type", dev_nvmem_type, sizeof(dev_nvmem_type));
// trim the trailing newline if present:
if (dev_nvmem_type_len > 0 && dev_nvmem_type[dev_nvmem_type_len - 1] == '\n') {
dev_nvmem_type[--dev_nvmem_type_len] = '\0';
}
if (dev_nvmem_type_len <= 0 || strcmp(dev_nvmem_type, nvmem_type) != 0) {
DEBUG_PRINTF(" %s/type '%s' does not match expected '%s', continuing...\n", nvmem_path, dev_nvmem_type, nvmem_type);
continue;
}
DEBUG_PRINTF(" matched nvmem-type '%s'\n", dev_nvmem_type);
}

// ensure the device has an nvmem file:
if (snprintf(g_rtc_nvmem_path, sizeof(g_rtc_nvmem_path), "%s/nvmem", nvmem_path) >= (int)sizeof(g_rtc_nvmem_path)) {
DEBUG_PRINTF(" ERROR path too long: %s/nvmem\n" , nvmem_path);
continue;
}
struct stat sb;
if (stat(g_rtc_nvmem_path, &sb) != 0) {
DEBUG_PRINTF(" WARN nvmem path %s does not exist, continuing...\n", g_rtc_nvmem_path);
continue;
}
/* Ensure the nvmem file has sufficient size for the requested offset:
* we need 2 bytes at offset (magic + bootcount).
*/
if (sb.st_size < (off_t)(g_offset + 2)) {
DEBUG_PRINTF(" ERROR nvmem size %ld too small for offset 0x%lx\n", (long)sb.st_size, (unsigned long)g_offset);
continue;
}

matched = true;
DEBUG_PRINTF(" Chose RTC nvmem %s\n", g_rtc_nvmem_path);
break;
}
closedir(dev_dir);
if (!matched)
return false;

g_inited = true;
return true;
}

bool dm_rtc_exists(void)
{
return discover_dm_rtc();
}

int dm_rtc_read_bootcount(uint16_t *val)
{
if (!dm_rtc_exists())
return E_DEVICE;
return dm_eeprom_read_path(g_rtc_nvmem_path, g_offset, RTC_MAGIC, val);
}

int dm_rtc_write_bootcount(uint16_t val)
{
if (!dm_rtc_exists())
return E_DEVICE;
return dm_eeprom_write_path(g_rtc_nvmem_path, g_offset, RTC_MAGIC, val);
}

7 changes: 7 additions & 0 deletions src/dm_rtc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#pragma once

#define DM_RTC_NAME "DM RTC NVMEM"

bool dm_rtc_exists(void);
int dm_rtc_read_bootcount(uint16_t *val);
int dm_rtc_write_bootcount(uint16_t val);
Loading