mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
Convert panic() to C code
Move the implementation of panic into C code. Only a very small part
needs to be in assembler, and the reset is easier to maintain as C.
As part of this, define panic_putc() and panic_puts() which directly
wite to the UART.
To make things more convenience for the future, add a simple printf()
implementation in the panic path. This is not reliant on the uart
buffering system being in a happy state. However, we do call the
emergency flush so that our panic message will appear after previous
output rather that surpressing it (which would be extremely confusing).
Code/data size for panic.o grows by about 200 bytes, but this is mostly
due to the increased flexibility.
text data bss dec hex filename
292 272 0 564 234 old panic.S
692 3 48 743 2e7 new panic.c
BUG=chrome-os-partner:10146
TEST=manual:
build and boot on snow:
> rw 0x06000000
=== EXCEPTION: 03 ====== xPSR: 01000000 ===========
r0 :0000000b r1 :00000047 r2 :06000000 r3 :200013dd
r4 :00000000 r5 :080052cc r6 :200013d0 r7 :00000002
r8 :00000000 r9 :200013de r10:00000000 r11:00000000
r12:00000000 sp :200009a0 lr :08002a5d pc :08003962
Rebooting...
Change-Id: If3e3f572e0f32af780b6ebda235b1b3cde4de5e4
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/24503
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
@@ -368,7 +368,7 @@ fini_loop:
|
||||
/* default exception handler */
|
||||
.thumb_func
|
||||
default_handler:
|
||||
b panic
|
||||
b exception_panic
|
||||
|
||||
_bss_start:
|
||||
.long __bss_start
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
* Fatal exception handling and debug tracing
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
.text
|
||||
|
||||
.syntax unified
|
||||
.code 16
|
||||
|
||||
.macro hex_reg r, offset @ prepare to build
|
||||
add r1, r3, #\offset @ .. hexadecimal string
|
||||
mov r0, \r @ .. from the reg value
|
||||
bl buildhex
|
||||
.endm
|
||||
|
||||
/* fatal exception handler */
|
||||
.global panic
|
||||
.thumb_func
|
||||
panic:
|
||||
#ifndef CONFIG_DEBUG
|
||||
b EcSystemReset @ Reboot the system
|
||||
#else /* CONFIG_DEBUG */
|
||||
/* check that the exception stack pointer is valid */
|
||||
ldr r0, =CONFIG_RAM_BASE @ start of RAM
|
||||
ldr r1, =CONFIG_RAM_BASE+CONFIG_RAM_SIZE @ end of RAM
|
||||
mrs r12, psp @ process stack pointer
|
||||
cmp r12, r0 @ is sp >= RAM start ?
|
||||
it ge
|
||||
cmpge r1, r12 @ is sp < RAM end ?
|
||||
blt panic_print @ no => no values to read
|
||||
/* output registers value */
|
||||
ldr r3, =msg_excep @ pointer to the text buffer
|
||||
mrs r2, ipsr @ get exception num from IPSR
|
||||
bfc r2, #9, #23 @ the exception is the 3 LSB
|
||||
hex_reg r2, 18 @ prepare hexa for excep number
|
||||
hex_reg r4, 119 @ prepare hexa for R4
|
||||
hex_reg r5, 132 @ prepare hexa for R5
|
||||
hex_reg r6, 145 @ prepare hexa for R6
|
||||
hex_reg r7, 156 @ prepare hexa for R7
|
||||
hex_reg r8, 171 @ prepare hexa for R8
|
||||
hex_reg r9, 184 @ prepare hexa for R9
|
||||
hex_reg r10, 197 @ prepare hexa for R10
|
||||
hex_reg r11, 210 @ prepare hexa for R11
|
||||
ldmia r12!, {r4-r11} @ load saved r0-r3,r12,lr,pc,psr
|
||||
hex_reg r4, 66 @ prepare hexa for R0
|
||||
hex_reg r5, 79 @ prepare hexa for R1
|
||||
hex_reg r6, 92 @ prepare hexa for R2
|
||||
hex_reg r7, 105 @ prepare hexa for R3
|
||||
hex_reg r8, 225 @ prepare hexa for R12
|
||||
hex_reg r12, 238 @ prepare hexa for SP
|
||||
hex_reg r9, 251 @ prepare hexa for LR
|
||||
hex_reg r10, 264 @ prepare hexa for PC
|
||||
hex_reg r11, 40 @ prepare hexa for xPSR
|
||||
/* print exception trace */
|
||||
panic_print:
|
||||
ldr r0, =msg_excep @ pointer to the text buffer
|
||||
bl emergency_puts @ print the banner
|
||||
mov r0, #0 @ Soft boot
|
||||
b system_reset @ Reboot the system
|
||||
|
||||
/* Helpers for exception trace */
|
||||
/* print a string on the UART
|
||||
* r0: asciiZ string pointer
|
||||
*/
|
||||
emergency_puts:
|
||||
ldr r1, =CONFIG_UART_ADDRESS @ UART base address
|
||||
1:
|
||||
ldrb r3, [r0], #1 @ read one character
|
||||
cmp r3, #0 @ end of the string ?
|
||||
beq 3f @ if yes, return
|
||||
2: /* putchar */
|
||||
ldr r2, [r1, #CONFIG_UART_SR_OFFSET] @ read UART status
|
||||
tst r2, #CONFIG_UART_SR_TXEMPTY @ free space on TX ?
|
||||
beq 2b @ if no, wait
|
||||
str r3, [r1, #CONFIG_UART_DR_OFFSET] @ send character to UART DR
|
||||
b 1b @ goto next character
|
||||
3: /* return */
|
||||
bx lr
|
||||
|
||||
/* write a number in hexadecimal in a text buffer
|
||||
* r0: number to print
|
||||
* r1: pointer to *end* of the buffer (filled with '0')
|
||||
*/
|
||||
buildhex:
|
||||
cmp r0, #0
|
||||
it eq
|
||||
bxeq lr
|
||||
and r2, r0, #0xf
|
||||
cmp r2, #10
|
||||
ite lt
|
||||
addlt r2, #'0'
|
||||
addge r2, #'A'-10
|
||||
strb r2, [r1],#-1
|
||||
lsr r0, #4
|
||||
b buildhex
|
||||
|
||||
.data
|
||||
msg_excep: .ascii "\r\n=== EXCEPTION: 00 ====== xPSR: 00000000 ===========\r\n"
|
||||
msg_reg0: .ascii "R0 :00000000 R1 :00000000 R2 :00000000 R3 :00000000\r\n"
|
||||
msg_reg1: .ascii "R4 :00000000 R5 :00000000 R6 :00000000 R7 :00000000\r\n"
|
||||
msg_reg2: .ascii "R8 :00000000 R9 :00000000 R10:00000000 R11:00000000\r\n"
|
||||
msg_reg3: .ascii "R12:00000000 SP :00000000 LR :00000000 PC :00000000\r\n\0"
|
||||
.align 4
|
||||
#endif /* CONFIG_DEBUG */
|
||||
215
core/cortex-m/panic.c
Normal file
215
core/cortex-m/panic.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "cpu.h"
|
||||
#include "panic.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "uart.h"
|
||||
#include "util.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
|
||||
/* We save registers here for display by report_panic() */
|
||||
static struct save_area
|
||||
{
|
||||
uint32_t saved_regs[11]; /* psp, ipsr, lr, r4-r11 */
|
||||
} save_area __attribute__((aligned(8)));
|
||||
|
||||
|
||||
void panic_putc(int ch)
|
||||
{
|
||||
uart_emergency_flush();
|
||||
if (ch == '\n')
|
||||
panic_putc('\r');
|
||||
uart_write_char(ch);
|
||||
while (uart_tx_ready())
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void panic_puts(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
panic_putc(*s++);
|
||||
}
|
||||
|
||||
|
||||
void panic_vprintf(const char *format, va_list args)
|
||||
{
|
||||
int pad_width;
|
||||
|
||||
while (*format) {
|
||||
int c = *format++;
|
||||
|
||||
/* Copy normal characters */
|
||||
if (c != '%') {
|
||||
panic_putc(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get first format character */
|
||||
c = *format++;
|
||||
|
||||
/* Handle %c */
|
||||
if (c == 'c') {
|
||||
c = va_arg(args, int);
|
||||
panic_putc(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Count padding length (only supported for hex) */
|
||||
pad_width = 0;
|
||||
while (c >= '0' && c <= '9') {
|
||||
pad_width = (10 * pad_width) + c - '0';
|
||||
c = *format++;
|
||||
}
|
||||
|
||||
if (c == 's') {
|
||||
char *vstr;
|
||||
|
||||
vstr = va_arg(args, char *);
|
||||
panic_puts(vstr ? vstr : "(null)");
|
||||
} else { /* assume 'x' */
|
||||
uint32_t v, shift;
|
||||
int i;
|
||||
|
||||
v = va_arg(args, uint32_t);
|
||||
if (!pad_width)
|
||||
pad_width = 8;
|
||||
shift = pad_width * 4 - 4;
|
||||
for (i = 0; i < pad_width; i++) {
|
||||
int ch = '0' + ((v >> shift) & 0xf);
|
||||
|
||||
if (ch > '9')
|
||||
ch += 'a' - '9' - 1;
|
||||
panic_putc(ch);
|
||||
shift -= 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void panic_printf(const char *format, ...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args, format);
|
||||
panic_vprintf(format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Print the name and value of a register
|
||||
*
|
||||
* This is a convenient helper function for displaying a register value.
|
||||
* It shows the register name in a 3 character field, followed by a colon.
|
||||
* The register value is regs[index], and this is shown in hex. If regs is
|
||||
* NULL, then we display spaces instead.
|
||||
*
|
||||
* After displaying the value, either a space or \n is displayed depending
|
||||
* on the register number, so that (assuming the caller passes all 16
|
||||
* registers in sequence) we put 4 values per line like this
|
||||
*
|
||||
* r0 :0000000b r1 :00000047 r2 :60000000 r3 :200012b5
|
||||
* r4 :00000000 r5 :08004e64 r6 :08004e1c r7 :200012a8
|
||||
* r8 :08004e64 r9 :00000002 r10:00000000 r11:00000000
|
||||
* r12:0000003f sp :200009a0 lr :0800270d pc :0800351a
|
||||
*
|
||||
* @param regnum Register number to display (0-15)
|
||||
* @param regs Pointer to array holding the registers, or NULL
|
||||
* @param index Index into array where the register value is present
|
||||
*/
|
||||
static void print_reg(int regnum, uint32_t *regs, int index)
|
||||
{
|
||||
static const char regname[] = "r10r11r12sp lr pc ";
|
||||
static char rname[3] = "r ";
|
||||
const char *name;
|
||||
|
||||
rname[1] = '0' + regnum;
|
||||
name = regnum < 10 ? rname : ®name[(regnum - 10) * 3];
|
||||
panic_printf("%c%c%c:", name[0], name[1], name[2]);
|
||||
if (regs)
|
||||
panic_printf("%8x", regs[index]);
|
||||
else
|
||||
panic_puts(" ");
|
||||
panic_putc((regnum & 3) == 3 ? '\n' : ' ');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a message and reboot
|
||||
*/
|
||||
static void panic_reboot(void)
|
||||
{
|
||||
panic_puts("\n\nRebooting...\n");
|
||||
system_reset(0);
|
||||
}
|
||||
|
||||
|
||||
void report_panic(const char *msg, uint32_t *lregs)
|
||||
{
|
||||
if (msg) {
|
||||
panic_printf("\n** PANIC: %s\n", msg);
|
||||
} else if (lregs) {
|
||||
uint32_t *sregs = NULL;
|
||||
uint32_t psp;
|
||||
int i;
|
||||
|
||||
psp = lregs[0];
|
||||
if (psp >= CONFIG_RAM_BASE
|
||||
&& psp < CONFIG_RAM_BASE + CONFIG_RAM_SIZE)
|
||||
sregs = (uint32_t *)psp;
|
||||
panic_printf("\n=== EXCEPTION: %2x ====== xPSR: %8x "
|
||||
"===========\n", lregs[1] & 7, sregs ? sregs[7] : -1);
|
||||
for (i = 0; i < 4; i++)
|
||||
print_reg(i, sregs, i);
|
||||
for (i = 4; i < 10; i++)
|
||||
print_reg(i, lregs, i - 1);
|
||||
print_reg(10, lregs, 9);
|
||||
print_reg(11, lregs, 10);
|
||||
print_reg(12, sregs, 4);
|
||||
print_reg(13, &psp, 0);
|
||||
print_reg(14, sregs, 5);
|
||||
print_reg(15, sregs, 6);
|
||||
}
|
||||
|
||||
panic_reboot();
|
||||
}
|
||||
|
||||
/* Default exception handler, which reports a panic */
|
||||
void exception_panic(void) __attribute__((naked));
|
||||
void exception_panic(void)
|
||||
{
|
||||
/* Naked call so we can extract raw LR and IPSR */
|
||||
asm volatile(
|
||||
/*
|
||||
* This instruction will generate ldr rx, [pc, #offset]
|
||||
* followed by a mov r0, rx. It would clearly be better if
|
||||
* we could get ldr r0, [pc, #offset] but that doesn't seem
|
||||
* to be supported. Nor does gcc seem to define which
|
||||
* temporary register it uses. Therefore we put this
|
||||
* instruction first so that it matters less.
|
||||
*
|
||||
* If you see a failure in the panic handler, please check
|
||||
* the final assembler output here.
|
||||
*/
|
||||
"mov r0, %[save_area]\n"
|
||||
"mrs r1, psp\n"
|
||||
"mrs r2, ipsr\n"
|
||||
"mov r3, lr\n"
|
||||
"stmia r0, {r1-r11}\n"
|
||||
"mov r1, r0\n"
|
||||
"mov r0, #0\n"
|
||||
"b report_panic" : :
|
||||
[save_area] "r" (save_area.saved_regs)
|
||||
);
|
||||
}
|
||||
69
include/panic.h
Normal file
69
include/panic.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*
|
||||
* Panic handling, including displaying a message on the panic reporting
|
||||
* device, which is currently the UART.
|
||||
*/
|
||||
|
||||
#ifndef __PANIC_H
|
||||
#include <stdarg.h>
|
||||
|
||||
/**
|
||||
* Write a character to the panic reporting device
|
||||
*
|
||||
* This function will not return until the character has left the UART
|
||||
* data register. Any previously queued UART traffic is displayed first.
|
||||
*
|
||||
* @param ch Character to write
|
||||
*/
|
||||
void panic_putc(int ch);
|
||||
|
||||
/**
|
||||
* Write a string to the panic reporting device
|
||||
*
|
||||
* This function will not return until the string has left the UART
|
||||
* data register. Any previously queued UART traffic is displayed first.
|
||||
*
|
||||
* @param ch Character to write
|
||||
*/
|
||||
void panic_puts(const char *s);
|
||||
|
||||
/**
|
||||
* Very basic vprintf() for use in panic situations
|
||||
*
|
||||
* We only support %s and %nx where n is the number of hex digits to display.
|
||||
* Currently we don't even support %d, and it is aimed at small code size.
|
||||
*
|
||||
* TODO(sjg@chromium.org): Really what we need is a vsnprintf() that is
|
||||
* shared between the console UART and panic (and is also available as an
|
||||
* snprintf()). The only downside is that we would then require a large
|
||||
* printf() implementation to be always present, whereas presumably now we
|
||||
* can turn it off.
|
||||
*
|
||||
* @param format printf-style format string
|
||||
* @param args List of arguments to process
|
||||
*/
|
||||
void panic_vprintf(const char *format, va_list args);
|
||||
|
||||
/**
|
||||
* Very basic printf() for use in panic situations
|
||||
*
|
||||
* See panic_vprintf() for full details
|
||||
*
|
||||
* @param format printf-style format string
|
||||
* @param ... Arguments to process
|
||||
*/
|
||||
void panic_printf(const char *format, ...);
|
||||
|
||||
/**
|
||||
* Report a panic to the panic reporting device
|
||||
*
|
||||
* This is exported only to permit use from assembler.
|
||||
*
|
||||
* @param msg Panic message
|
||||
* @param lregs Registers from the exception: psp, ipsr, lr, r4-r11
|
||||
*/
|
||||
void report_panic(const char *msg, uint32_t *lregs);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user