Skip to content
Permalink
Browse files
drivers, misc: add U-Boot bootcount driver
This driver implements the Linux kernel half of the boot count feature -
the boot counter can only be reset after it is clear that the
application has been started and is running correctly, which usually
can only be determined by the application code itself. Thus the reset
of the boot counter must be done by application code, which thus needs
an appropriate driver.

Required feature by the Carrier Grade Linux Requirements Definition;
see for example document "Carrier Grade Linux Requirements Definition
Overview V3.0" at

http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection

        Description: OSDL CGL specifies that carrier grade Linux
        shall provide support for detecting a repeating reboot cycle
	due to recurring failures. This detection should happen in
	user space before system services are started.

This driver provides read/write access to the U-Boot bootcounter
through sysFS file.

Currently memory type only supported.

The bootcountregister gets configured via DTS.
for example on the enbw_cmc board:

bootcount@0x23060 {
                  compatible = "uboot,bootcount";
                  reg = <0x23060 0x1>;
                 };

original post from:
http://lkml.indiana.edu/hypermail/linux/kernel/1112.0/01142.html

Change-Id: Icc050a10743fd7364f3c64546ca3c5a39512ca9f
Signed-off-by: Heiko Schocher <hs@denx.de>
Cc: Wolfgang Denk <wd@denx.de>
Cc: Vitaly Bordug <vbordug@ru.mvista.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-kernel@vger.kernel.org
Cc: Steffen Rumler <steffen.rumler@siemens.com>
Cc: Matthias Kaehlcke <matthias@kaehlcke.net>
Cc: Paul Bolle <pebolle@tiscali.nl>
Cc: Wolfram Sang <w.sang@pengutronix.de>
Cc: Ryan Mallon <rmallon@gmail.com>
Signed-off-by: Heiko Schocher <hs@denx.de>
Signed-off-by: Tom Rini <trini@ti.com>
Signed-off-by: Eric Nelson <eric@nelint.com>
Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
  • Loading branch information
hsdenx authored and zandrey committed Nov 2, 2021
1 parent f5aa50c commit 88f59122f76cf1f1a59b770abf44962f95ebfd95
Show file tree
Hide file tree
Showing 5 changed files with 232 additions and 0 deletions.
@@ -0,0 +1,11 @@
What: /sys/class/misc/uboot-bootcount/uboot-bootcount
Date: Dezember 2011
Contact: Heiko Schocher <hs@denx.de>
Description:
This driver implements the Linux kernel half of the
U-Boot bootcount feature - the boot counter can only
be reset after it is clear that the application has
been started and is running correctly, which usually
can only be determined by the application code itself.
Thus the reset of the boot counter must be done by
application code, which thus needs an appropriate driver.
@@ -0,0 +1,40 @@
U-Boot bootcount driver

This driver implements the Linux kernel half of the boot count feature -
the boot counter can only be reset after it is clear that the
application has been started and is running correctly, which usually
can only be determined by the application code itself. Thus the reset
of the boot counter must be done by application code, which thus needs
an appropriate driver.

The length property here determines if a single address is being used for
both the magic value and count, or if they are in adjacent locations.
Within U-Boot either is supported, so this information must be passed.

Required feature by the Carrier Grade Linux Requirements Definition;
see for example document "Carrier Grade Linux Requirements Definition
Overview V3.0" at

http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection

Description: OSDL CGL specifies that carrier grade Linux
shall provide support for detecting a repeating reboot cycle
due to recurring failures. This detection should happen in
user space before system services are started.

This driver provides read/write access to the U-Boot bootcounter
through sysFS file.

Currently memory type only supported.

Required properties:

- compatible : should be "uboot,bootcount"
- reg: the address of the bootcounter

Example:

bootcount@1c23000 {
compatible = "uboot,bootcount";
reg = <0x23060 0x2>;
};
@@ -470,6 +470,13 @@ config HISI_HIKEY_USB
switching between the dual-role USB-C port and the USB-A host ports
using only one USB controller.

config UBOOT_BOOTCOUNT
tristate "U-Boot Bootcount driver"
depends on OF
help
The U-Boot Bootcount driver allows to access the
bootcounter through sysFS file.

source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
@@ -39,6 +39,7 @@ obj-$(CONFIG_VMWARE_BALLOON) += vmw_balloon.o
obj-$(CONFIG_PCH_PHUB) += pch_phub.o
obj-y += ti-st/
obj-y += lis3lv02d/
obj-$(CONFIG_UBOOT_BOOTCOUNT) += uboot_bootcount.o
obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/
obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
@@ -0,0 +1,173 @@
/*
* This driver gives access(read/write) to the bootcounter used by u-boot.
* Access is supported via sysfs.
*
* Copyright 2008 DENX Software Engineering GmbH
* Author: Heiko Schocher <hs@denx.de>
* Based on work from: Steffen Rumler <Steffen.Rumler@siemens.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/

#include <linux/fs.h>
#include <linux/io.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>

#define UBOOT_BOOTCOUNT_NAME "bootcount"

#define UBOOT_BOOTCOUNT_MAGIC_OFFSET 0x04 /* offset of magic number */
#define UBOOT_BOOTCOUNT_MAGIC 0xB001C041 /* magic number value */
#define UBOOT_BOOTCOUNT_MAGIC_MASK 0xFFFF0000 /* magic, when combined */
#define UBOOT_BOOTCOUNT_COUNT_MASK 0x0000FFFF /* value, when combined */

static void __iomem *mem;
/* Default to magic number being adjacent. */
static bool single_register = false;

/* Helper for the sysfs */
static int show_str_bootcount(struct device *device,
struct device_attribute *attr,
char *buf)
{
unsigned long counter;

counter = be32_to_cpu(readl(mem));

if (single_register)
counter &= UBOOT_BOOTCOUNT_COUNT_MASK;

return sprintf(buf, "%lu\n", counter);
}

static int store_str_bootcount(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t count)
{
int r;
u32 value;

r = kstrtou32(buf, 0, &value);
if (r < 0)
return -EINVAL;

if (single_register)
value = (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK) |
(value & UBOOT_BOOTCOUNT_COUNT_MASK);

writel(cpu_to_be32(value), mem);

return count;
}

static DEVICE_ATTR(bootcount, S_IWUSR | S_IRUGO, show_str_bootcount,
store_str_bootcount);

static const struct file_operations bootcount_fops = {
.owner = THIS_MODULE,
};

static struct miscdevice bootcount_miscdev = {
MISC_DYNAMIC_MINOR,
UBOOT_BOOTCOUNT_NAME,
&bootcount_fops
};


static int bootcount_probe(struct platform_device *ofdev)
{
struct device_node *np = of_node_get(ofdev->dev.of_node);
unsigned long magic;

if (resource_size(ofdev->resource) == 1)
single_register = true;

mem = of_iomap(np, 0);
if (mem == NULL) {
dev_err(&ofdev->dev, "couldnt map register.\n");
return -ENODEV;
}

if (single_register) {
magic = be32_to_cpu(readl(mem));
if ((magic & UBOOT_BOOTCOUNT_MAGIC_MASK) !=
(UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) {
dev_err(&ofdev->dev, "bad magic.\n");
goto no_magic;
}
} else {
magic = be32_to_cpu(readl(mem + UBOOT_BOOTCOUNT_MAGIC_OFFSET));
if (magic != UBOOT_BOOTCOUNT_MAGIC) {
dev_err(&ofdev->dev, "bad magic.\n");
goto no_magic;
}
}

if (misc_register(&bootcount_miscdev)) {
dev_err(&ofdev->dev, "failed to register device\n");
goto misc_register_fail;
}

if (device_create_file(bootcount_miscdev.this_device,
&dev_attr_bootcount)) {
dev_warn(&ofdev->dev, "couldn't register sysfs entry.\n");
goto register_sysfs_fail;
}

return 0;
register_sysfs_fail:
misc_deregister(&bootcount_miscdev);
misc_register_fail:
no_magic:
iounmap(mem);
return -ENODEV;
}

static int bootcount_remove(struct platform_device *ofdev)
{
misc_deregister(&bootcount_miscdev);
iounmap(mem);
return 0;
}

static const struct of_device_id bootcount_match[] = {
{
.compatible = "uboot,bootcount",
},
{},
};
MODULE_DEVICE_TABLE(of, bootcount_match);

static struct platform_driver bootcount_driver = {
.driver = {
.name = UBOOT_BOOTCOUNT_NAME,
.of_match_table = bootcount_match,
.owner = THIS_MODULE,
},
.probe = bootcount_probe,
.remove = bootcount_remove,
};

static int __init uboot_bootcount_init(void)
{
return platform_driver_register(&bootcount_driver);
}

static void __exit uboot_bootcount_cleanup(void)
{
platform_driver_unregister(&bootcount_driver);
}

late_initcall(uboot_bootcount_init);
module_exit(uboot_bootcount_cleanup);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Steffen Rumler <steffen.rumler@siemens.com>");
MODULE_DESCRIPTION("Provide (read/write) access to the U-Boot bootcounter via sysfs");

0 comments on commit 88f5912

Please sign in to comment.