summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/drivers/elog/Kconfig17
-rw-r--r--src/drivers/elog/Makefile.inc3
-rw-r--r--src/drivers/elog/boot_count.c122
-rw-r--r--src/drivers/elog/elog.c6
-rw-r--r--src/include/elog.h5
-rw-r--r--src/northbridge/intel/sandybridge/early_init.c8
6 files changed, 161 insertions, 0 deletions
diff --git a/src/drivers/elog/Kconfig b/src/drivers/elog/Kconfig
index d61dd10857..867416cc77 100644
--- a/src/drivers/elog/Kconfig
+++ b/src/drivers/elog/Kconfig
@@ -67,3 +67,20 @@ config ELOG_SHRINK_SIZE
Default is 1K.
endif
+
+config ELOG_BOOT_COUNT
+ depends on ELOG
+ bool "Maintain a monotonic boot number in CMOS"
+ default n
+ help
+ Store a monotonic boot number in CMOS and provide an interface
+ to read the current value and increment the counter. This boot
+ counter will be logged as part of the System Boot event.
+
+config ELOG_BOOT_COUNT_CMOS_OFFSET
+ depends on ELOG && ELOG_BOOT_COUNT && !USE_OPTION_TABLE
+ int "Offset in CMOS to store the boot count"
+ default 0
+ help
+ This value must be greater than 16 bytes so as not to interfere
+ with the standard RTC region. Requires 8 bytes.
diff --git a/src/drivers/elog/Makefile.inc b/src/drivers/elog/Makefile.inc
index a01841db96..32509c8ce0 100644
--- a/src/drivers/elog/Makefile.inc
+++ b/src/drivers/elog/Makefile.inc
@@ -1 +1,4 @@
ramstage-$(CONFIG_ELOG) += elog.c
+
+romstage-$(CONFIG_ELOG_BOOT_COUNT) += boot_count.c
+ramstage-$(CONFIG_ELOG_BOOT_COUNT) += boot_count.c
diff --git a/src/drivers/elog/boot_count.c b/src/drivers/elog/boot_count.c
new file mode 100644
index 0000000000..9ea828d04d
--- /dev/null
+++ b/src/drivers/elog/boot_count.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2012 The ChromiumOS Authors. All rights reserved.
+ *
+ * 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; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#include <console/console.h>
+#include <ip_checksum.h>
+#include <pc80/mc146818rtc.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <elog.h>
+
+/*
+ * We need a region in CMOS to store the boot counter.
+ *
+ * This can either be declared as part of the option
+ * table or statically defined in the board config.
+ */
+#if CONFIG_USE_OPTION_TABLE
+# include "option_table.h"
+# define BOOT_COUNT_CMOS_OFFSET (CMOS_VSTART_boot_count_offset >> 3)
+#else
+# if defined(CONFIG_ELOG_BOOT_COUNT_CMOS_OFFSET)
+# define BOOT_COUNT_CMOS_OFFSET CONFIG_ELOG_BOOT_COUNT_CMOS_OFFSET
+# else
+# error "Must define CONFIG_ELOG_BOOT_COUNT_CMOS_OFFSET"
+# endif
+#endif
+
+#define BOOT_COUNT_SIGNATURE 0x4342 /* 'BC' */
+
+struct boot_count {
+ u16 signature;
+ u32 count;
+ u16 checksum;
+} __attribute__ ((packed));
+
+/* Read and validate boot count structure from CMOS */
+static int boot_count_cmos_read(struct boot_count *bc)
+{
+ u8 i, *p;
+ u16 csum;
+
+ for (p = (u8*)bc, i = 0; i < sizeof(*bc); i++, p++)
+ *p = cmos_read(BOOT_COUNT_CMOS_OFFSET + i);
+
+ /* Verify signature */
+ if (bc->signature != BOOT_COUNT_SIGNATURE) {
+ printk(BIOS_DEBUG, "Boot Count invalid signature\n");
+ return -1;
+ }
+
+ /* Verify checksum over signature and counter only */
+ csum = compute_ip_checksum(bc, offsetof(struct boot_count, checksum));
+
+ if (csum != bc->checksum) {
+ printk(BIOS_DEBUG, "Boot Count checksum mismatch\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Write boot count structure to CMOS */
+static void boot_count_cmos_write(struct boot_count *bc)
+{
+ u8 i, *p;
+
+ /* Checksum over signature and counter only */
+ bc->checksum = compute_ip_checksum(
+ bc, offsetof(struct boot_count, checksum));
+
+ for (p = (u8*)bc, i = 0; i < sizeof(*bc); i++, p++)
+ cmos_write(*p, BOOT_COUNT_CMOS_OFFSET + i);
+}
+
+/* Increment boot count and return the new value */
+u32 boot_count_increment(void)
+{
+ struct boot_count bc;
+
+ /* Read and increment boot count */
+ if (boot_count_cmos_read(&bc) < 0) {
+ /* Structure invalid, re-initialize */
+ bc.signature = BOOT_COUNT_SIGNATURE;
+ bc.count = 0;
+ }
+
+ /* Increment boot counter */
+ bc.count++;
+
+ /* Write the new count to CMOS */
+ boot_count_cmos_write(&bc);
+
+ printk(BIOS_DEBUG, "Boot Count incremented to %u\n", bc.count);
+ return bc.count;
+}
+
+/* Return the current boot count */
+u32 boot_count_read(void)
+{
+ struct boot_count bc;
+
+ if (boot_count_cmos_read(&bc) < 0)
+ return 0;
+
+ return bc.count;
+}
diff --git a/src/drivers/elog/elog.c b/src/drivers/elog/elog.c
index 9c0fa930ad..1219883907 100644
--- a/src/drivers/elog/elog.c
+++ b/src/drivers/elog/elog.c
@@ -780,6 +780,12 @@ int elog_init(void)
elog_add_event_word(ELOG_TYPE_LOG_CLEAR,
elog_get_flash()->total_size);
+#if CONFIG_ELOG_BOOT_COUNT && !defined(__SMM__)
+ /* Log boot count event except in S3 resume */
+ if (acpi_slp_type != 3)
+ elog_add_event_dword(ELOG_TYPE_BOOT, boot_count_read());
+#endif
+
return 0;
}
diff --git a/src/include/elog.h b/src/include/elog.h
index 897a90bf41..0542f68fe1 100644
--- a/src/include/elog.h
+++ b/src/include/elog.h
@@ -115,6 +115,11 @@ extern void elog_add_event_dword(u8 event_type, u32 data);
extern void elog_add_event_wake(u8 source, u32 instance);
extern int elog_smbios_write_type15(unsigned long *current, int handle);
+#if CONFIG_ELOG_BOOT_COUNT
+u32 boot_count_read(void);
+u32 boot_count_increment(void);
+#endif
+
#endif /* !CONFIG_ELOG */
#endif /* ELOG_H_ */
diff --git a/src/northbridge/intel/sandybridge/early_init.c b/src/northbridge/intel/sandybridge/early_init.c
index ff20c44833..b060831613 100644
--- a/src/northbridge/intel/sandybridge/early_init.c
+++ b/src/northbridge/intel/sandybridge/early_init.c
@@ -24,6 +24,7 @@
#include <arch/io.h>
#include <arch/romcc_io.h>
#include <device/pci_def.h>
+#include <elog.h>
#include "sandybridge.h"
#include "pcie_config.c"
@@ -63,6 +64,13 @@ static void sandybridge_setup_bars(void)
pci_write_config8(PCI_DEV(0, 0x00, 0), PAM5, 0x33);
pci_write_config8(PCI_DEV(0, 0x00, 0), PAM6, 0x33);
+#if CONFIG_ELOG_BOOT_COUNT
+ /* Increment Boot Counter for non-S3 resume */
+ if ((inw(DEFAULT_PMBASE + PM1_STS) & WAK_STS) &&
+ ((inl(DEFAULT_PMBASE + PM1_CNT) >> 10) & 7) != SLP_TYP_S3)
+ boot_count_increment();
+#endif
+
printk(BIOS_DEBUG, " done.\n");
#if CONFIG_ELOG_BOOT_COUNT