iBoot/lib/paniclog/paniclog.c

180 lines
5.0 KiB
C

/*
* Copyright (c) 2010, 2013-2015 Apple Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <stdlib.h>
#include <list.h>
#include <lib/mib.h>
#include <platform.h> /* for platform_cache_operation() */
#include <lib/cksum.h>
#include <lib/nvram.h>
#include <lib/blockdev.h>
/* If the panic ram area is large enough, use copies to improve
* resilience against errors. */
#define PANIC_MIN_SIZE_FOR_COPIES (16 * 1024)
/* The number of copies (including the original) to make of the entire
* panic log, including headers, if the area is large enough. This
* provides a minimum of 2664 (after 8->7 bit packing and headers)
* bytes of contained log data. */
#define PANIC_COPIES 7
#define PANIC_BDEV_NAME "paniclog"
struct vram_panic {
uint32_t magic;
uint32_t crc;
char buf[];
};
void clear_panic_region(unsigned char pattern)
{
void *panicBufferAddr = (void *)mib_get_addr(kMIBTargetPanicBufferAddress);
size_t panicBufferSize = mib_get_size(kMIBTargetPanicBufferSize);
/* clear the panic area to a pattern. values other than 0 useful for debug */
memset(panicBufferAddr, pattern, panicBufferSize);
/* ensure this reaches DRAM */
#if defined(__arm64__)
/* on coherent archs (H6+), force a cache clean by adding invalidate flag, and specifying address and size */
platform_cache_operation(CACHE_CLEAN | CACHE_INVALIDATE | CACHE_DRAIN_WRITE_BUFFER, panicBufferAddr, panicBufferSize);
#else
platform_cache_operation(CACHE_CLEAN | CACHE_DRAIN_WRITE_BUFFER, NULL, 0);
#endif
}
// arm64 targets only have the new panic log format.
// armv7? targets may have either.
int save_panic_log(void)
{
struct vram_panic *vpi = NULL;
void *panic_buf = NULL;
uint32_t panic_text_len;
addr_t panicBufferAddr = mib_get_addr(kMIBTargetPanicBufferAddress);
size_t panicBufferSize = mib_get_size(kMIBTargetPanicBufferSize);
#if !defined(__arm64__)
if (mib_get_bool(kMIBTargetWithLegacyPanicLogs)) {
struct {
unsigned a:7;
unsigned b:7;
unsigned c:7;
unsigned d:7;
unsigned e:7;
unsigned f:7;
unsigned g:7;
unsigned h:7;
} __attribute__((packed)) pack;
uint32_t panic_size;
int i, panic_copies;
if (panicBufferSize >= PANIC_MIN_SIZE_FOR_COPIES) {
panic_size = panicBufferSize / PANIC_COPIES;
panic_copies = PANIC_COPIES;
} else {
panic_size = panicBufferSize;
panic_copies = 1;
}
panic_text_len = panic_size - 8;
/* find an intact copy */
for (i = 0; i < panic_copies; ++i) {
vpi = (struct vram_panic *) (panicBufferAddr + i * panic_size);
/* check for magic header */
if (vpi->magic != 0x4F4F5053 /* 'OOPS' */) {
continue;
}
/* check CRC */
if (vpi->crc != crc32((unsigned char *) vpi->buf, panic_text_len)) {
continue;
}
/* this copy is very likely intact */
panic_buf = vpi->buf;
break;
}
/* failed to find an intact copy? */
if (panic_buf == NULL) {
/* Don't attempt correction. This method should have
* been resilient enough that at least one copy
* survived. Log this CRC error. */
pack.a = 0x43; /* 'C' */
pack.b = 0x52; /* 'R' */
pack.c = 0x43; /* 'C' */
pack.d = 0x20; /* ' ' */
pack.e = 0x45; /* 'E' */
pack.f = 0x52; /* 'R' */
pack.g = 0x52; /* 'R' */
pack.h = 0x21; /* '!' */
panic_buf = &pack;
panic_text_len = sizeof(pack);
}
/* write the panic to the nvram partition */
if (nvram_set_panic(panic_buf, panic_text_len) < 0) {
clear_panic_region(0xe1);
return -1;
}
/* wipe the area so we don't produce duplicate logs */
clear_panic_region(0);
/* save nvram */
if (nvram_save() < 0) {
clear_panic_region(0xe2);
return -1;
}
return 0;
}
#endif // !defined(__arm64__)
int err;
struct blockdev *dev;
const char *crc_err = "CRC ERR!";
ASSERT(!mib_get_bool(kMIBTargetWithLegacyPanicLogs));
panic_text_len = panicBufferSize - 8;
vpi = (struct vram_panic *) panicBufferAddr;
/* check for magic header 'DARN'/'SICK' and CRC value */
if ((vpi->magic == 0x4441524E || vpi->magic == 0x5349434B) &&
vpi->crc == crc32((unsigned char *) vpi->buf, panic_text_len)) {
/* this copy is very likely intact */
panic_buf = vpi->buf;
} else {
/* failed to match DARN magic header or CRC */
/* Don't attempt correction. Log this CRC error. */
panic_buf = (void*)crc_err;
panic_text_len = sizeof(*crc_err);
}
dev = lookup_blockdev(PANIC_BDEV_NAME);
if (!dev) {
dprintf(DEBUG_CRITICAL, "Couldn't find block device '%s'\n", PANIC_BDEV_NAME);
clear_panic_region(0xe1);
return -1;
}
err = blockdev_write(dev, (void *)panic_buf, 0, panic_text_len);
if (err <= 0) {
dprintf(DEBUG_CRITICAL, "blockdev_write to '%s' device failed with return value %d\n",
PANIC_BDEV_NAME, err);
clear_panic_region(0xe2);
return -1;
}
/* wipe the area so we don't produce duplicate logs */
clear_panic_region(0);
return 0;
}