Raul Rangel has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/42890 )
Change subject: WIP: Hash RAM contents on S3 entry and verify on S3 resume ......................................................................
WIP: Hash RAM contents on S3 entry and verify on S3 resume
I used murmur3 since it's supposed to be fast. Though I don't think the license is really compatible.
The ram_hash code still needs some refactoring to reduce duplication and to add the ability to handle unaligned blocks.
BUG=b:159081993 TEST=Boot trembyle and see no verification failures.
Signed-off-by: Raul E Rangel rrangel@chromium.org Change-Id: I6b38facba17bdd57a94987cfb49e71067ff8b769 --- M Makefile.inc M src/arch/x86/acpi_s3.c A src/include/murmur3.h A src/include/ram_hash.h M src/lib/Makefile.inc A src/lib/murmur3.c A src/lib/ram_hash.c M src/soc/amd/picasso/chip.c M src/soc/amd/picasso/memlayout.ld M src/soc/amd/picasso/smihandler.c 10 files changed, 595 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/90/42890/1
diff --git a/Makefile.inc b/Makefile.inc index 86335d9..b7020e8 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -410,8 +410,8 @@ CFLAGS_common += -Wwrite-strings -Wredundant-decls -Wno-trigraphs -Wimplicit-fallthrough CFLAGS_common += -Wstrict-aliasing -Wshadow -Wdate-time -Wtype-limits -Wvla CFLAGS_common += -Wlogical-op -Wduplicated-cond -Wdangling-else -CFLAGS_common += -fno-common -ffreestanding -fno-builtin -fomit-frame-pointer -CFLAGS_common += -ffunction-sections -fdata-sections -fno-pie +CFLAGS_common += -fno-common -ffreestanding -fno-builtin -fmit-frame-pointer +CFLAGS_common += -ffunction-sections -fdata-sections -fno-pie -Wno-implicit-fallthrough ifeq ($(CONFIG_COMPILER_GCC),y) # Don't add these GCC specific flags when running scan-build ifeq ($(CCC_ANALYZER_OUTPUT_FORMAT),) diff --git a/src/arch/x86/acpi_s3.c b/src/arch/x86/acpi_s3.c index d4c697e..9c209b2 100644 --- a/src/arch/x86/acpi_s3.c +++ b/src/arch/x86/acpi_s3.c @@ -9,6 +9,7 @@ #include <cpu/x86/smm.h> #include <fallback.h> #include <timestamp.h> +#include <ram_hash.h> #include <romstage_handoff.h>
#if ENV_RAMSTAGE || ENV_POSTCAR @@ -70,6 +71,8 @@
timestamp_add_now(TS_ACPI_WAKE_JUMP);
+ verify_ram(); + acpi_do_wakeup((uintptr_t)vector); }
diff --git a/src/include/murmur3.h b/src/include/murmur3.h new file mode 100644 index 0000000..138f87f --- /dev/null +++ b/src/include/murmur3.h @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the +// public domain. The author hereby disclaims copyright to this source +// code. + +#ifndef _MURMURHASH3_H_ +#define _MURMURHASH3_H_ + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 (const void *key, int len, uint32_t seed, uint32_t *out); + +void MurmurHash3_x86_128(const void *key, int len, uint32_t seed, void *out); + +void MurmurHash3_x64_128(const void *key, int len, uint32_t seed, void *out); + +//----------------------------------------------------------------------------- + +#ifdef __cplusplus +} +#endif + +#endif // _MURMURHASH3_H_ diff --git a/src/include/ram_hash.h b/src/include/ram_hash.h new file mode 100644 index 0000000..67eaa76 --- /dev/null +++ b/src/include/ram_hash.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef RAM_HASH_H +#define RAM_HASH_H + +void hash_ram(void); +void verify_ram(void); + +#endif /* RAM_HASH_H */ diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index e0003bd..5bf0df2 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -145,6 +145,10 @@ ramstage-y += imd_cbmem.c ramstage-y += imd.c
+bootblock-y += ram_hash.c crc32.c murmur3.c +ramstage-y += ram_hash.c crc32.c murmur3.c +smm-y += ram_hash.c crc32.c murmur3.c + postcar-$(CONFIG_VENDOR_EMULATION) += ramdetect.c postcar-y += cbmem_common.c postcar-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c diff --git a/src/lib/murmur3.c b/src/lib/murmur3.c new file mode 100644 index 0000000..f2b6b18 --- /dev/null +++ b/src/lib/murmur3.c @@ -0,0 +1,314 @@ +//----------------------------------------------------------------------------- +// MurmurHash3 was written by Austin Appleby, and is placed in the public +// domain. The author hereby disclaims copyright to this source code. + +// Note - The x86 and x64 versions do _not_ produce the same results, as the +// algorithms are optimized for their respective platforms. You can still +// compile and run any of them on any platform, but your performance with the +// non-native version will be less than optimal. + +#include "murmur3.h" + +//----------------------------------------------------------------------------- +// Platform-specific functions and macros + +#ifdef __GNUC__ +#define FORCE_INLINE __attribute__((always_inline)) inline +#else +#define FORCE_INLINE inline +#endif + +static FORCE_INLINE uint32_t rotl32 ( uint32_t x, int8_t r ) +{ + return (x << r) | (x >> (32 - r)); +} + +static FORCE_INLINE uint64_t rotl64 ( uint64_t x, int8_t r ) +{ + return (x << r) | (x >> (64 - r)); +} + +#define ROTL32(x,y) rotl32(x,y) +#define ROTL64(x,y) rotl64(x,y) + +#define BIG_CONSTANT(x) (x##LLU) + +//----------------------------------------------------------------------------- +// Block read - if your platform needs to do endian-swapping or can only +// handle aligned reads, do the conversion here + +#define getblock(p, i) (p[i]) + +//----------------------------------------------------------------------------- +// Finalization mix - force all bits of a hash block to avalanche + +static FORCE_INLINE uint32_t fmix32 ( uint32_t h ) +{ + h ^= h >> 16; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + return h; +} + +//---------- + +static FORCE_INLINE uint64_t fmix64 ( uint64_t k ) +{ + k ^= k >> 33; + k *= BIG_CONSTANT(0xff51afd7ed558ccd); + k ^= k >> 33; + k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53); + k ^= k >> 33; + + return k; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_32 ( const void * key, int len, + uint32_t seed, uint32_t * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 4; + int i; + + uint32_t h1 = seed; + + uint32_t c1 = 0xcc9e2d51; + uint32_t c2 = 0x1b873593; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*4); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i); + + k1 *= c1; + k1 = ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*4); + + uint32_t k1 = 0; + + switch(len & 3) + { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: k1 ^= tail[0]; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; + + h1 = fmix32(h1); + + *(uint32_t*)out = h1; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x86_128 ( const void * key, const int len, + uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint32_t h1 = seed; + uint32_t h2 = seed; + uint32_t h3 = seed; + uint32_t h4 = seed; + + uint32_t c1 = 0x239b961b; + uint32_t c2 = 0xab0e9789; + uint32_t c3 = 0x38b34ae5; + uint32_t c4 = 0xa1e38b93; + + //---------- + // body + + const uint32_t * blocks = (const uint32_t *)(data + nblocks*16); + + for(i = -nblocks; i; i++) + { + uint32_t k1 = getblock(blocks,i*4+0); + uint32_t k2 = getblock(blocks,i*4+1); + uint32_t k3 = getblock(blocks,i*4+2); + uint32_t k4 = getblock(blocks,i*4+3); + + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + + h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b; + + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747; + + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35; + + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint32_t k1 = 0; + uint32_t k2 = 0; + uint32_t k3 = 0; + uint32_t k4 = 0; + + switch(len & 15) + { + case 15: k4 ^= tail[14] << 16; + case 14: k4 ^= tail[13] << 8; + case 13: k4 ^= tail[12] << 0; + k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4; + + case 12: k3 ^= tail[11] << 24; + case 11: k3 ^= tail[10] << 16; + case 10: k3 ^= tail[ 9] << 8; + case 9: k3 ^= tail[ 8] << 0; + k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3; + + case 8: k2 ^= tail[ 7] << 24; + case 7: k2 ^= tail[ 6] << 16; + case 6: k2 ^= tail[ 5] << 8; + case 5: k2 ^= tail[ 4] << 0; + k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2; + + case 4: k1 ^= tail[ 3] << 24; + case 3: k1 ^= tail[ 2] << 16; + case 2: k1 ^= tail[ 1] << 8; + case 1: k1 ^= tail[ 0] << 0; + k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len; + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + h1 = fmix32(h1); + h2 = fmix32(h2); + h3 = fmix32(h3); + h4 = fmix32(h4); + + h1 += h2; h1 += h3; h1 += h4; + h2 += h1; h3 += h1; h4 += h1; + + ((uint32_t*)out)[0] = h1; + ((uint32_t*)out)[1] = h2; + ((uint32_t*)out)[2] = h3; + ((uint32_t*)out)[3] = h4; +} + +//----------------------------------------------------------------------------- + +void MurmurHash3_x64_128 ( const void * key, const int len, + const uint32_t seed, void * out ) +{ + const uint8_t * data = (const uint8_t*)key; + const int nblocks = len / 16; + int i; + + uint64_t h1 = seed; + uint64_t h2 = seed; + + uint64_t c1 = BIG_CONSTANT(0x87c37b91114253d5); + uint64_t c2 = BIG_CONSTANT(0x4cf5ad432745937f); + + //---------- + // body + + const uint64_t * blocks = (const uint64_t *)(data); + + for(i = 0; i < nblocks; i++) + { + uint64_t k1 = getblock(blocks,i*2+0); + uint64_t k2 = getblock(blocks,i*2+1); + + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + + h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729; + + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5; + } + + //---------- + // tail + + const uint8_t * tail = (const uint8_t*)(data + nblocks*16); + + uint64_t k1 = 0; + uint64_t k2 = 0; + + switch(len & 15) + { + case 15: k2 ^= (uint64_t)(tail[14]) << 48; + case 14: k2 ^= (uint64_t)(tail[13]) << 40; + case 13: k2 ^= (uint64_t)(tail[12]) << 32; + case 12: k2 ^= (uint64_t)(tail[11]) << 24; + case 11: k2 ^= (uint64_t)(tail[10]) << 16; + case 10: k2 ^= (uint64_t)(tail[ 9]) << 8; + case 9: k2 ^= (uint64_t)(tail[ 8]) << 0; + k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2; + + case 8: k1 ^= (uint64_t)(tail[ 7]) << 56; + case 7: k1 ^= (uint64_t)(tail[ 6]) << 48; + case 6: k1 ^= (uint64_t)(tail[ 5]) << 40; + case 5: k1 ^= (uint64_t)(tail[ 4]) << 32; + case 4: k1 ^= (uint64_t)(tail[ 3]) << 24; + case 3: k1 ^= (uint64_t)(tail[ 2]) << 16; + case 2: k1 ^= (uint64_t)(tail[ 1]) << 8; + case 1: k1 ^= (uint64_t)(tail[ 0]) << 0; + k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1; + }; + + //---------- + // finalization + + h1 ^= len; h2 ^= len; + + h1 += h2; + h2 += h1; + + h1 = fmix64(h1); + h2 = fmix64(h2); + + h1 += h2; + h2 += h1; + + ((uint64_t*)out)[0] = h1; + ((uint64_t*)out)[1] = h2; +} + +//----------------------------------------------------------------------------- diff --git a/src/lib/ram_hash.c b/src/lib/ram_hash.c new file mode 100644 index 0000000..9f07183 --- /dev/null +++ b/src/lib/ram_hash.c @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include <stdint.h> +#include <murmur3.h> +#include <ram_hash.h> +#include <string.h> +#include <lib.h> +#include <console/console.h> +#include <commonlib/bsd/helpers.h> + +#define HASH_BUFFER 0x2BD000 +#define EHASH_BUFFER 0x3BD000 +// extern char HASH_BUFFER[]; +// extern char EHASH_BUFFER[]; + +struct hash_range { + uintptr_t start; + uintptr_t end; + unsigned int block_size; + unsigned int diff; +}; + +struct hash_entry { + uint32_t hash; + uint32_t data[]; +}; + +struct hash_range mem_layout[] = { + { + .start = 0x1000, + .end = 0xa0000, + .block_size = 1 * KiB, + .diff = 1 + }, + { + .start = 0x3bd000, + .end = 0x3c0000, + .block_size = 4 * KiB, + .diff = 0 + }, + { + .start = 0x3c0000, + .end = 0xcc4c0000, + .block_size = 256 * KiB, + .diff = 0 + }, + { + .start = 0xcc4c0000, + .end = 0xcc51c000, + .block_size = 4 * KiB, + .diff = 0 + }, +}; + +static struct hash_entry *hash_range(const struct hash_range *range, struct hash_entry *entry, + size_t buffer_size) +{ + unsigned int total_blocks = (range->end - range->start) / range->block_size; + void *current; + unsigned int i; + + for (i = 0; i < total_blocks; ++i) { + current = (void *)(range->start + i * range->block_size); + + if (!(i % 256)) + printk(BIOS_DEBUG, "%s: block %u/%u @ %p\n", __func__, i, total_blocks, + current); + + // printk(BIOS_ERR, "%s: buffer_size: %zd\n", __func__, buffer_size); + + if (buffer_size < sizeof(*entry)) { + printk(BIOS_ERR, "%s: Hash buffer is too small for entry\n", __func__); + return NULL; + } + + // printk(BIOS_ERR, "%s: current: %p\n", __func__, current); + // printk(BIOS_ERR, "%s: current_entry: %p\n", __func__, entry); + + /* Allocate one entry in the buffer */ + buffer_size -= sizeof(*entry); + + MurmurHash3_x86_32(current, range->block_size, 0xDEADBEEF, &entry->hash); + // printk(BIOS_ERR, "%s: crc: %#x\n", __func__, entry->hash); + + if (range->diff) { + if (buffer_size < range->block_size) { + printk(BIOS_DEBUG, "%s: Hash buffer is too small for data\n", + __func__); + return NULL; + } + + /* Allocate diff size in buffer */ + buffer_size -= range->block_size; + memcpy(entry->data, current, range->block_size); + + entry = (struct hash_entry *)((uintptr_t)(entry + 1) + + range->block_size); + } else { + entry++; + } + // printk(BIOS_ERR, "%s: next_entry: %p\n", __func__, entry); + } + + return entry; +} + +void hash_ram(void) +{ + struct hash_entry *current_entry = (struct hash_entry *)HASH_BUFFER; + struct hash_range *range; + unsigned int i; + + printk(BIOS_INFO, "%s: Start: HASH_BUFFER: %#x, EHASH_BUFFER: %#x\n", __func__, + HASH_BUFFER, EHASH_BUFFER); + + for (i = 0; i < ARRAY_SIZE(mem_layout); ++i) { + range = &mem_layout[i]; + printk(BIOS_DEBUG, "%s: Hashing range %p -> %p\n", __func__, + (void *)range->start, (void *)range->end); + current_entry = + hash_range(range, current_entry, + ((uintptr_t)EHASH_BUFFER - (uintptr_t)current_entry)); + + if (!current_entry) { + printk(BIOS_ERR, "%s: Hash buffer is too small\n", __func__); + break; + } + } + + printk(BIOS_INFO, "%s: Done\n", __func__); +} + +static struct hash_entry *verify_range(const struct hash_range *range, struct hash_entry *entry, + size_t buffer_size) +{ + unsigned int total_blocks = (range->end - range->start) / range->block_size; + void *current; + unsigned int i; + uint32_t crc; + + for (i = 0; i < total_blocks; ++i) { + current = (void *)(range->start + i * range->block_size); + + if (!(i % 256)) + printk(BIOS_DEBUG, "%s: block %u/%u @ %p\n", __func__, i, total_blocks, + current); + + // printk(BIOS_ERR, "%s: buffer_size: %zd\n", __func__, buffer_size); + + if (buffer_size < sizeof(*entry)) { + printk(BIOS_ERR, "%s: Hash buffer is too small for entry\n", __func__); + return NULL; + } + + // printk(BIOS_ERR, "%s: current: %p\n", __func__, current); + // printk(BIOS_ERR, "%s: current_entry: %p\n", __func__, entry); + + /* Allocate one entry in the buffer */ + buffer_size -= sizeof(*entry); + + MurmurHash3_x86_32(current, range->block_size, 0xDEADBEEF, &crc); + // printk(BIOS_ERR, "%s: crc: %#x\n", __func__, entry->hash); + + if (crc != entry->hash) { + printk(BIOS_ERR, "%s: crc error at %p. %x != %x\n", __func__, current, + crc, entry->hash); + } + + if (range->diff) { + if (buffer_size < range->block_size) { + printk(BIOS_DEBUG, "%s: Hash buffer is too small for data\n", + __func__); + return NULL; + } + + /* Allocate diff size in buffer */ + buffer_size -= range->block_size; + + if (crc != entry->hash) { + + printk(BIOS_ERR, "Expected:\n"); + hexdump(entry->data, range->block_size); + + printk(BIOS_ERR, "Actual:\n"); + hexdump(current, range->block_size); + } + + entry = (struct hash_entry *)((uintptr_t)(entry + 1) + + range->block_size); + } else { + entry++; + } + // printk(BIOS_ERR, "%s: next_entry: %p\n", __func__, entry); + } + + return entry; +} + +void verify_ram() +{ + struct hash_entry *current_entry = (struct hash_entry *)HASH_BUFFER; + struct hash_range *range; + unsigned int i; + + printk(BIOS_INFO, "%s: Start: HASH_BUFFER: %#x, EHASH_BUFFER: %#x\n", __func__, + HASH_BUFFER, EHASH_BUFFER); + + for (i = 0; i < ARRAY_SIZE(mem_layout); ++i) { + range = &mem_layout[i]; + printk(BIOS_DEBUG, "%s: Verifying range %p -> %p\n", __func__, + (void *)range->start, (void *)range->end); + current_entry = + verify_range(range, current_entry, + ((uintptr_t)EHASH_BUFFER - (uintptr_t)current_entry)); + + if (!current_entry) { + printk(BIOS_ERR, "%s: Hash buffer is too small\n", __func__); + break; + } + } + + printk(BIOS_INFO, "%s: Done\n", __func__); +} diff --git a/src/soc/amd/picasso/chip.c b/src/soc/amd/picasso/chip.c index 2abe54e..678ce03 100644 --- a/src/soc/amd/picasso/chip.c +++ b/src/soc/amd/picasso/chip.c @@ -14,6 +14,7 @@ #include <soc/southbridge.h> #include "chip.h" #include <fsp/api.h> +#include <ram_hash.h>
/* Supplied by i2c.c */ extern struct device_operations picasso_i2c_mmio_ops; @@ -184,6 +185,7 @@ static void picasso_os_entry(void *unused) { picasso_disable_paging(NULL); + verify_ram(); }
struct chip_operations soc_amd_picasso_ops = { diff --git a/src/soc/amd/picasso/memlayout.ld b/src/soc/amd/picasso/memlayout.ld index 27c72a2..a128019 100644 --- a/src/soc/amd/picasso/memlayout.ld +++ b/src/soc/amd/picasso/memlayout.ld @@ -92,6 +92,7 @@ REGION(pagetables, CONFIG_PAGE_TABLE_ADDR, 4096 * 12, 8) REGION(pdpt, ., 32, 32)
+ REGION(hash_buffer, 0x2BD000, 1M, 4) EARLY_RESERVED_DRAM_END(.)
RAMSTAGE(CONFIG_RAMBASE, 8M) diff --git a/src/soc/amd/picasso/smihandler.c b/src/soc/amd/picasso/smihandler.c index 992dc2b..616f116 100644 --- a/src/soc/amd/picasso/smihandler.c +++ b/src/soc/amd/picasso/smihandler.c @@ -3,6 +3,7 @@ #include <arch/io.h> #include <console/console.h> #include <cpu/x86/smm.h> +#include <ram_hash.h> #include <cpu/x86/cache.h> #include <cpu/amd/amd64_save_state.h> #include <acpi/acpi.h> @@ -144,6 +145,13 @@ printk(BIOS_SPEW, "SMI#: SLP = 0x%04x\n", pm1cnt); slp_typ = acpi_sleep_from_pm1(pm1cnt);
+ /* + * Hash RAM as soon as we know we are entering S3 to catch any problems + * caused by the SMM handler, PSP, or SMU. + */ + if (slp_typ == ACPI_S3) + hash_ram(); + /* Do any mainboard sleep handling */ mainboard_smi_sleep(slp_typ);