From f4515c3082dd413017ae06c25d7e85b1dcee30bf Mon Sep 17 00:00:00 2001 From: Magnus Holmgren Date: Sun, 8 Mar 2009 12:48:58 +0000 Subject: Add setjmp/longjmp for ARM and ColdFire to the codec lib, and use it in the Vorbis codec to better handle out of memory conditions (to exit rather than crash; the AAC codec could use it too). setjmp/longjmp comes from newlib 1.17.0 with a few minor changes (combine parts of some files, remove support for some architectures, change some ifdef's). git-svn-id: svn://svn.rockbox.org/rockbox/trunk@20235 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs/lib/SOURCES | 5 ++ apps/codecs/lib/setjmp.h | 59 +++++++++++++ apps/codecs/lib/setjmp_arm.S | 172 ++++++++++++++++++++++++++++++++++++++ apps/codecs/lib/setjmp_cf.S | 79 +++++++++++++++++ apps/codecs/libtremor/oggmalloc.c | 9 ++ apps/codecs/vorbis.c | 18 +++- 6 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 apps/codecs/lib/setjmp.h create mode 100644 apps/codecs/lib/setjmp_arm.S create mode 100644 apps/codecs/lib/setjmp_cf.S diff --git a/apps/codecs/lib/SOURCES b/apps/codecs/lib/SOURCES index 8099620098..b0ebbc5441 100644 --- a/apps/codecs/lib/SOURCES +++ b/apps/codecs/lib/SOURCES @@ -5,11 +5,16 @@ codeclib.c mdct2.c #ifdef CPU_ARM mdct_arm.S +setjmp_arm.S #if ARM_ARCH == 4 udiv32_armv4.S #endif #endif +#ifdef CPU_COLDFIRE +setjmp_cf.S +#endif + #elif defined(SIMULATOR) && defined(__APPLE__) osx.dummy.c #endif diff --git a/apps/codecs/lib/setjmp.h b/apps/codecs/lib/setjmp.h new file mode 100644 index 0000000000..4bcf6af623 --- /dev/null +++ b/apps/codecs/lib/setjmp.h @@ -0,0 +1,59 @@ +#ifndef _SETJMP_H_ +#define _SETJMP_H_ + +/* Combined parts of include/setjmp.h and include/machine/setjmp.h in + * newlib 1.17.0, with minor changes for Rockbox. + */ + +#ifdef CPU_ARM +/* + * All callee preserved registers: + * v1 - v7, fp, ip, sp, lr, f4, f5, f6, f7 + */ +#define _JBLEN 23 +#endif + +/* necv70 was 9 as well. */ + +#ifdef CPU_COLDFIRE +/* + * onsstack,sigmask,sp,pc,psl,d2-d7,a2-a6, + * fp2-fp7 for 68881. + * All else recovered by under/over(flow) handling. + */ +#define _JBLEN 34 +#endif + +#ifdef __mips__ +#ifdef __mips64 +#define _JBTYPE long long +#endif +#ifdef __mips_soft_float +#define _JBLEN 11 +#else +#define _JBLEN 23 +#endif +#endif + +#ifdef __sh__ +#if __SH5__ +#define _JBLEN 50 +#define _JBTYPE long long +#else +#define _JBLEN 20 +#endif /* __SH5__ */ +#endif + +#ifdef _JBLEN +#ifdef _JBTYPE +typedef _JBTYPE jmp_buf[_JBLEN]; +#else +typedef int jmp_buf[_JBLEN]; +#endif +#endif + + +extern void longjmp(jmp_buf __jmpb, int __retval); +extern int setjmp(jmp_buf __jmpb); + +#endif // _SETJMP_H_ diff --git a/apps/codecs/lib/setjmp_arm.S b/apps/codecs/lib/setjmp_arm.S new file mode 100644 index 0000000000..4bb2a46a7a --- /dev/null +++ b/apps/codecs/lib/setjmp_arm.S @@ -0,0 +1,172 @@ +/* This is a simple version of setjmp and longjmp. + + Nick Clifton, Cygnus Solutions, 13 June 1997. */ + +/* ANSI concatenation macros. */ +#define CONCAT(a, b) CONCAT2(a, b) +#define CONCAT2(a, b) a##b + +#ifndef __USER_LABEL_PREFIX__ +#error __USER_LABEL_PREFIX__ not defined +#endif + +#define SYM(x) CONCAT (__USER_LABEL_PREFIX__, x) + +#ifdef __ELF__ +#define TYPE(x) .type SYM(x),function +#define SIZE(x) .size SYM(x), . - SYM(x) +#else +#define TYPE(x) +#define SIZE(x) +#endif + +/* Arm/Thumb interworking support: + + The interworking scheme expects functions to use a BX instruction + to return control to their parent. Since we need this code to work + in both interworked and non-interworked environments as well as with + older processors which do not have the BX instruction we do the + following: + Test the return address. + If the bottom bit is clear perform an "old style" function exit. + (We know that we are in ARM mode and returning to an ARM mode caller). + Otherwise use the BX instruction to perform the function exit. + + We know that we will never attempt to perform the BX instruction on + an older processor, because that kind of processor will never be + interworked, and a return address with the bottom bit set will never + be generated. + + In addition, we do not actually assemble the BX instruction as this would + require us to tell the assembler that the processor is an ARM7TDMI and + it would store this information in the binary. We want this binary to be + able to be linked with binaries compiled for older processors however, so + we do not want such information stored there. + + If we are running using the APCS-26 convention however, then we never + test the bottom bit, because this is part of the processor status. + Instead we just do a normal return, since we know that we cannot be + returning to a Thumb caller - the Thumb does not support APCS-26. + + Function entry is much simpler. If we are compiling for the Thumb we + just switch into ARM mode and then drop through into the rest of the + function. The function exit code will take care of the restore to + Thumb mode. + + For Thumb-2 do everything in Thumb mode. */ + +#ifdef __APCS_26__ +#define RET movs pc, lr +#elif defined(__thumb2__) +#define RET bx lr +#else +#define RET tst lr, #1; \ + moveq pc, lr ; \ +.word 0xe12fff1e /* bx lr */ +#endif + +#ifdef __thumb2__ +.macro COND where when + i\where \when +.endm +#else +.macro COND where when +.endm +#endif + +#if defined(__thumb2__) +.syntax unified +.macro MODE + .thumb + .thumb_func +.endm +.macro PROLOGUE name +.endm + +#elif defined(__thumb__) +#define MODE .thumb_func +.macro PROLOGUE name + .code 16 + bx pc + nop + .code 32 +SYM (.arm_start_of.\name): +.endm +#else /* Arm */ +#define MODE .code 32 +.macro PROLOGUE name +.endm +#endif + +.macro FUNC_START name + .text + .align 2 + MODE + .globl SYM (\name) + TYPE (\name) +SYM (\name): + PROLOGUE \name +.endm + +.macro FUNC_END name + RET + SIZE (\name) +.endm + +/* -------------------------------------------------------------------- + int setjmp (jmp_buf); + -------------------------------------------------------------------- */ + + FUNC_START setjmp + + /* Save all the callee-preserved registers into the jump buffer. */ +#ifdef __thumb2__ + stmea a1!, { v1-v7, fp, ip, lr } + str sp, [a1],#+4 +#else + stmea a1!, { v1-v7, fp, ip, sp, lr } +#endif + +#if 0 /* Simulator does not cope with FP instructions yet. */ +#ifndef __SOFTFP__ + /* Save the floating point registers. */ + sfmea f4, 4, [a1] +#endif +#endif + /* When setting up the jump buffer return 0. */ + mov a1, #0 + + FUNC_END setjmp + +/* -------------------------------------------------------------------- + volatile void longjmp (jmp_buf, int); + -------------------------------------------------------------------- */ + + FUNC_START longjmp + + /* If we have stack extension code it ought to be handled here. */ + + /* Restore the registers, retrieving the state when setjmp() was called. */ +#ifdef __thumb2__ + ldmfd a1!, { v1-v7, fp, ip, lr } + ldr sp, [a1],#+4 +#else + ldmfd a1!, { v1-v7, fp, ip, sp, lr } +#endif + +#if 0 /* Simulator does not cope with FP instructions yet. */ +#ifndef __SOFTFP__ + /* Restore floating point registers as well. */ + lfmfd f4, 4, [a1] +#endif +#endif + /* Put the return value into the integer result register. + But if it is zero then return 1 instead. */ + movs a1, a2 +#ifdef __thumb2__ + it eq +#endif + moveq a1, #1 + + FUNC_END longjmp + diff --git a/apps/codecs/lib/setjmp_cf.S b/apps/codecs/lib/setjmp_cf.S new file mode 100644 index 0000000000..acc98c3f59 --- /dev/null +++ b/apps/codecs/lib/setjmp_cf.S @@ -0,0 +1,79 @@ +/* ANSI concatenation macros. */ + +#define CONCAT1(a, b) CONCAT2(a, b) +#define CONCAT2(a, b) a ## b + +/* Use the right prefix for global labels. */ + +#define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x) + +/* Use the right prefix for registers. */ + +#define REG(x) CONCAT1 (__REGISTER_PREFIX__, x) + +#define d0 REG (d0) +#define d1 REG (d1) +#define d2 REG (d2) +#define d3 REG (d3) +#define d4 REG (d4) +#define d5 REG (d5) +#define d6 REG (d6) +#define d7 REG (d7) +#define a0 REG (a0) +#define a1 REG (a1) +#define a2 REG (a2) +#define a3 REG (a3) +#define a4 REG (a4) +#define a5 REG (a5) +#define a6 REG (a6) +#define fp REG (fp) +#define sp REG (sp) + + +.global SYM (setjmp) +.global SYM (longjmp) + +SYM (setjmp): + moveal sp@(4),a0 + movel sp@(0),a0@(12) + movel sp,a0@(8) + moveml d2-d7/a2-a6,a0@(20) + clrl d0 + rts + +SYM (longjmp): + moveal sp@(4),a0 + movel sp@(8),d0 + bne 1f + movel &1,d0 +1: + moveml a0@(20),d2-d7/a2-a6 + moveal a0@(8),sp + movel a0@(12),sp@ + rts + +#ifdef M68881 +.global SYM (setjmp_68881) +.global SYM (longjmp_68881) + +SYM (setjmp_68881): + moveal sp@(4),a0 + movel sp@(0),a0@(12) + movel sp,a0@(8) + moveml d2-d7/a2-a6,a0@(20) + fmovemx fp2-fp7,a0@(64) + clrl d0 + rts + +SYM (longjmp_68881): + moveal sp@(4),a0 + fmovemx a0@(64),fp2-fp7 + movel sp@(8),d0 + bne 1f + movel &1,d0 +1: + moveml a0@(20),d2-d7/a2-a6 + moveal a0@(8),sp + movel a0@(12),sp@ + rts +#endif diff --git a/apps/codecs/libtremor/oggmalloc.c b/apps/codecs/libtremor/oggmalloc.c index ca917ff397..4aa2760629 100644 --- a/apps/codecs/libtremor/oggmalloc.c +++ b/apps/codecs/libtremor/oggmalloc.c @@ -1,5 +1,10 @@ #include "os_types.h" +#if defined(CPU_ARM) || defined(CPU_COLDFIRE) +#include +extern jmp_buf rb_jump_buf; +#endif + static size_t tmp_ptr; void ogg_malloc_init(void) @@ -16,7 +21,11 @@ void *ogg_malloc(size_t size) size = (size + 3) & ~3; if (mem_ptr + size > tmp_ptr) +#if defined(CPU_ARM) || defined(CPU_COLDFIRE) + longjmp(rb_jump_buf, 1); +#else return NULL; +#endif x = &mallocbuf[mem_ptr]; mem_ptr += size; /* Keep memory 32-bit aligned */ diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c index 74d582ebb3..f14aeead84 100644 --- a/apps/codecs/vorbis.c +++ b/apps/codecs/vorbis.c @@ -25,6 +25,11 @@ CODEC_HEADER +#if defined(CPU_ARM) || defined(CPU_COLDFIRE) +#include +jmp_buf rb_jump_buf; +#endif + /* Some standard functions and variables needed by Tremor */ static size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) @@ -107,7 +112,7 @@ enum codec_status codec_main(void) int error; long n; int current_section; - int previous_section = -1; + int previous_section; int eof; ogg_int64_t vf_offsets[2]; ogg_int64_t vf_dataoffsets; @@ -119,6 +124,15 @@ enum codec_status codec_main(void) * they should be set differently based on quality setting */ +#if defined(CPU_ARM) || defined(CPU_COLDFIRE) + if (setjmp(rb_jump_buf) != 0) + { + /* malloc failed; skip to next track */ + error = CODEC_ERROR; + goto done; + } +#endif + /* We need to flush reserver memory every track load. */ next_track: if (codec_init()) { @@ -181,6 +195,7 @@ next_track: ci->set_offset(ov_raw_tell(&vf)); } + previous_section = -1; eof = 0; while (!eof) { ci->yield(); @@ -227,7 +242,6 @@ done: vf.serialnos = NULL; vf.pcmlengths = NULL; ov_clear(&vf); - previous_section = -1; goto next_track; } -- cgit v1.2.3