blob: 29513963fb621abdfba3ea931e2ccc3985380434 [file] [log] [blame]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <cstddef>
#include <base/containers/span.h>
#include <base/strings/strcat.h>
#include <base/strings/stringprintf.h>
#include "libec/ec_command.h"
#include "libec/ec_panicinfo.h"
namespace ec {
namespace {
static std::string PrintPanicReg(int regnum, const uint32_t* regs, int index) {
static const char* const regname[] = {
"r0 ", "r1 ", "r2 ", "r3 ", "r4 ", "r5 ", "r6 ", "r7 ",
"r8 ", "r9 ", "r10", "r11", "r12", "sp ", "lr ", "pc "};
std::string ret;
ret = base::StringPrintf("%s:", regname[regnum]);
if (regs)
base::StrAppend(&ret, {base::StringPrintf("%08x", regs[index])});
else
base::StrAppend(&ret, {base::StringPrintf(" ")});
base::StrAppend(&ret, {(regnum & 3) == 3 ? "\n" : " "});
return ret;
}
static std::string PanicShowExtraCm(const struct panic_data* pdata) {
enum {
CPU_NVIC_CFSR_BFARVALID = BIT(15),
CPU_NVIC_CFSR_MFARVALID = BIT(7),
};
std::string ret;
uint32_t bfar, mfar, cfsr, shcsr, hfsr, dfsr, ipsr;
if (pdata->struct_version == 1) {
bfar = pdata->cm_v1.bfar;
mfar = pdata->cm_v1.mfar;
cfsr = pdata->cm_v1.cfsr;
shcsr = pdata->cm_v1.shcsr;
hfsr = pdata->cm_v1.hfsr;
dfsr = pdata->cm_v1.dfsr;
ipsr = pdata->cm_v1.regs[CORTEX_PANIC_REGISTER_IPSR];
} else {
bfar = pdata->cm.bfar;
mfar = pdata->cm.mfar;
cfsr = pdata->cm.cfsr;
shcsr = pdata->cm.shcsr;
hfsr = pdata->cm.hfsr;
dfsr = pdata->cm.dfsr;
ipsr = pdata->cm.regs[CORTEX_PANIC_REGISTER_IPSR];
}
ret = base::StringPrintf("\n");
if (pdata->cm.cfsr & CPU_NVIC_CFSR_BFARVALID)
base::StrAppend(&ret, {base::StringPrintf("bfar=%08x, ", bfar)});
if (pdata->cm.cfsr & CPU_NVIC_CFSR_MFARVALID)
base::StrAppend(&ret, {base::StringPrintf("mfar=%08x, ", mfar)});
base::StrAppend(
&ret, {base::StringPrintf("cfsr=%08x, ", cfsr),
base::StringPrintf("shcsr=%08x, ", shcsr),
base::StringPrintf("hfsr=%08x, ", hfsr),
base::StringPrintf("dfsr=%08x, ", dfsr),
base::StringPrintf("ipsr=%08x", ipsr), base::StringPrintf("\n")});
return ret;
}
static std::string ParsePanicInfoCm(const struct panic_data* pdata) {
const uint32_t* lregs = pdata->cm.regs;
const uint32_t* sregs = nullptr;
enum { ORIG_UNKNOWN = 0, ORIG_PROCESS, ORIG_HANDLER } origin = ORIG_UNKNOWN;
int i;
const char* panic_origins[3] = {"", "PROCESS", "HANDLER"};
std::string ret;
ret = base::StringPrintf(
"Saved panic data:%s\n",
(pdata->flags & PANIC_DATA_FLAG_OLD_HOSTCMD ? "" : " (NEW)"));
if (pdata->struct_version == 2)
origin = ((lregs[11] & 0xf) == 1 || (lregs[11] & 0xf) == 9) ? ORIG_HANDLER
: ORIG_PROCESS;
/*
* In pdata struct, 'regs', which is allocated before 'frame', has
* one less elements in version 1. Choose the data according to the version.
*/
if (pdata->flags & PANIC_DATA_FLAG_FRAME_VALID)
sregs = pdata->struct_version == 1 ? pdata->cm_v1.frame : pdata->cm.frame;
base::StrAppend(&ret, {base::StringPrintf(
"=== %s EXCEPTION: %02x ====== xPSR: %08x ===\n",
panic_origins[origin], lregs[1] & 0xff,
sregs ? sregs[7] : -1)});
for (i = 0; i < 4; ++i)
base::StrAppend(&ret, {PrintPanicReg(i, sregs, i)});
for (i = 4; i < 10; ++i)
base::StrAppend(&ret, {PrintPanicReg(i, lregs, i - 1)});
base::StrAppend(&ret,
{PrintPanicReg(10, lregs, 9), PrintPanicReg(11, lregs, 10),
PrintPanicReg(12, sregs, 4),
PrintPanicReg(13, lregs, origin == ORIG_HANDLER ? 2 : 0),
PrintPanicReg(14, sregs, 5), PrintPanicReg(15, sregs, 6),
PanicShowExtraCm(pdata)});
return ret;
}
static std::string ParsePanicInfoNds32(const struct panic_data* pdata) {
const uint32_t* regs = pdata->nds_n8.regs;
uint32_t itype = pdata->nds_n8.itype;
uint32_t ipc = pdata->nds_n8.ipc;
uint32_t ipsw = pdata->nds_n8.ipsw;
std::string ret;
ret = base::StringPrintf(
"Saved panic data:%s\n",
(pdata->flags & PANIC_DATA_FLAG_OLD_HOSTCMD ? "" : " (NEW)"));
base::StrAppend(
&ret,
{base::StringPrintf("=== EXCEP: ITYPE=%x ===\n", itype),
base::StringPrintf("R0 %08x R1 %08x R2 %08x R3 %08x\n", regs[0],
regs[1], regs[2], regs[3]),
base::StringPrintf("R4 %08x R5 %08x R6 %08x R7 %08x\n", regs[4],
regs[5], regs[6], regs[7]),
base::StringPrintf("R8 %08x R9 %08x R10 %08x R15 %08x\n", regs[8],
regs[9], regs[10], regs[11]),
base::StringPrintf("FP %08x GP %08x LP %08x SP %08x\n", regs[12],
regs[13], regs[14], regs[15]),
base::StringPrintf("IPC %08x IPSW %05x\n", ipc, ipsw),
base::StringPrintf("SWID of ITYPE: %x\n", ((itype >> 16) & 0x7fff))});
return ret;
}
static std::string ParsePanicInfoRv32i(const struct panic_data* pdata) {
const uint32_t* regs;
uint32_t mcause, mepc;
regs = reinterpret_cast<const uint32_t*>(pdata->riscv.regs);
mcause = pdata->riscv.mcause;
mepc = pdata->riscv.mepc;
std::string ret;
ret = base::StringPrintf("=== EXCEPTION: MCAUSE=%x ===\n", mcause);
base::StrAppend(&ret,
{base::StringPrintf("S11 %08x S10 %08x S9 %08x S8 %08x\n",
regs[0], regs[1], regs[2], regs[3]),
base::StringPrintf("S7 %08x S6 %08x S5 %08x S4 %08x\n",
regs[4], regs[5], regs[6], regs[7]),
base::StringPrintf("S3 %08x S2 %08x S1 %08x S0 %08x\n",
regs[8], regs[9], regs[10], regs[11]),
base::StringPrintf("T6 %08x T5 %08x T4 %08x T3 %08x\n",
regs[12], regs[13], regs[14], regs[15]),
base::StringPrintf("T2 %08x T1 %08x T0 %08x A7 %08x\n",
regs[16], regs[17], regs[18], regs[19]),
base::StringPrintf("A6 %08x A5 %08x A4 %08x A3 %08x\n",
regs[20], regs[21], regs[22], regs[23]),
base::StringPrintf("A2 %08x A1 %08x A0 %08x TP %08x\n",
regs[24], regs[25], regs[26], regs[27]),
base::StringPrintf("GP %08x RA %08x SP %08x MEPC %08x\n",
regs[28], regs[29], regs[30], mepc)});
return ret;
}
} // namespace
base::expected<std::vector<uint8_t>, std::string> GetPanicInput(
size_t max_size) {
size_t size = 0;
size_t read;
std::vector<uint8_t> data(max_size);
while (1) {
read = fread(&data[size], 1, max_size - size, stdin);
if (read < 0) {
return base::unexpected("Cannot read panicinfo from stdin.");
}
if (read == 0)
break;
size += read;
if (size >= max_size) {
return base::unexpected("Too much panicinfo data in stdin.");
}
}
data.resize(size);
return data;
}
base::expected<std::string, std::string> ParsePanicInfo(
base::span<const uint8_t> data) {
size_t size = data.size();
/* Size of the panic information "header". */
const size_t header_size = 4;
/* Size of the panic information "trailer" (struct_size and magic). */
const size_t trailer_size =
sizeof(struct panic_data) - offsetof(struct panic_data, struct_size);
struct panic_data pdata = {0};
size_t copy_size;
std::string warning;
if (size < (header_size + trailer_size)) {
return base::unexpected(
base::StringPrintf("ERROR: Panic data too short (%zd).\n", size));
}
if (size > sizeof(pdata)) {
warning = base::StringPrintf("WARNING: Panic data too large (%zd > %zd)\n",
size, sizeof(pdata));
copy_size = sizeof(pdata);
} else {
copy_size = size;
}
/* Copy the data into pdata, as the struct size may have changed. */
memcpy(&pdata, data.data(), copy_size);
/* Then copy the trailer in position. */
memcpy(reinterpret_cast<uint8_t*>(&pdata) +
(sizeof(struct panic_data) - trailer_size),
data.last(trailer_size).data(), trailer_size);
/*
* We only understand panic data with version in [1, 2]. Error on invalid
* versions.
*/
if (pdata.struct_version > 2 || pdata.struct_version == 0)
return base::unexpected(
warning +
base::StringPrintf("ERROR: Unknown panic data version (%d).\n",
pdata.struct_version));
if (pdata.reserved != 0)
return base::unexpected(
warning + base::StringPrintf("ERROR: Panic reserve is not 0 (%d).\n",
pdata.reserved));
/* Validate flag is within BIT(0) to BIT(6) inclusive. */
if (pdata.flags >> 7)
return base::unexpected(
warning +
base::StringPrintf("ERROR: Incorrect flag (%d).\n", pdata.flags));
/*
* Validate magic number. This is unlikely to happen but we should investigate
* the mismatching in crash reports.
*/
if (pdata.magic != PANIC_DATA_MAGIC)
base::StrAppend(
&warning, {base::StringPrintf("WARNING: Incorrect panic magic (%d).\n",
pdata.magic)});
/*
* The size mismatching is unlikely to happen but we should investiage this
* case in crash reports.
*/
if (pdata.struct_size != size)
base::StrAppend(
&warning, {base::StringPrintf(
"WARNING: Panic struct size inconsistent (%u vs %zd).\n",
pdata.struct_size, size)});
std::string ret;
switch (pdata.arch) {
case PANIC_ARCH_CORTEX_M:
return base::ok(warning + ParsePanicInfoCm(&pdata));
case PANIC_ARCH_NDS32_N8:
return base::ok(warning + ParsePanicInfoNds32(&pdata));
case PANIC_ARCH_RISCV_RV32I:
return base::ok(warning + ParsePanicInfoRv32i(&pdata));
default:
return base::unexpected(
warning + base::StringPrintf("ERROR: Unknown architecture (%d).\n",
pdata.arch));
}
}
} // namespace ec