Created
December 14, 2011 10:25
-
-
Save pamaury/1476044 to your computer and use it in GitHub Desktop.
imx233/fuze+ sd work in progress
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/apps/debug_menu.c b/apps/debug_menu.c | |
index 79a0752..bcbecc7 100644 | |
--- a/apps/debug_menu.c | |
+++ b/apps/debug_menu.c | |
@@ -1132,7 +1132,9 @@ static bool view_battery(void) | |
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) | |
#if (CONFIG_STORAGE & STORAGE_MMC) || (CONFIG_STORAGE & STORAGE_SD) | |
-#if (CONFIG_STORAGE & STORAGE_MMC) | |
+#if (CONFIG_STORAGE & (STORAGE_MMC | STORAGE_SD)) | |
+#define CARDTYPE "SD/MMC" | |
+#elif (CONFIG_STORAGE & STORAGE_MMC) | |
#define CARDTYPE "MMC" | |
#elif (CONFIG_STORAGE & STORAGE_SD) | |
#define CARDTYPE "microSD" | |
diff --git a/firmware/SOURCES b/firmware/SOURCES | |
index f5ee787..8a13d41 100644 | |
--- a/firmware/SOURCES | |
+++ b/firmware/SOURCES | |
@@ -474,6 +474,13 @@ target/arm/memset16-arm.S | |
target/arm/ffs-arm.S | |
#endif | |
#if CONFIG_PLATFORM & PLATFORM_NATIVE | |
+target/arm/unwarminder/client.c | |
+target/arm/unwarminder/get_sp.c | |
+target/arm/unwarminder/unwarm_arm.c | |
+target/arm/unwarminder/unwarm.c | |
+target/arm/unwarminder/unwarminder.c | |
+target/arm/unwarminder/unwarmmem.c | |
+target/arm/unwarminder/unwarm_thumb.c | |
target/arm/system-arm.c | |
#endif | |
#if CONFIG_I2C == I2C_PP5024 || CONFIG_I2C == I2C_PP5020 || CONFIG_I2C == I2C_PP5002 | |
diff --git a/firmware/export/config/sansafuzeplus.h b/firmware/export/config/sansafuzeplus.h | |
index c9376cc..880bbae 100644 | |
--- a/firmware/export/config/sansafuzeplus.h | |
+++ b/firmware/export/config/sansafuzeplus.h | |
@@ -102,6 +102,9 @@ | |
/* Define this if you have a software controlled poweroff */ | |
#define HAVE_SW_POWEROFF | |
+/* Some Sansa Fuzes seem to be FAT16 formatted */ | |
+#define HAVE_FAT16SUPPORT | |
+ | |
/* The number of bytes reserved for loadable codecs */ | |
#define CODEC_SIZE 0x100000 | |
diff --git a/firmware/export/enc_base.h b/firmware/export/enc_base.h | |
index 321421c..a476dbe 100644 | |
--- a/firmware/export/enc_base.h | |
+++ b/firmware/export/enc_base.h | |
@@ -160,6 +160,7 @@ struct encoder_config | |
/* Header at the beginning of every encoder chunk */ | |
#ifdef DEBUG | |
+#define H_TO_BE32 htobe32 | |
#define ENC_CHUNK_MAGIC H_TO_BE32(('P' << 24) | ('T' << 16) | ('Y' << 8) | 'R') | |
#endif | |
struct enc_chunk_hdr | |
diff --git a/firmware/export/panic.h b/firmware/export/panic.h | |
index b0325aa..1e222ef 100644 | |
--- a/firmware/export/panic.h | |
+++ b/firmware/export/panic.h | |
@@ -22,8 +22,12 @@ | |
#ifndef __PANIC_H__ | |
#define __PANIC_H__ | |
+#include "config.h" | |
#include "gcc_extensions.h" | |
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM) | |
+void panicf( const char *fmt, ... ) __attribute__ ((naked)); | |
+#else | |
void panicf( const char *fmt, ... ) ATTRIBUTE_PRINTF(1, 2); | |
- | |
+#endif | |
#endif /* __PANIC_H__ */ | |
diff --git a/firmware/panic.c b/firmware/panic.c | |
index bd2c719..9f694f5 100644 | |
--- a/firmware/panic.c | |
+++ b/firmware/panic.c | |
@@ -31,14 +31,42 @@ | |
#include "power.h" | |
#include "system.h" | |
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM) | |
+#include "gcc_extensions.h" | |
+#include "unwarminder/client.h" | |
+#endif | |
+ | |
static char panic_buf[128]; | |
#define LINECHARS (LCD_WIDTH/SYSFONT_WIDTH) - 2 | |
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM) | |
+void panicf_f( const char *fmt, ...); | |
+ | |
+/* we wrap panicf() here with naked function to catch SP value */ | |
+void panicf( const char *fmt, ...) | |
+{ | |
+ (void)fmt; | |
+ asm volatile ("mov r4, sp \n" | |
+ "b panicf_f \n" | |
+ ); | |
+} | |
+ | |
/* | |
* "Dude. This is pretty fucked-up, right here." | |
*/ | |
+void panicf_f( const char *fmt, ...) | |
+{ | |
+ int sp; | |
+ | |
+ asm volatile ("mov %[SP],r4 \n" | |
+ : [SP] "=r" (sp) | |
+ ); | |
+ | |
+ int pc = (int)__builtin_return_address(0); | |
+#else | |
void panicf( const char *fmt, ...) | |
{ | |
+#endif | |
va_list ap; | |
#if (CONFIG_PLATFORM & PLATFORM_NATIVE) | |
@@ -82,6 +110,10 @@ void panicf( const char *fmt, ...) | |
panic_buf[i+LINECHARS] = c; | |
} | |
} | |
+ | |
+#if (CONFIG_PLATFORM & PLATFORM_NATIVE) && defined(CPU_ARM) | |
+ backtrace(pc, sp, &y); | |
+#endif | |
#else | |
/* no LCD */ | |
#endif | |
diff --git a/firmware/target/arm/imx233/app.lds b/firmware/target/arm/imx233/app.lds | |
index 0eeecc1..528cd16 100644 | |
--- a/firmware/target/arm/imx233/app.lds | |
+++ b/firmware/target/arm/imx233/app.lds | |
@@ -52,6 +52,7 @@ SECTIONS | |
_iramstart = .; // always 0 | |
*(.vectors) | |
KEEP(*(.vectors));// otherwise there are no references to it and the linker strip it | |
+ KEEP(*(.symbols)); | |
*(.icode) | |
*(.irodata) | |
*(.idata) | |
diff --git a/firmware/target/arm/imx233/clkctrl-imx233.c b/firmware/target/arm/imx233/clkctrl-imx233.c | |
index fa94f23..dbdc12e 100644 | |
--- a/firmware/target/arm/imx233/clkctrl-imx233.c | |
+++ b/firmware/target/arm/imx233/clkctrl-imx233.c | |
@@ -266,7 +266,7 @@ unsigned imx233_get_clock_freq(enum imx233_clock_t clk) | |
{ | |
case CLK_PLL: /* PLL: 480MHz when enable */ | |
return imx233_is_clock_enable(CLK_PLL) ? 480000 : 0; | |
- case CLK_XTAL: /* crytsal: 24MHz */ | |
+ case CLK_XTAL: /* crystal: 24MHz */ | |
return 24000; | |
case CLK_CPU: | |
{ | |
diff --git a/firmware/target/arm/imx233/mmc-imx233.c b/firmware/target/arm/imx233/mmc-imx233.c | |
index 8bdefeb..f638c06 100644 | |
--- a/firmware/target/arm/imx233/mmc-imx233.c | |
+++ b/firmware/target/arm/imx233/mmc-imx233.c | |
@@ -206,7 +206,7 @@ static int transfer_sectors(IF_MD2(int drive,) unsigned long start, int count, v | |
start += mmc_window_start; | |
if((start + count) > mmc_window_end) | |
return -201; | |
- /* get mutex (needed because we done multiple commands for count > 0 */ | |
+ /* get mutex (needed because we do multiple commands for count > 0 */ | |
mutex_lock(&mmc_mutex); | |
int ret = 0; | |
uint32_t resp; | |
diff --git a/firmware/target/arm/imx233/sd-imx233.c b/firmware/target/arm/imx233/sd-imx233.c | |
index 1f26531..cdaaa76 100644 | |
--- a/firmware/target/arm/imx233/sd-imx233.c | |
+++ b/firmware/target/arm/imx233/sd-imx233.c | |
@@ -51,56 +51,181 @@ static int last_disk_activity; | |
static void sd_detect_callback(int ssp) | |
{ | |
(void)ssp; | |
- | |
/* This is called only if the state was stable for 300ms - check state | |
* and post appropriate event. */ | |
if(imx233_ssp_sdmmc_detect(SD_SSP)) | |
queue_broadcast(SYS_HOTSWAP_INSERTED, 0); | |
else | |
queue_broadcast(SYS_HOTSWAP_EXTRACTED, 0); | |
- imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback); | |
+ imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback, false); | |
+} | |
+ | |
+void sd_power(bool on) | |
+{ | |
+ #ifdef SANSA_FUZEPLUS | |
+ /* The Fuze+ uses pin B0P8 for whatever reason, power ? */ | |
+ imx233_set_pin_function(0, 8, PINCTRL_FUNCTION_GPIO); | |
+ imx233_enable_gpio_output(0, 8, true); | |
+ imx233_set_gpio_output(0, 8, !on); | |
+ /* disable pull ups when not needed to save power */ | |
+ imx233_ssp_setup_ssp1_sd_mmc_pins(on, 4, PINCTRL_DRIVE_4mA, false); | |
+ /* It also setups pin B1P30, unknown purpose */ | |
+ imx233_set_pin_function(1, 30, PINCTRL_FUNCTION_GPIO); | |
+ imx233_enable_gpio_output(1, 30, false); | |
+ #endif | |
} | |
void sd_enable(bool on) | |
{ | |
- static bool sd_enable = false; | |
+ static int sd_enable = 2; /* 2 means not on and not off, for init purpose */ | |
if(sd_enable == on) | |
return; | |
- | |
- mutex_lock(&sd_mutex); | |
- if(on) | |
- imx233_ssp_start(SD_SSP); | |
- else | |
- imx233_ssp_stop(SD_SSP); | |
- mutex_unlock(&sd_mutex); | |
+ | |
sd_enable = on; | |
} | |
+#define MCI_NO_RESP 0 | |
+#define MCI_RESP (1<<0) | |
+#define MCI_LONG_RESP (1<<1) | |
+#define MCI_ACMD (1<<2) | |
+#define MCI_NOCRC (1<<3) | |
+ | |
+static bool send_cmd(uint8_t cmd, uint32_t arg, uint32_t flags, uint32_t *resp) | |
+{ | |
+ if((flags & MCI_ACMD) && !send_cmd(SD_APP_CMD, card_info.rca, MCI_RESP, resp)) | |
+ return false; | |
+ | |
+ enum imx233_ssp_resp_t resp_type = (flags & MCI_LONG_RESP) ? SSP_LONG_RESP : | |
+ (flags & MCI_RESP) ? SSP_SHORT_RESP : SSP_NO_RESP; | |
+ enum imx233_ssp_error_t ret = imx233_ssp_sd_mmc_transfer(SD_SSP, cmd, arg, | |
+ resp_type, NULL, 0, false, false, resp); | |
+ return ret == SSP_SUCCESS; | |
+} | |
+ | |
+static int sd_wait_for_tran_state(void) | |
+{ | |
+ unsigned long response; | |
+ unsigned int timeout = current_tick + 5*HZ; | |
+ int cmd_retry = 10; | |
+ | |
+ while (1) | |
+ { | |
+ while(!send_cmd(SD_SEND_STATUS, card_info.rca, SSP_SHORT_RESP, &response) && cmd_retry > 0) | |
+ cmd_retry--; | |
+ | |
+ if(cmd_retry <= 0) | |
+ return -1; | |
+ | |
+ if(((response >> 9) & 0xf) == SD_TRAN) | |
+ return 0; | |
+ | |
+ if(TIME_AFTER(current_tick, timeout)) | |
+ return -10 * ((response >> 9) & 0xf); | |
+ | |
+ last_disk_activity = current_tick; | |
+ } | |
+} | |
+ | |
static int sd_init_card(void) | |
{ | |
+ sd_power(false); | |
+ sd_power(true); | |
+ sd_enable(true); | |
imx233_ssp_start(SD_SSP); | |
imx233_ssp_softreset(SD_SSP); | |
imx233_ssp_set_mode(SD_SSP, HW_SSP_CTRL1__SSP_MODE__SD_MMC); | |
/* SSPCLK @ 96MHz | |
* gives bitrate of 96000 / 240 / 1 = 400kHz */ | |
imx233_ssp_set_timings(SD_SSP, 240, 0, 0xffff); | |
+ | |
+ imx233_ssp_sd_mmc_power_up_sequence(SD_SSP); | |
imx233_ssp_set_bus_width(SD_SSP, 1); | |
imx233_ssp_set_block_size(SD_SSP, 9); | |
card_info.rca = 0; | |
- bool is_v2 = false; | |
+ bool sd_v2 = false; | |
uint32_t resp; | |
+ long init_timeout; | |
/* go to idle state */ | |
- int ret = imx233_ssp_sd_mmc_transfer(SD_SSP, SD_GO_IDLE_STATE, 0, SSP_NO_RESP, NULL, 0, false, false, NULL); | |
- if(ret != 0) | |
+ if(!send_cmd(SD_GO_IDLE_STATE, 0, MCI_NO_RESP, NULL)) | |
return -1; | |
/* CMD8 Check for v2 sd card. Must be sent before using ACMD41 | |
- Non v2 cards will not respond to this command*/ | |
- ret = imx233_ssp_sd_mmc_transfer(SD_SSP, SD_SEND_IF_COND, 0x1AA, SSP_SHORT_RESP, NULL, 0, false, false, &resp); | |
- if(ret == 0 && (resp & 0xFFF) == 0x1AA) | |
- is_v2 = true; | |
+ Non v2 cards will not respond to this command */ | |
+ if(send_cmd(SD_SEND_IF_COND, 0x1AA, MCI_RESP, &resp)) | |
+ if((resp & 0xFFF) == 0x1AA) | |
+ sd_v2 = true; | |
+ /* timeout for initialization is 1sec, from SD Specification 2.00 */ | |
+ init_timeout = current_tick + HZ; | |
+ do | |
+ { | |
+ /* this timeout is the only valid error for this loop*/ | |
+ if(TIME_AFTER(current_tick, init_timeout)) | |
+ return -2; | |
+ | |
+ /* ACMD41 For v2 cards set HCS bit[30] & send host voltage range to all */ | |
+ if(!send_cmd(SD_APP_OP_COND, (0x00FF8000 | (sd_v2 ? 1<<30 : 0)), | |
+ MCI_ACMD|MCI_NOCRC|MCI_RESP, &card_info.ocr)) | |
+ return -100; | |
+ } while(!(card_info.ocr & (1<<31))); | |
+ | |
+ /* CMD2 send CID */ | |
+ if(!send_cmd(SD_ALL_SEND_CID, 0, MCI_RESP|MCI_LONG_RESP, card_info.cid)) | |
+ return -3; | |
+ | |
+ /* CMD3 send RCA */ | |
+ if(!send_cmd(SD_SEND_RELATIVE_ADDR, 0, MCI_RESP, &card_info.rca)) | |
+ return -4; | |
+ | |
+ /* Try to switch V2 cards to HS timings, non HS seem to ignore this */ | |
+ if(sd_v2) | |
+ { | |
+ /* CMD7 w/rca: Select card to put it in TRAN state */ | |
+ if(!send_cmd(SD_SELECT_CARD, card_info.rca, MCI_RESP, NULL)) | |
+ return -5; | |
+ | |
+ if(sd_wait_for_tran_state()) | |
+ return -6; | |
+ | |
+ /* CMD6 */ | |
+ if(!send_cmd(SD_SWITCH_FUNC, 0x80fffff1, MCI_NO_RESP, NULL)) | |
+ return -7; | |
+ sleep(HZ/10); | |
+ | |
+ /* go back to STBY state so we can read csd */ | |
+ /* CMD7 w/rca=0: Deselect card to put it in STBY state */ | |
+ if(!send_cmd(SD_DESELECT_CARD, 0, MCI_NO_RESP, NULL)) | |
+ return -8; | |
+ } | |
+ | |
+ /* CMD9 send CSD */ | |
+ if(!send_cmd(SD_SEND_CSD, card_info.rca, MCI_RESP|MCI_LONG_RESP, card_info.csd)) | |
+ return -9; | |
+ | |
+ sd_parse_csd(&card_info); | |
+ | |
+ /* SSPCLK @ 96MHz | |
+ * gives bitrate of 96 / 4 / 1 = 24MHz */ | |
+ imx233_ssp_set_timings(SD_SSP, 4, 0, 0xffff); | |
+ | |
+ /* CMD7 w/rca: Select card to put it in TRAN state */ | |
+ if(!send_cmd(SD_SELECT_CARD, card_info.rca, MCI_RESP, &resp)) | |
+ return -12; | |
+ if(sd_wait_for_tran_state() < 0) | |
+ return -13; | |
+ | |
+ /* ACMD6: set bus width to 4-bit */ | |
+ if(!send_cmd(SD_SET_BUS_WIDTH, 2, MCI_RESP|MCI_ACMD, &resp)) | |
+ return -15; | |
+ /* ACMD42: disconnect the pull-up resistor on CD/DAT3 */ | |
+ if(!send_cmd(SD_SET_CLR_CARD_DETECT, 0, MCI_RESP|MCI_ACMD, &resp)) | |
+ return -17; | |
+ | |
+ /* Switch to 4-bit */ | |
+ imx233_ssp_set_bus_width(SD_SSP, 4); | |
+ | |
+ card_info.initialized = 1; | |
- return -10; | |
+ return 0; | |
} | |
static void sd_thread(void) NORETURN_ATTR; | |
@@ -117,6 +242,7 @@ static void sd_thread(void) | |
case SYS_HOTSWAP_INSERTED: | |
case SYS_HOTSWAP_EXTRACTED: | |
{ | |
+ int microsd_init = 1; | |
fat_lock(); /* lock-out FAT activity first - | |
prevent deadlocking via disk_mount that | |
would cause a reverse-order attempt with | |
@@ -135,29 +261,25 @@ static void sd_thread(void) | |
if(ev.id == SYS_HOTSWAP_INSERTED) | |
{ | |
- int ret = sd_init_card(); | |
- if(ret == 0) | |
- { | |
- ret = disk_mount(sd_first_drive); /* 0 if fail */ | |
- if(ret < 0) | |
- DEBUGF("disk_mount failed: %d", ret); | |
- } | |
- else | |
- DEBUGF("sd_init_card failed: %d", ret); | |
- } | |
+ microsd_init = sd_init_card(); | |
+ if(microsd_init < 0) /* initialisation failed */ | |
+ panicf("microSD init failed : %d", microsd_init); | |
+ microsd_init = disk_mount(sd_first_drive); /* 0 if fail */ | |
+ } | |
/* | |
* Mount succeeded, or this was an EXTRACTED event, | |
* in both cases notify the system about the changed filesystems | |
*/ | |
- if(card_info.initialized) | |
+ if(microsd_init) | |
queue_broadcast(SYS_FS_CHANGED, 0); | |
+ sd_enable(false); | |
/* Access is now safe */ | |
mutex_unlock(&sd_mutex); | |
fat_unlock(); | |
- } | |
break; | |
+ } | |
case SYS_TIMEOUT: | |
if(!TIME_BEFORE(current_tick, last_disk_activity+(3*HZ))) | |
sd_enable(false); | |
@@ -177,42 +299,87 @@ int sd_init(void) | |
queue_init(&sd_queue, true); | |
create_thread(sd_thread, sd_stack, sizeof(sd_stack), 0, | |
sd_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) IF_COP(, CPU)); | |
+ sd_enable(false); | |
+ imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback, true); | |
+ | |
+ return 0; | |
+} | |
- #ifdef SANSA_FUZEPLUS | |
- imx233_ssp_setup_ssp1_sd_mmc_pins(true, 4, PINCTRL_DRIVE_8mA, false); | |
- #endif | |
- imx233_ssp_sdmmc_setup_detect(SD_SSP, true, sd_detect_callback); | |
+static int transfer_sectors(IF_MD2(int drive,) unsigned long start, int count, void *buf, bool read) | |
+{ | |
+ IF_MD((void) drive); | |
+ int ret = 0; | |
+ uint32_t resp; | |
- if(imx233_ssp_sdmmc_detect(SD_SSP)) | |
- queue_broadcast(SYS_HOTSWAP_INSERTED, 0); | |
+ last_disk_activity = current_tick; | |
+ | |
+ mutex_lock(&sd_mutex); | |
+ sd_enable(true); | |
+ | |
+ if(card_info.initialized <= 0) | |
+ { | |
+ panicf("not init, this will lock up"); | |
+ ret = sd_init_card(); | |
+ if(card_info.initialized <= 0) | |
+ goto Lend; | |
+ } | |
- return 0; | |
+ if(!send_cmd(SD_SELECT_CARD, card_info.rca, SSP_NO_RESP, NULL)) | |
+ { | |
+ ret = -20; | |
+ goto Lend; | |
+ } | |
+ ret = sd_wait_for_tran_state(); | |
+ if(ret < 0) | |
+ goto Ldeselect; | |
+ | |
+ while(count != 0) | |
+ { | |
+ int this_count = MIN(count, IMX233_MAX_SSP_XFER_SIZE / 512); | |
+ /* Set bank_start to the correct unit (blocks or bytes) */ | |
+ int bank_start = start; | |
+ if(!(card_info.ocr & (1<<30))) /* not SDHC */ | |
+ bank_start *= SD_BLOCK_SIZE; | |
+ ret = imx233_ssp_sd_mmc_transfer(SD_SSP, read ? SD_READ_MULTIPLE_BLOCK : SD_WRITE_MULTIPLE_BLOCK, | |
+ bank_start, SSP_SHORT_RESP, buf, this_count, false, read, &resp); | |
+ if(ret != SSP_SUCCESS) | |
+ break; | |
+ if(!send_cmd(SD_STOP_TRANSMISSION, 0, SSP_SHORT_RESP, &resp)) | |
+ { | |
+ ret = -15; | |
+ break; | |
+ } | |
+ count -= this_count; | |
+ start += this_count; | |
+ buf += this_count * 512; | |
+ } | |
+ | |
+ Ldeselect: | |
+ /* CMD7 w/rca =0 : deselects card & puts it in STBY state */ | |
+ if(!send_cmd(SD_DESELECT_CARD, 0, SSP_NO_RESP, NULL)) | |
+ ret = -23; | |
+ | |
+ Lend: | |
+ mutex_unlock(&sd_mutex); | |
+ return ret; | |
} | |
int sd_read_sectors(IF_MD2(int drive,) unsigned long start, int count, | |
void* buf) | |
{ | |
- IF_MD((void) drive); | |
- (void) start; | |
- (void) count; | |
- (void) buf; | |
- return -1; | |
+ return transfer_sectors(IF_MD2(drive,) start, count, buf, true); | |
} | |
int sd_write_sectors(IF_MD2(int drive,) unsigned long start, int count, | |
const void* buf) | |
{ | |
- IF_MD((void) drive); | |
- (void) start; | |
- (void) count; | |
- (void) buf; | |
- return -1; | |
+ return transfer_sectors(IF_MD2(drive,) start, count, (void *)buf, false); | |
} | |
tCardInfo *card_get_info_target(int card_no) | |
{ | |
(void)card_no; | |
- return NULL; | |
+ return &card_info; | |
} | |
int sd_num_drives(int first_drive) | |
@@ -235,6 +402,6 @@ bool sd_removable(IF_MD(int drive)) | |
long sd_last_disk_activity(void) | |
{ | |
- return 0; | |
+ return last_disk_activity; | |
} | |
diff --git a/firmware/target/arm/imx233/ssp-imx233.c b/firmware/target/arm/imx233/ssp-imx233.c | |
index 21d5658..eb7a3a2 100644 | |
--- a/firmware/target/arm/imx233/ssp-imx233.c | |
+++ b/firmware/target/arm/imx233/ssp-imx233.c | |
@@ -38,7 +38,8 @@ struct ssp_dma_command_t | |
uint32_t cmd1; | |
}; | |
-static int ssp_in_use = 0; | |
+static bool ssp_in_use[2]; | |
+static int ssp_nr_in_use = 0; | |
static struct mutex ssp_mutex[2]; | |
static struct semaphore ssp_sema[2]; | |
static struct ssp_dma_command_t ssp_dma_cmd[2]; | |
@@ -82,7 +83,7 @@ void imx233_ssp_init(void) | |
__REG_SET(HW_SSP_CTRL0(1)) = __BLOCK_CLKGATE; | |
__REG_SET(HW_SSP_CTRL0(2)) = __BLOCK_CLKGATE; | |
- ssp_in_use = 0; | |
+ ssp_nr_in_use = 0; | |
semaphore_init(&ssp_sema[0], 1, 0); | |
semaphore_init(&ssp_sema[1], 1, 0); | |
mutex_init(&ssp_mutex[0]); | |
@@ -92,12 +93,15 @@ void imx233_ssp_init(void) | |
void imx233_ssp_start(int ssp) | |
{ | |
+ if(ssp_in_use[ssp - 1]) | |
+ return; | |
+ ssp_in_use[ssp - 1] = true; | |
/* Gate block */ | |
imx233_reset_block(&HW_SSP_CTRL0(ssp)); | |
/* Gate dma channel */ | |
imx233_dma_clkgate_channel(APB_SSP(ssp), true); | |
/* If first block to start, start SSP clock */ | |
- if(ssp_in_use == 0) | |
+ if(ssp_nr_in_use == 0) | |
{ | |
/** 2.3.1: the clk_ssp maximum frequency is 102.858 MHz */ | |
/* fracdiv = 18 => clk_io = pll = 480Mhz | |
@@ -108,18 +112,21 @@ void imx233_ssp_start(int ssp) | |
imx233_set_bypass_pll(CLK_SSP, false); /* use IO */ | |
imx233_enable_clock(CLK_SSP, true); | |
} | |
- ssp_in_use++; | |
+ ssp_nr_in_use++; | |
} | |
void imx233_ssp_stop(int ssp) | |
{ | |
+ if(!ssp_in_use[ssp - 1]) | |
+ return; | |
+ ssp_in_use[ssp - 1] = false; | |
/* Gate off */ | |
__REG_SET(HW_SSP_CTRL0(ssp)) = __BLOCK_CLKGATE; | |
/* Gate off dma */ | |
imx233_dma_clkgate_channel(APB_SSP(ssp), false); | |
/* If last block to stop, stop SSP clock */ | |
- ssp_in_use--; | |
- if(ssp_in_use == 0) | |
+ ssp_nr_in_use--; | |
+ if(ssp_nr_in_use == 0) | |
{ | |
imx233_enable_clock(CLK_SSP, false); | |
imx233_set_fractional_divisor(CLK_IO, 0); | |
@@ -137,24 +144,6 @@ void imx233_ssp_set_timings(int ssp, int divide, int rate, int timeout) | |
timeout << HW_SSP_TIMING__CLOCK_TIMEOUT_BP; | |
} | |
-#if 0 | |
-static void setup_ssp_sd_pins(int ssp) | |
-{ | |
- imx233_set_pin_function(1, 29, PINCTRL_FUNCTION_GPIO); | |
- imx233_enable_gpio_output(1, 29, true); | |
- imx233_set_gpio_output(1, 29, false); | |
- | |
- if(ssp == 1) | |
- { | |
- | |
- } | |
- else | |
- { | |
- | |
- } | |
-} | |
-#endif | |
- | |
void imx233_ssp_setup_ssp1_sd_mmc_pins(bool enable_pullups, unsigned bus_width, | |
unsigned drive_strength, bool use_alt) | |
{ | |
@@ -352,7 +341,7 @@ static void detect_irq(int bank, int pin) | |
timeout_register(&ssp2_detect_oneshot, ssp2_detect_oneshot_callback, (3*HZ/10), 0); | |
} | |
-void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn) | |
+void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn, bool first_time) | |
{ | |
int bank = ssp == 1 ? 2 : 0; | |
int pin = ssp == 1 ? 1 : 19; | |
@@ -362,6 +351,8 @@ void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn) | |
imx233_set_pin_function(bank, pin, PINCTRL_FUNCTION_GPIO); | |
imx233_enable_gpio_output(bank, pin, false); | |
} | |
+ if(first_time && imx233_ssp_sdmmc_detect(ssp)) | |
+ detect_irq(bank, pin); | |
imx233_setup_pin_irq(bank, pin, enable, true, !imx233_ssp_sdmmc_detect(ssp), detect_irq); | |
} | |
diff --git a/firmware/target/arm/imx233/ssp-imx233.h b/firmware/target/arm/imx233/ssp-imx233.h | |
index 42aa255..a463c04 100644 | |
--- a/firmware/target/arm/imx233/ssp-imx233.h | |
+++ b/firmware/target/arm/imx233/ssp-imx233.h | |
@@ -168,8 +168,10 @@ void imx233_ssp_setup_ssp1_sd_mmc_pins(bool enable_pullups, unsigned bus_width, | |
void imx233_ssp_setup_ssp2_sd_mmc_pins(bool enable_pullups, unsigned bus_width, | |
unsigned drive_strength); | |
/* after callback is fired, imx233_ssp_sdmmc_setup_detect needs to be called | |
- * to enable detection again */ | |
-void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn); | |
+ * to enable detection again. If first_time is true, the callback will | |
+ * be called if the sd card is inserted when the function is called, otherwise | |
+ * it will be called on the next insertion change. */ | |
+void imx233_ssp_sdmmc_setup_detect(int ssp, bool enable, ssp_detect_cb_t fn, bool first_time); | |
bool imx233_ssp_sdmmc_detect(int ssp); | |
/* SD/MMC requires that the card be provided the clock during an init sequence of | |
* at least 1msec (or 74 clocks). Does NOT touch the clock so it has to be correct. */ | |
diff --git a/firmware/target/arm/system-arm.c b/firmware/target/arm/system-arm.c | |
index 23ccfd1..4c32801 100644 | |
--- a/firmware/target/arm/system-arm.c | |
+++ b/firmware/target/arm/system-arm.c | |
@@ -25,6 +25,9 @@ | |
#include "font.h" | |
#include "gcc_extensions.h" | |
+#include "unwarminder/get_sp.h" | |
+#include "unwarminder/client.h" | |
+ | |
static const char* const uiename[] = { | |
"Undefined instruction", | |
"Prefetch abort", | |
@@ -49,9 +52,7 @@ void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) | |
lcd_setfont(FONT_SYSFIXED); | |
lcd_set_viewport(NULL); | |
lcd_clear_display(); | |
- lcd_puts(0, line++, uiename[num]); | |
- lcd_putsf(0, line++, "at %08x" IF_COP(" (%d)"), pc | |
- IF_COP(, CURRENT_CORE)); | |
+ lcd_putsf(0, line++, "%s at %08x" IF_COP(" (%d)"), uiename[num], pc IF_COP(, CURRENT_CORE)); | |
#if !defined(CPU_ARM7TDMI) && (CONFIG_CPU != RK27XX) /* arm7tdmi has no MPU/MMU */ | |
if(num == 1 || num == 2) /* prefetch / data abort */ | |
@@ -88,6 +89,7 @@ void NORETURN_ATTR UIE(unsigned int pc, unsigned int num) | |
} /* num == 1 || num == 2 // prefetch/data abort */ | |
#endif /* !defined(CPU_ARM7TDMI */ | |
+ backtrace(pc, __get_sp(), &line); | |
lcd_update(); | |
disable_interrupt(IRQ_FIQ_STATUS); | |
diff --git a/firmware/target/arm/unwarminder/client.c b/firmware/target/arm/unwarminder/client.c | |
new file mode 100644 | |
index 0000000..3a6430b | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/client.c | |
@@ -0,0 +1,119 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Unwinder client that reads local memory. | |
+ * This client reads from local memory and is designed to run on target | |
+ * along with the unwinder. Memory read requests are implemented by | |
+ * casting a point to read the memory directly, although checks for | |
+ * alignment should probably also be made if this is to be used in | |
+ * production code, as otherwise the ARM may return the memory in a | |
+ * rotated/rolled format, or the MMU may generate an alignment exception | |
+ * if present and so configured. | |
+ **************************************************************************/ | |
+ | |
+/*************************************************************************** | |
+ * Includes | |
+ ***************************************************************************/ | |
+ | |
+#include "client.h" | |
+ | |
+/*************************************************************************** | |
+ * Prototypes | |
+ ***************************************************************************/ | |
+ | |
+static Boolean CliReport(void *data, Int32 address); | |
+static Boolean CliReadW(Int32 a, Int32 *v); | |
+static Boolean CliReadH(Int32 a, Int16 *v); | |
+static Boolean CliReadB(Int32 a, Int8 *v); | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ ***************************************************************************/ | |
+ | |
+/* Table of function pointers for passing to the unwinder */ | |
+const UnwindCallbacks cliCallbacks = | |
+ { | |
+ CliReport, | |
+ CliReadW, | |
+ CliReadH, | |
+ CliReadB | |
+#if defined(UNW_DEBUG) | |
+ ,printf | |
+#endif | |
+ }; | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Callbacks | |
+ ***************************************************************************/ | |
+ | |
+/*************************************************************************** | |
+ * | |
+ * Function: CliReport | |
+ * | |
+ * Parameters: data - Pointer to data passed to UnwindStart() | |
+ * address - The return address of a stack frame. | |
+ * | |
+ * Returns: TRUE if unwinding should continue, otherwise FALSE to | |
+ * indicate that unwinding should stop. | |
+ * | |
+ * Description: This function is called from the unwinder each time a stack | |
+ * frame has been unwound. The LSB of address indicates if | |
+ * the processor is in ARM mode (LSB clear) or Thumb (LSB | |
+ * set). | |
+ * | |
+ ***************************************************************************/ | |
+static Boolean CliReport(void *data, Int32 address) | |
+{ | |
+ CliStack *s = (CliStack *)data; | |
+ | |
+#if defined(UNW_DEBUG) | |
+ lcd_putsf(0, 10, "CliReport: 0x%08x\n", address); | |
+ lcd_update(); | |
+#endif | |
+ | |
+ s->address[s->frameCount] = address; | |
+ s->frameCount++; | |
+ | |
+ if(s->frameCount >= (sizeof(s->address) / sizeof(s->address[0]))) | |
+ { | |
+ return FALSE; | |
+ } | |
+ else | |
+ { | |
+ return TRUE; | |
+ } | |
+} | |
+ | |
+static Boolean CliReadW(const Int32 a, Int32 *v) | |
+{ | |
+ *v = *(Int32 *)a; | |
+ return TRUE; | |
+} | |
+ | |
+static Boolean CliReadH(const Int32 a, Int16 *v) | |
+{ | |
+ *v = *(Int16 *)a; | |
+ return TRUE; | |
+} | |
+ | |
+static Boolean CliReadB(const Int32 a, Int8 *v) | |
+{ | |
+ *v = *(Int8 *)a; | |
+ return TRUE; | |
+} | |
+ | |
+Boolean CliInvalidateW(const Int32 a) | |
+{ | |
+ *(Int32 *)a = 0xdeadbeef; | |
+ return TRUE; | |
+} | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/client.h b/firmware/target/arm/unwarminder/client.h | |
new file mode 100644 | |
index 0000000..68bb991 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/client.h | |
@@ -0,0 +1,85 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Unwinder client that reads local memory. | |
+ **************************************************************************/ | |
+ | |
+#ifndef CLIENT_H | |
+#define CLIENT_H | |
+ | |
+/*************************************************************************** | |
+ * Nested Includes | |
+ ***************************************************************************/ | |
+#include "config.h" | |
+#include "system.h" | |
+#include "lcd.h" | |
+ | |
+#include <stdio.h> | |
+#include "unwarminder.h" | |
+#include "get_sp.h" | |
+#include "gcc_extensions.h" | |
+ | |
+#if defined(SIM_CLIENT) | |
+#error This file is not for the simulated unwinder client | |
+#endif | |
+ | |
+/*************************************************************************** | |
+ * Typedefs | |
+ ***************************************************************************/ | |
+ | |
+/** Example structure for holding unwind results. | |
+ */ | |
+typedef struct | |
+{ | |
+ Int16 frameCount; | |
+ Int32 address[32]; | |
+} | |
+CliStack; | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ ***************************************************************************/ | |
+ | |
+extern const UnwindCallbacks cliCallbacks; | |
+ | |
+static inline const char *backtrace_symbol_name(uint32_t addr) | |
+{ | |
+ return ""; | |
+} | |
+ | |
+static inline void backtrace(int pcAddr, int spAddr, unsigned *line) | |
+{ | |
+ CliStack results; | |
+ Int8 t; | |
+ UnwResult r; | |
+ | |
+ results.frameCount = 0; | |
+ r = UnwindStart(pcAddr, spAddr, &cliCallbacks, &results); | |
+ | |
+ | |
+ lcd_putsf(0, (*line)++, "%s pc: 0x%08x, sp: 0x%08x", "backtrace start", pcAddr, spAddr); | |
+ | |
+ for (t=0; t<results.frameCount; t++) | |
+ { | |
+ const char *name = backtrace_symbol_name(results.address[t]); | |
+ lcd_putsf(0, (*line)++, " %c: 0x%08x %s", | |
+ (results.address[t] & 0x1) ? 'T' : 'A', | |
+ results.address[t] & (~0x1), | |
+ name); | |
+ } | |
+ | |
+ lcd_puts(0, (*line)++, "backtrace end"); | |
+ lcd_update(); | |
+} | |
+ | |
+#endif | |
+ | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/get_sp.c b/firmware/target/arm/unwarminder/get_sp.c | |
new file mode 100644 | |
index 0000000..a316af1 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/get_sp.c | |
@@ -0,0 +1,27 @@ | |
+unsigned int __get_sp(void) | |
+{ | |
+ unsigned int result; | |
+ unsigned long cpsr_save, mode; | |
+ | |
+ asm volatile ( | |
+ "mrs %[cpsr_save], cpsr \n" /* save current state */ | |
+ "orr %[mode], %[cpsr_save], #0xc0 \n" | |
+ "msr cpsr, %[mode] \n" /* disable IRQ and FIQ */ | |
+ "and %[mode], %[cpsr_save], #0x1f \n" /* get current mode */ | |
+ "cmp %[mode], #0x1f \n" /* are we in sys mode? */ | |
+ "beq get_sp \n" | |
+ "call_from_exception: \n" | |
+ "mrs %[mode], spsr \n" /* get saved state */ | |
+ "and %[mode], %[mode], #0x1f \n" /* get mode bits */ | |
+ "orr %[mode], %[mode], #0xc0 \n" /* no FIQ no IRQ */ | |
+ "msr cpsr, %[mode] \n" /* change mode */ | |
+ "get_sp: \n" | |
+ "mov %[result], sp \n" /* get SP */ | |
+ "msr cpsr, %[cpsr_save] \n" /* restore mode */ | |
+ : [result] "=r" (result), | |
+ [cpsr_save] "=r" (cpsr_save), | |
+ [mode] "=r" (mode) | |
+ ); | |
+ | |
+ return result; | |
+} | |
diff --git a/firmware/target/arm/unwarminder/get_sp.h b/firmware/target/arm/unwarminder/get_sp.h | |
new file mode 100644 | |
index 0000000..a8c965f | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/get_sp.h | |
@@ -0,0 +1 @@ | |
+int __get_sp(void); | |
diff --git a/firmware/target/arm/unwarminder/types.h b/firmware/target/arm/unwarminder/types.h | |
new file mode 100644 | |
index 0000000..2e902f3 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/types.h | |
@@ -0,0 +1,39 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ **************************************************************************/ | |
+/** \file | |
+ * Types common across the whole system. | |
+ **************************************************************************/ | |
+ | |
+#ifndef TYPES_H | |
+#define TYPES_H | |
+ | |
+#define UPGRADE_ARM_STACK_UNWIND | |
+#undef UNW_DEBUG | |
+ | |
+typedef unsigned char Int8; | |
+typedef unsigned short Int16; | |
+typedef unsigned int Int32; | |
+ | |
+ | |
+typedef signed char SignedInt8; | |
+typedef signed short SignedInt16; | |
+typedef signed int SignedInt32; | |
+ | |
+ | |
+typedef enum | |
+{ | |
+ FALSE, | |
+ TRUE | |
+} Boolean; | |
+ | |
+#endif | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/unwarm.c b/firmware/target/arm/unwarminder/unwarm.c | |
new file mode 100644 | |
index 0000000..99f6a12 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarm.c | |
@@ -0,0 +1,183 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Utility functions and glue for ARM unwinding sub-modules. | |
+ **************************************************************************/ | |
+ | |
+#define MODULE_NAME "UNWARM" | |
+ | |
+/*************************************************************************** | |
+ * Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#include <stdio.h> | |
+#include <stdarg.h> | |
+#include <string.h> | |
+#include "unwarm.h" | |
+#include "unwarmmem.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Local Functions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Global Functions | |
+ **************************************************************************/ | |
+ | |
+#if defined(UNW_DEBUG) | |
+/** Printf wrapper. | |
+ * This is used such that alternative outputs for any output can be selected | |
+ * by modification of this wrapper function. | |
+ */ | |
+void UnwPrintf(const char *format, ...) | |
+{ | |
+ va_list args; | |
+ | |
+ va_start( args, format ); | |
+ vprintf(format, args ); | |
+} | |
+#endif | |
+ | |
+/** Invalidate all general purpose registers. | |
+ */ | |
+void UnwInvalidateRegisterFile(RegData *regFile) | |
+{ | |
+ Int8 t = 0; | |
+ | |
+ do | |
+ { | |
+ regFile[t].o = REG_VAL_INVALID; | |
+ t++; | |
+ } | |
+ while(t < 13); | |
+ | |
+} | |
+ | |
+ | |
+/** Initialise the data used for unwinding. | |
+ */ | |
+void UnwInitState(UnwState * const state, /**< Pointer to structure to fill. */ | |
+ const UnwindCallbacks *cb, /**< Callbacks. */ | |
+ void *rptData, /**< Data to pass to report function. */ | |
+ Int32 pcValue, /**< PC at which to start unwinding. */ | |
+ Int32 spValue) /**< SP at which to start unwinding. */ | |
+{ | |
+ UnwInvalidateRegisterFile(state->regData); | |
+ | |
+ /* Store the pointer to the callbacks */ | |
+ state->cb = cb; | |
+ state->reportData = rptData; | |
+ | |
+ /* Setup the SP and PC */ | |
+ state->regData[13].v = spValue; | |
+ state->regData[13].o = REG_VAL_FROM_CONST; | |
+ state->regData[15].v = pcValue; | |
+ state->regData[15].o = REG_VAL_FROM_CONST; | |
+ | |
+ UnwPrintd3("\nInitial: PC=0x%08x SP=0x%08x\n", pcValue, spValue); | |
+ | |
+ /* Invalidate all memory addresses */ | |
+ memset(state->memData.used, 0, sizeof(state->memData.used)); | |
+} | |
+ | |
+ | |
+/** Call the report function to indicate some return address. | |
+ * This returns the value of the report function, which if TRUE | |
+ * indicates that unwinding may continue. | |
+ */ | |
+Boolean UnwReportRetAddr(UnwState * const state, Int32 addr) | |
+{ | |
+ /* Cast away const from reportData. | |
+ * The const is only to prevent the unw module modifying the data. | |
+ */ | |
+ return state->cb->report((void *)state->reportData, addr); | |
+} | |
+ | |
+ | |
+/** Write some register to memory. | |
+ * This will store some register and meta data onto the virtual stack. | |
+ * The address for the write | |
+ * \param state [in/out] The unwinding state. | |
+ * \param wAddr [in] The address at which to write the data. | |
+ * \param reg [in] The register to store. | |
+ * \return TRUE if the write was successful, FALSE otherwise. | |
+ */ | |
+Boolean UnwMemWriteRegister(UnwState * const state, | |
+ const Int32 addr, | |
+ const RegData * const reg) | |
+{ | |
+ return UnwMemHashWrite(&state->memData, | |
+ addr, | |
+ reg->v, | |
+ M_IsOriginValid(reg->o)); | |
+} | |
+ | |
+/** Read a register from memory. | |
+ * This will read a register from memory, and setup the meta data. | |
+ * If the register has been previously written to memory using | |
+ * UnwMemWriteRegister, the local hash will be used to return the | |
+ * value while respecting whether the data was valid or not. If the | |
+ * register was previously written and was invalid at that point, | |
+ * REG_VAL_INVALID will be returned in *reg. | |
+ * \param state [in] The unwinding state. | |
+ * \param addr [in] The address to read. | |
+ * \param reg [out] The result, containing the data value and the origin | |
+ * which will be REG_VAL_FROM_MEMORY, or REG_VAL_INVALID. | |
+ * \return TRUE if the address could be read and *reg has been filled in. | |
+ * FALSE is the data could not be read. | |
+ */ | |
+Boolean UnwMemReadRegister(UnwState * const state, | |
+ const Int32 addr, | |
+ RegData * const reg) | |
+{ | |
+ Boolean tracked; | |
+ | |
+ /* Check if the value can be found in the hash */ | |
+ if(UnwMemHashRead(&state->memData, addr, ®->v, &tracked)) | |
+ { | |
+ reg->o = tracked ? REG_VAL_FROM_MEMORY : REG_VAL_INVALID; | |
+ return TRUE; | |
+ } | |
+ /* Not in the hash, so read from real memory */ | |
+ else if(state->cb->readW(addr, ®->v)) | |
+ { | |
+ reg->o = REG_VAL_FROM_MEMORY; | |
+ return TRUE; | |
+ } | |
+ /* Not in the hash, and failed to read from memory */ | |
+ else | |
+ { | |
+ return FALSE; | |
+ } | |
+} | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/unwarm.h b/firmware/target/arm/unwarminder/unwarm.h | |
new file mode 100644 | |
index 0000000..d24e6b9 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarm.h | |
@@ -0,0 +1,178 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commerically or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liablity for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Internal interface between the ARM unwinding sub-modules. | |
+ **************************************************************************/ | |
+ | |
+#ifndef UNWARM_H | |
+#define UNWARM_H | |
+ | |
+/*************************************************************************** | |
+ * Nested Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#include "unwarminder.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+/** The maximum number of instructions to interpet in a function. | |
+ * Unwinding will be unconditionally stopped and UNWIND_EXHAUSTED returned | |
+ * if more than this number of instructions are interpreted in a single | |
+ * function without unwinding a stack frame. This prevents infinite loops | |
+ * or corrupted program memory from preventing unwinding from progressing. | |
+ */ | |
+#define UNW_MAX_INSTR_COUNT 1000 | |
+ | |
+/** The size of the hash used to track reads and writes to memory. | |
+ * This should be a prime value for efficiency. | |
+ */ | |
+#define MEM_HASH_SIZE 63 | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+typedef enum | |
+{ | |
+ /** Invalid value. */ | |
+ REG_VAL_INVALID = 0x00, | |
+ REG_VAL_FROM_STACK = 0x01, | |
+ REG_VAL_FROM_MEMORY = 0x02, | |
+ REG_VAL_FROM_CONST = 0x04, | |
+ REG_VAL_ARITHMETIC = 0x80 | |
+} | |
+RegValOrigin; | |
+ | |
+ | |
+/** Type for tracking information about a register. | |
+ * This stores the register value, as well as other data that helps unwinding. | |
+ */ | |
+typedef struct | |
+{ | |
+ /** The value held in the register. */ | |
+ Int32 v; | |
+ | |
+ /** The origin of the register value. | |
+ * This is used to track how the value in the register was loaded. | |
+ */ | |
+ RegValOrigin o; | |
+} | |
+RegData; | |
+ | |
+ | |
+/** Structure used to track reads and writes to memory. | |
+ * This structure is used as a hash to store a small number of writes | |
+ * to memory. | |
+ */ | |
+typedef struct | |
+{ | |
+ /** Memory contents. */ | |
+ Int32 v[MEM_HASH_SIZE]; | |
+ | |
+ /** Address at which v[n] represents. */ | |
+ Int32 a[MEM_HASH_SIZE]; | |
+ | |
+ /** Indicates whether the data in v[n] and a[n] is occupied. | |
+ * Each bit represents one hash value. | |
+ */ | |
+ Int8 used[(MEM_HASH_SIZE + 7) / 8]; | |
+ | |
+ /** Indicates whether the data in v[n] is valid. | |
+ * This allows a[n] to be set, but for v[n] to be marked as invalid. | |
+ * Specifically this is needed for when an untracked register value | |
+ * is written to memory. | |
+ */ | |
+ Int8 tracked[(MEM_HASH_SIZE + 7) / 8]; | |
+} | |
+MemData; | |
+ | |
+ | |
+/** Structure that is used to keep track of unwinding meta-data. | |
+ * This data is passed between all the unwinding functions. | |
+ */ | |
+typedef struct | |
+{ | |
+ /** The register values and meta-data. */ | |
+ RegData regData[16]; | |
+ | |
+ /** Memory tracking data. */ | |
+ MemData memData; | |
+ | |
+ /** Pointer to the callback functions */ | |
+ const UnwindCallbacks *cb; | |
+ | |
+ /** Pointer to pass to the report function. */ | |
+ const void *reportData; | |
+} | |
+UnwState; | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+#define M_IsOriginValid(v) (((v) & 0x7f) ? TRUE : FALSE) | |
+#define M_Origin2Str(v) ((v) ? "VALID" : "INVALID") | |
+ | |
+#if defined(UNW_DEBUG) | |
+#define UnwPrintd1(a) state->cb->printf(a) | |
+#define UnwPrintd2(a,b) state->cb->printf(a,b) | |
+#define UnwPrintd3(a,b,c) state->cb->printf(a,b,c) | |
+#define UnwPrintd4(a,b,c,d) state->cb->printf(a,b,c,d) | |
+#define UnwPrintd5(a,b,c,d,e) state->cb->printf(a,b,c,d,e) | |
+#define UnwPrintd6(a,b,c,d,e,f) state->cb->printf(a,b,c,d,e,f) | |
+#define UnwPrintd7(a,b,c,d,e,f,g) state->cb->printf(a,b,c,d,e,f,g) | |
+#define UnwPrintd8(a,b,c,d,e,f,g,h) state->cb->printf(a,b,c,d,e,f,g,h) | |
+#else | |
+#define UnwPrintd1(a) | |
+#define UnwPrintd2(a,b) | |
+#define UnwPrintd3(a,b,c) | |
+#define UnwPrintd4(a,b,c,d) | |
+#define UnwPrintd5(a,b,c,d,e) | |
+#define UnwPrintd6(a,b,c,d,e,f) | |
+#define UnwPrintd7(a,b,c,d,e,f,g) | |
+#define UnwPrintd8(a,b,c,d,e,f,g,h) | |
+#endif | |
+ | |
+/*************************************************************************** | |
+ * Function Prototypes | |
+ **************************************************************************/ | |
+ | |
+UnwResult UnwStartArm (UnwState * const state); | |
+ | |
+UnwResult UnwStartThumb (UnwState * const state); | |
+ | |
+void UnwInvalidateRegisterFile(RegData *regFile); | |
+ | |
+void UnwInitState (UnwState * const state, | |
+ const UnwindCallbacks *cb, | |
+ void *rptData, | |
+ Int32 pcValue, | |
+ Int32 spValue); | |
+ | |
+Boolean UnwReportRetAddr (UnwState * const state, Int32 addr); | |
+ | |
+Boolean UnwMemWriteRegister (UnwState * const state, | |
+ const Int32 addr, | |
+ const RegData * const reg); | |
+ | |
+Boolean UnwMemReadRegister (UnwState * const state, | |
+ const Int32 addr, | |
+ RegData * const reg); | |
+ | |
+void UnwMemHashGC (UnwState * const state); | |
+ | |
+#endif /* UNWARM_H */ | |
+ | |
+/* END OF FILE */ | |
+ | |
+ | |
diff --git a/firmware/target/arm/unwarminder/unwarm_arm.c b/firmware/target/arm/unwarminder/unwarm_arm.c | |
new file mode 100644 | |
index 0000000..0c41c05 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarm_arm.c | |
@@ -0,0 +1,701 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Abstract interpreter for ARM mode. | |
+ **************************************************************************/ | |
+ | |
+#define MODULE_NAME "UNWARM_ARM" | |
+ | |
+/*************************************************************************** | |
+ * Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#if defined(UPGRADE_ARM_STACK_UNWIND) | |
+#include <stdio.h> | |
+#include "unwarm.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Local Functions | |
+ **************************************************************************/ | |
+ | |
+/** Check if some instruction is a data-processing instruction. | |
+ * Decodes the passed instruction, checks if it is a data-processing and | |
+ * verifies that the parameters and operation really indicate a data- | |
+ * processing instruction. This is needed because some parts of the | |
+ * instruction space under this instruction can be extended or represent | |
+ * other operations such as MRS, MSR. | |
+ * | |
+ * \param[in] inst The instruction word. | |
+ * \retval TRUE Further decoding of the instruction indicates that this is | |
+ * a valid data-processing instruction. | |
+ * \retval FALSE This is not a data-processing instruction, | |
+ */ | |
+static Boolean isDataProc(Int32 instr) | |
+{ | |
+ Int8 opcode = (instr & 0x01e00000) >> 21; | |
+ Boolean S = (instr & 0x00100000) ? TRUE : FALSE; | |
+ | |
+ if((instr & 0xfc000000) != 0xe0000000) | |
+ { | |
+ return FALSE; | |
+ } | |
+ else if(!S && opcode >= 8 && opcode <= 11) | |
+ { | |
+ /* TST, TEQ, CMP and CMN all require S to be set */ | |
+ return FALSE; | |
+ } | |
+ else | |
+ { | |
+ return TRUE; | |
+ } | |
+} | |
+ | |
+/*************************************************************************** | |
+ * Global Functions | |
+ **************************************************************************/ | |
+ | |
+ | |
+UnwResult UnwStartArm(UnwState * const state) | |
+{ | |
+ Boolean found = FALSE; | |
+ Int16 t = UNW_MAX_INSTR_COUNT; | |
+ | |
+ do | |
+ { | |
+ Int32 instr; | |
+ | |
+ /* Attempt to read the instruction */ | |
+ if(!state->cb->readW(state->regData[15].v, &instr)) | |
+ { | |
+ return UNWIND_IREAD_W_FAIL; | |
+ } | |
+ | |
+ UnwPrintd4("A %x %x %08x:", | |
+ state->regData[13].v, state->regData[15].v, instr); | |
+ | |
+ /* Check that the PC is still on Arm alignment */ | |
+ if(state->regData[15].v & 0x3) | |
+ { | |
+ UnwPrintd1("\nError: PC misalignment\n"); | |
+ return UNWIND_INCONSISTENT; | |
+ } | |
+ | |
+ /* Check that the SP and PC have not been invalidated */ | |
+ if(!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) | |
+ { | |
+ UnwPrintd1("\nError: PC or SP invalidated\n"); | |
+ return UNWIND_INCONSISTENT; | |
+ } | |
+ | |
+ /* Branch and Exchange (BX) | |
+ * This is tested prior to data processing to prevent | |
+ * mis-interpretation as an invalid TEQ instruction. | |
+ */ | |
+ if((instr & 0xfffffff0) == 0xe12fff10) | |
+ { | |
+ Int8 rn = instr & 0xf; | |
+ | |
+ UnwPrintd4("BX r%d\t ; r%d %s\n", rn, rn, M_Origin2Str(state->regData[rn].o)); | |
+ | |
+ if(!M_IsOriginValid(state->regData[rn].o)) | |
+ { | |
+ UnwPrintd1("\nUnwind failure: BX to untracked register\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ | |
+ /* Set the new PC value */ | |
+ state->regData[15].v = state->regData[rn].v; | |
+ | |
+ /* Check if the return value is from the stack */ | |
+ if(state->regData[rn].o == REG_VAL_FROM_STACK) | |
+ { | |
+ /* Now have the return address */ | |
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v & (~0x1)); | |
+ | |
+ /* Report the return address */ | |
+ if(!UnwReportRetAddr(state, state->regData[rn].v)) | |
+ { | |
+ return UNWIND_TRUNCATED; | |
+ } | |
+ } | |
+ | |
+ /* Determine the return mode */ | |
+ if(state->regData[rn].v & 0x1) | |
+ { | |
+ /* Branching to THUMB */ | |
+ return UnwStartThumb(state); | |
+ } | |
+ else | |
+ { | |
+ /* Branch to ARM */ | |
+ | |
+ /* Account for the auto-increment which isn't needed */ | |
+ state->regData[15].v -= 4; | |
+ } | |
+ } | |
+ /* Branch */ | |
+ else if((instr & 0xff000000) == 0xea000000) | |
+ { | |
+ SignedInt32 offset = (instr & 0x00ffffff); | |
+ | |
+ /* Shift value */ | |
+ offset = offset << 2; | |
+ | |
+ /* Sign extend if needed */ | |
+ if(offset & 0x02000000) | |
+ { | |
+ offset |= 0xfc000000; | |
+ } | |
+ | |
+ UnwPrintd2("B %d\n", offset); | |
+ | |
+ /* Adjust PC */ | |
+ state->regData[15].v += offset; | |
+ | |
+ /* Account for pre-fetch, where normally the PC is 8 bytes | |
+ * ahead of the instruction just executed. | |
+ */ | |
+ state->regData[15].v += 4; | |
+ | |
+ } | |
+ /* MRS */ | |
+ else if((instr & 0xffbf0fff) == 0xe10f0000) | |
+ { | |
+#if defined(UNW_DEBUG) | |
+ Boolean R = (instr & 0x00400000) ? TRUE : FALSE; | |
+#endif | |
+ Int8 rd = (instr & 0x0000f000) >> 12; | |
+ | |
+ UnwPrintd4("MRS r%d,%s\t; r%d invalidated", rd, R ? "SPSR" : "CPSR", rd); | |
+ | |
+ /* Status registers untracked */ | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ } | |
+ /* MSR */ | |
+ else if((instr & 0xffb0f000) == 0xe120f000) | |
+ { | |
+#if defined(UNW_DEBUG) | |
+ Boolean R = (instr & 0x00400000) ? TRUE : FALSE; | |
+ | |
+ UnwPrintd2("MSR %s_?, ???", R ? "SPSR" : "CPSR"); | |
+#endif | |
+ /* Status registers untracked. | |
+ * Potentially this could change processor mode and switch | |
+ * banked registers r8-r14. Most likely is that r13 (sp) will | |
+ * be banked. However, invalidating r13 will stop unwinding | |
+ * when potentially this write is being used to disable/enable | |
+ * interrupts (a common case). Therefore no invalidation is | |
+ * performed. | |
+ */ | |
+ } | |
+ /* Data processing */ | |
+ else if(isDataProc(instr)) | |
+ { | |
+ Boolean I = (instr & 0x02000000) ? TRUE : FALSE; | |
+ Int8 opcode = (instr & 0x01e00000) >> 21; | |
+#if defined(UNW_DEBUG) | |
+ Boolean S = (instr & 0x00100000) ? TRUE : FALSE; | |
+#endif | |
+ Int8 rn = (instr & 0x000f0000) >> 16; | |
+ Int8 rd = (instr & 0x0000f000) >> 12; | |
+ Int16 operand2 = (instr & 0x00000fff); | |
+ Int32 op2val; | |
+ RegValOrigin op2origin; | |
+ | |
+ switch(opcode) | |
+ { | |
+ case 0: UnwPrintd4("AND%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 1: UnwPrintd4("EOR%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 2: UnwPrintd4("SUB%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 3: UnwPrintd4("RSB%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 4: UnwPrintd4("ADD%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 5: UnwPrintd4("ADC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 6: UnwPrintd4("SBC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 7: UnwPrintd4("RSC%s r%d,r%d,", S ? "S" : "", rd, rn); break; | |
+ case 8: UnwPrintd3("TST%s r%d,", S ? "S" : "", rn); break; | |
+ case 9: UnwPrintd3("TEQ%s r%d,", S ? "S" : "", rn); break; | |
+ case 10: UnwPrintd3("CMP%s r%d,", S ? "S" : "", rn); break; | |
+ case 11: UnwPrintd3("CMN%s r%d,", S ? "S" : "", rn); break; | |
+ case 12: UnwPrintd3("ORR%s r%d,", S ? "S" : "", rn); break; | |
+ case 13: UnwPrintd3("MOV%s r%d,", S ? "S" : "", rd); break; | |
+ case 14: UnwPrintd4("BIC%s r%d,r%d", S ? "S" : "", rd, rn); break; | |
+ case 15: UnwPrintd3("MVN%s r%d,", S ? "S" : "", rd); break; | |
+ } | |
+ | |
+ /* Decode operand 2 */ | |
+ if(I) | |
+ { | |
+ Int8 shiftDist = (operand2 & 0x0f00) >> 8; | |
+ Int8 shiftConst = (operand2 & 0x00ff); | |
+ | |
+ /* rotate const right by 2 * shiftDist */ | |
+ shiftDist *= 2; | |
+ op2val = (shiftConst >> shiftDist) | | |
+ (shiftConst << (32 - shiftDist)); | |
+ op2origin = REG_VAL_FROM_CONST; | |
+ | |
+ UnwPrintd2("#0x%x", op2val); | |
+ } | |
+ else | |
+ { | |
+ /* Register and shift */ | |
+ Int8 rm = (operand2 & 0x000f); | |
+ Int8 regShift = (operand2 & 0x0010) ? TRUE : FALSE; | |
+ Int8 shiftType = (operand2 & 0x0060) >> 5; | |
+ Int32 shiftDist; | |
+#if defined(UNW_DEBUG) | |
+ const char * const shiftMnu[4] = { "LSL", "LSR", "ASR", "ROR" }; | |
+#endif | |
+ UnwPrintd2("r%d ", rm); | |
+ | |
+ /* Get the shift distance */ | |
+ if(regShift) | |
+ { | |
+ Int8 rs = (operand2 & 0x0f00) >> 8; | |
+ | |
+ if(operand2 & 0x00800) | |
+ { | |
+ UnwPrintd1("\nError: Bit should be zero\n"); | |
+ return UNWIND_ILLEGAL_INSTR; | |
+ } | |
+ else if(rs == 15) | |
+ { | |
+ UnwPrintd1("\nError: Cannot use R15 with register shift\n"); | |
+ return UNWIND_ILLEGAL_INSTR; | |
+ } | |
+ | |
+ /* Get shift distance */ | |
+ shiftDist = state->regData[rs].v; | |
+ op2origin = state->regData[rs].o; | |
+ | |
+ UnwPrintd7("%s r%d\t; r%d %s r%d %s", | |
+ shiftMnu[shiftType], rs, | |
+ rm, M_Origin2Str(state->regData[rm].o), | |
+ rs, M_Origin2Str(state->regData[rs].o)); | |
+ } | |
+ else | |
+ { | |
+ shiftDist = (operand2 & 0x0f80) >> 7; | |
+ op2origin = REG_VAL_FROM_CONST; | |
+ | |
+ if(shiftDist) | |
+ { | |
+ UnwPrintd3("%s #%d", | |
+ shiftMnu[shiftType], shiftDist); | |
+ } | |
+ UnwPrintd3("\t; r%d %s", rm, M_Origin2Str(state->regData[rm].o)); | |
+ | |
+ } | |
+ | |
+ /* Apply the shift type to the source register */ | |
+ switch(shiftType) | |
+ { | |
+ case 0: /* logical left */ | |
+ op2val = state->regData[rm].v << shiftDist; | |
+ break; | |
+ case 1: /* logical right */ | |
+ | |
+ if(!regShift && shiftDist == 0) | |
+ { | |
+ shiftDist = 32; | |
+ } | |
+ | |
+ op2val = state->regData[rm].v >> shiftDist; | |
+ break; | |
+ case 2: /* arithmetic right */ | |
+ | |
+ if(!regShift && shiftDist == 0) | |
+ { | |
+ shiftDist = 32; | |
+ } | |
+ | |
+ if(state->regData[rm].v & 0x80000000) | |
+ { | |
+ /* Register shifts maybe greater than 32 */ | |
+ if(shiftDist >= 32) | |
+ { | |
+ op2val = 0xffffffff; | |
+ } | |
+ else | |
+ { | |
+ op2val = state->regData[rm].v >> shiftDist; | |
+ op2val |= 0xffffffff << (32 - shiftDist); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ op2val = state->regData[rm].v >> shiftDist; | |
+ } | |
+ break; | |
+ case 3: /* rotate right */ | |
+ | |
+ if(!regShift && shiftDist == 0) | |
+ { | |
+ /* Rotate right with extend. | |
+ * This uses the carry bit and so always has an | |
+ * untracked result. | |
+ */ | |
+ op2origin = REG_VAL_INVALID; | |
+ op2val = 0; | |
+ } | |
+ else | |
+ { | |
+ /* Limit shift distance to 0-31 incase of register shift */ | |
+ shiftDist &= 0x1f; | |
+ | |
+ op2val = (state->regData[rm].v >> shiftDist) | | |
+ (state->regData[rm].v << (32 - shiftDist)); | |
+ } | |
+ break; | |
+ | |
+ default: | |
+ UnwPrintd2("\nError: Invalid shift type: %d\n", shiftType); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ | |
+ /* Decide the data origin */ | |
+ if(M_IsOriginValid(op2origin) && | |
+ M_IsOriginValid(state->regData[rm].o)) | |
+ { | |
+ op2origin = state->regData[rm].o; | |
+ op2origin |= REG_VAL_ARITHMETIC; | |
+ } | |
+ else | |
+ { | |
+ op2origin = REG_VAL_INVALID; | |
+ } | |
+ | |
+ } | |
+ | |
+ /* Propagate register validity */ | |
+ switch(opcode) | |
+ { | |
+ case 0: /* AND: Rd := Op1 AND Op2 */ | |
+ case 1: /* EOR: Rd := Op1 EOR Op2 */ | |
+ case 2: /* SUB: Rd:= Op1 - Op2 */ | |
+ case 3: /* RSB: Rd:= Op2 - Op1 */ | |
+ case 4: /* ADD: Rd:= Op1 + Op2 */ | |
+ case 12: /* ORR: Rd:= Op1 OR Op2 */ | |
+ case 14: /* BIC: Rd:= Op1 AND NOT Op2 */ | |
+ if(!M_IsOriginValid(state->regData[rn].o) || | |
+ !M_IsOriginValid(op2origin)) | |
+ { | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].o = state->regData[rn].o; | |
+ state->regData[rd].o |= op2origin; | |
+ } | |
+ break; | |
+ case 5: /* ADC: Rd:= Op1 + Op2 + C */ | |
+ case 6: /* SBC: Rd:= Op1 - Op2 + C */ | |
+ case 7: /* RSC: Rd:= Op2 - Op1 + C */ | |
+ /* CPSR is not tracked */ | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ break; | |
+ | |
+ case 8: /* TST: set condition codes on Op1 AND Op2 */ | |
+ case 9: /* TEQ: set condition codes on Op1 EOR Op2 */ | |
+ case 10: /* CMP: set condition codes on Op1 - Op2 */ | |
+ case 11: /* CMN: set condition codes on Op1 + Op2 */ | |
+ break; | |
+ | |
+ | |
+ case 13: /* MOV: Rd:= Op2 */ | |
+ case 15: /* MVN: Rd:= NOT Op2 */ | |
+ state->regData[rd].o = op2origin; | |
+ break; | |
+ } | |
+ | |
+ /* Account for pre-fetch by temporarily adjusting PC */ | |
+ if(rn == 15) | |
+ { | |
+ /* If the shift amount is specified in the instruction, | |
+ * the PC will be 8 bytes ahead. If a register is used | |
+ * to specify the shift amount the PC will be 12 bytes | |
+ * ahead. | |
+ */ | |
+ if(!I && (operand2 & 0x0010)) | |
+ state->regData[rn].v += 12; | |
+ else | |
+ state->regData[rn].v += 8; | |
+ } | |
+ | |
+ /* Compute values */ | |
+ switch(opcode) | |
+ { | |
+ case 0: /* AND: Rd := Op1 AND Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v & op2val; | |
+ break; | |
+ | |
+ case 1: /* EOR: Rd := Op1 EOR Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v ^ op2val; | |
+ break; | |
+ | |
+ case 2: /* SUB: Rd:= Op1 - Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v - op2val; | |
+ break; | |
+ case 3: /* RSB: Rd:= Op2 - Op1 */ | |
+ state->regData[rd].v = op2val - state->regData[rn].v; | |
+ break; | |
+ | |
+ case 4: /* ADD: Rd:= Op1 + Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v + op2val; | |
+ break; | |
+ | |
+ case 5: /* ADC: Rd:= Op1 + Op2 + C */ | |
+ case 6: /* SBC: Rd:= Op1 - Op2 + C */ | |
+ case 7: /* RSC: Rd:= Op2 - Op1 + C */ | |
+ case 8: /* TST: set condition codes on Op1 AND Op2 */ | |
+ case 9: /* TEQ: set condition codes on Op1 EOR Op2 */ | |
+ case 10: /* CMP: set condition codes on Op1 - Op2 */ | |
+ case 11: /* CMN: set condition codes on Op1 + Op2 */ | |
+ UnwPrintd1("\t; ????"); | |
+ break; | |
+ | |
+ case 12: /* ORR: Rd:= Op1 OR Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v | op2val; | |
+ break; | |
+ | |
+ case 13: /* MOV: Rd:= Op2 */ | |
+ state->regData[rd].v = op2val; | |
+ break; | |
+ | |
+ case 14: /* BIC: Rd:= Op1 AND NOT Op2 */ | |
+ state->regData[rd].v = state->regData[rn].v & (~op2val); | |
+ break; | |
+ | |
+ case 15: /* MVN: Rd:= NOT Op2 */ | |
+ state->regData[rd].v = ~op2val; | |
+ break; | |
+ } | |
+ | |
+ /* Remove the prefetch offset from the PC */ | |
+ if(rd != 15 && rn == 15) | |
+ { | |
+ if(!I && (operand2 & 0x0010)) | |
+ state->regData[rn].v -= 12; | |
+ else | |
+ state->regData[rn].v -= 8; | |
+ } | |
+ | |
+ } | |
+ /* Block Data Transfer | |
+ * LDM, STM | |
+ */ | |
+ else if((instr & 0xfe000000) == 0xe8000000) | |
+ { | |
+ Boolean P = (instr & 0x01000000) ? TRUE : FALSE; | |
+ Boolean U = (instr & 0x00800000) ? TRUE : FALSE; | |
+ Boolean S = (instr & 0x00400000) ? TRUE : FALSE; | |
+ Boolean W = (instr & 0x00200000) ? TRUE : FALSE; | |
+ Boolean L = (instr & 0x00100000) ? TRUE : FALSE; | |
+ Int16 baseReg = (instr & 0x000f0000) >> 16; | |
+ Int16 regList = (instr & 0x0000ffff); | |
+ Int32 addr = state->regData[baseReg].v; | |
+ Boolean addrValid = M_IsOriginValid(state->regData[baseReg].o); | |
+ SignedInt8 r; | |
+ | |
+#if defined(UNW_DEBUG) | |
+ /* Display the instruction */ | |
+ if(L) | |
+ { | |
+ UnwPrintd6("LDM%c%c r%d%s, {reglist}%s\n", | |
+ P ? 'E' : 'F', | |
+ U ? 'D' : 'A', | |
+ baseReg, | |
+ W ? "!" : "", | |
+ S ? "^" : ""); | |
+ } | |
+ else | |
+ { | |
+ UnwPrintd6("STM%c%c r%d%s, {reglist}%s\n", | |
+ !P ? 'E' : 'F', | |
+ !U ? 'D' : 'A', | |
+ baseReg, | |
+ W ? "!" : "", | |
+ S ? "^" : ""); | |
+ } | |
+#endif | |
+ /* S indicates that banked registers (untracked) are used, unless | |
+ * this is a load including the PC when the S-bit indicates that | |
+ * that CPSR is loaded from SPSR (also untracked, but ignored). | |
+ */ | |
+ if(S && (!L || (regList & (0x01 << 15)) == 0)) | |
+ { | |
+ UnwPrintd1("\nError:S-bit set requiring banked registers\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ else if(baseReg == 15) | |
+ { | |
+ UnwPrintd1("\nError: r15 used as base register\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ else if(regList == 0) | |
+ { | |
+ UnwPrintd1("\nError: Register list empty\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ | |
+ /* Check if ascending or descending. | |
+ * Registers are loaded/stored in order of address. | |
+ * i.e. r0 is at the lowest address, r15 at the highest. | |
+ */ | |
+ r = U ? 0 : 15; | |
+ | |
+ do | |
+ { | |
+ /* Check if the register is to be transferred */ | |
+ if(regList & (0x01 << r)) | |
+ { | |
+ if(P) addr += U ? 4 : -4; | |
+ | |
+ if(L) | |
+ { | |
+ if(addrValid) | |
+ { | |
+ if(!UnwMemReadRegister(state, addr, &state->regData[r])) | |
+ { | |
+ return UNWIND_DREAD_W_FAIL; | |
+ } | |
+ | |
+ /* Update the origin if read via the stack pointer */ | |
+ if(M_IsOriginValid(state->regData[r].o) && baseReg == 13) | |
+ { | |
+ state->regData[r].o = REG_VAL_FROM_STACK; | |
+ } | |
+ | |
+ UnwPrintd5(" R%d = 0x%08x\t; r%d %s\n", | |
+ r, | |
+ state->regData[r].v, | |
+ r, | |
+ M_Origin2Str(state->regData[r].o)); | |
+ } | |
+ else | |
+ { | |
+ /* Invalidate the register as the base reg was invalid */ | |
+ state->regData[r].o = REG_VAL_INVALID; | |
+ | |
+ UnwPrintd2(" R%d = ???\n", r); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ if(addrValid) | |
+ { | |
+ if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) | |
+ { | |
+ return UNWIND_DWRITE_W_FAIL; | |
+ } | |
+ } | |
+ | |
+ UnwPrintd2(" R%d = 0x%08x\n", r); | |
+ } | |
+ | |
+ if(!P) addr += U ? 4 : -4; | |
+ } | |
+ | |
+ /* Check the next register */ | |
+ r += U ? 1 : -1; | |
+ } | |
+ while(r >= 0 && r <= 15); | |
+ | |
+ /* Check the writeback bit */ | |
+ if(W) state->regData[baseReg].v = addr; | |
+ | |
+ /* Check if the PC was loaded */ | |
+ if(L && (regList & (0x01 << 15))) | |
+ { | |
+ if(!M_IsOriginValid(state->regData[15].o)) | |
+ { | |
+ /* Return address is not valid */ | |
+ UnwPrintd1("PC popped with invalid address\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ else | |
+ { | |
+ /* Store the return address */ | |
+ if(!UnwReportRetAddr(state, state->regData[15].v)) | |
+ { | |
+ return UNWIND_TRUNCATED; | |
+ } | |
+ | |
+ UnwPrintd2(" Return PC=0x%x", state->regData[15].v); | |
+ | |
+ /* Determine the return mode */ | |
+ if(state->regData[15].v & 0x1) | |
+ { | |
+ /* Branching to THUMB */ | |
+ return UnwStartThumb(state); | |
+ } | |
+ else | |
+ { | |
+ /* Branch to ARM */ | |
+ | |
+ /* Account for the auto-increment which isn't needed */ | |
+ state->regData[15].v -= 4; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ else | |
+ { | |
+ UnwPrintd1("????"); | |
+ | |
+ /* Unknown/undecoded. May alter some register, so invalidate file */ | |
+ UnwInvalidateRegisterFile(state->regData); | |
+ } | |
+ | |
+ UnwPrintd1("\n"); | |
+ | |
+ /* Should never hit the reset vector */ | |
+ if(state->regData[15].v == 0) return UNWIND_RESET; | |
+ | |
+ /* Check next address */ | |
+ state->regData[15].v += 4; | |
+ | |
+ /* Garbage collect the memory hash (used only for the stack) */ | |
+ UnwMemHashGC(state); | |
+ | |
+ t--; | |
+ if(t == 0) return UNWIND_EXHAUSTED; | |
+ | |
+ } | |
+ while(!found); | |
+ | |
+ return UNWIND_UNSUPPORTED; | |
+} | |
+ | |
+#endif /* UPGRADE_ARM_STACK_UNWIND */ | |
+ | |
+/* END OF FILE */ | |
+ | |
diff --git a/firmware/target/arm/unwarminder/unwarm_thumb.c b/firmware/target/arm/unwarminder/unwarm_thumb.c | |
new file mode 100644 | |
index 0000000..09b3c9e | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarm_thumb.c | |
@@ -0,0 +1,740 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Abstract interpretation for Thumb mode. | |
+ **************************************************************************/ | |
+ | |
+#define MODULE_NAME "UNWARM_THUMB" | |
+ | |
+/*************************************************************************** | |
+ * Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#if defined(UPGRADE_ARM_STACK_UNWIND) | |
+#include <stdio.h> | |
+#include "unwarm.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Local Functions | |
+ **************************************************************************/ | |
+ | |
+/** Sign extend an 11 bit value. | |
+ * This function simply inspects bit 11 of the input \a value, and if | |
+ * set, the top 5 bits are set to give a 2's compliment signed value. | |
+ * \param value The value to sign extend. | |
+ * \return The signed-11 bit value stored in a 16bit data type. | |
+ */ | |
+static SignedInt16 signExtend11(Int16 value) | |
+{ | |
+ if(value & 0x400) | |
+ { | |
+ value |= 0xf800; | |
+ } | |
+ | |
+ return value; | |
+} | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Global Functions | |
+ **************************************************************************/ | |
+ | |
+ | |
+UnwResult UnwStartThumb(UnwState * const state) | |
+{ | |
+ Boolean found = FALSE; | |
+ Int16 t = UNW_MAX_INSTR_COUNT; | |
+ | |
+ do | |
+ { | |
+ Int16 instr; | |
+ | |
+ /* Attempt to read the instruction */ | |
+ if(!state->cb->readH(state->regData[15].v & (~0x1), &instr)) | |
+ { | |
+ return UNWIND_IREAD_H_FAIL; | |
+ } | |
+ | |
+ UnwPrintd4("T %x %x %04x:", | |
+ state->regData[13].v, state->regData[15].v, instr); | |
+ | |
+ /* Check that the PC is still on Thumb alignment */ | |
+ if(!(state->regData[15].v & 0x1)) | |
+ { | |
+ UnwPrintd1("\nError: PC misalignment\n"); | |
+ return UNWIND_INCONSISTENT; | |
+ } | |
+ | |
+ /* Check that the SP and PC have not been invalidated */ | |
+ if(!M_IsOriginValid(state->regData[13].o) || !M_IsOriginValid(state->regData[15].o)) | |
+ { | |
+ UnwPrintd1("\nError: PC or SP invalidated\n"); | |
+ return UNWIND_INCONSISTENT; | |
+ } | |
+ | |
+ /* Format 1: Move shifted register | |
+ * LSL Rd, Rs, #Offset5 | |
+ * LSR Rd, Rs, #Offset5 | |
+ * ASR Rd, Rs, #Offset5 | |
+ */ | |
+ if((instr & 0xe000) == 0x0000 && (instr & 0x1800) != 0x1800) | |
+ { | |
+ Boolean signExtend; | |
+ Int8 op = (instr & 0x1800) >> 11; | |
+ Int8 offset5 = (instr & 0x07c0) >> 6; | |
+ Int8 rs = (instr & 0x0038) >> 3; | |
+ Int8 rd = (instr & 0x0007); | |
+ | |
+ switch(op) | |
+ { | |
+ case 0: /* LSL */ | |
+ UnwPrintd6("LSL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | |
+ state->regData[rd].v = state->regData[rs].v << offset5; | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ | |
+ case 1: /* LSR */ | |
+ UnwPrintd6("LSR r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | |
+ state->regData[rd].v = state->regData[rs].v >> offset5; | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ | |
+ case 2: /* ASR */ | |
+ UnwPrintd6("ASL r%d, r%d, #%d\t; r%d %s", rd, rs, offset5, rs, M_Origin2Str(state->regData[rs].o)); | |
+ | |
+ signExtend = (state->regData[rs].v & 0x8000) ? TRUE : FALSE; | |
+ state->regData[rd].v = state->regData[rs].v >> offset5; | |
+ if(signExtend) | |
+ { | |
+ state->regData[rd].v |= 0xffffffff << (32 - offset5); | |
+ } | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ } | |
+ } | |
+ /* Format 2: add/subtract | |
+ * ADD Rd, Rs, Rn | |
+ * ADD Rd, Rs, #Offset3 | |
+ * SUB Rd, Rs, Rn | |
+ * SUB Rd, Rs, #Offset3 | |
+ */ | |
+ else if((instr & 0xf800) == 0x1800) | |
+ { | |
+ Boolean I = (instr & 0x0400) ? TRUE : FALSE; | |
+ Boolean op = (instr & 0x0200) ? TRUE : FALSE; | |
+ Int8 rn = (instr & 0x01c0) >> 6; | |
+ Int8 rs = (instr & 0x0038) >> 3; | |
+ Int8 rd = (instr & 0x0007); | |
+ | |
+ /* Print decoding */ | |
+ UnwPrintd6("%s r%d, r%d, %c%d\t;", | |
+ op ? "SUB" : "ADD", | |
+ rd, rs, | |
+ I ? '#' : 'r', | |
+ rn); | |
+ UnwPrintd5("r%d %s, r%d %s", | |
+ rd, M_Origin2Str(state->regData[rd].o), | |
+ rs, M_Origin2Str(state->regData[rs].o)); | |
+ if(!I) | |
+ { | |
+ UnwPrintd3(", r%d %s", rn, M_Origin2Str(state->regData[rn].o)); | |
+ | |
+ /* Perform calculation */ | |
+ if(op) | |
+ { | |
+ state->regData[rd].v = state->regData[rs].v - state->regData[rn].v; | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].v = state->regData[rs].v + state->regData[rn].v; | |
+ } | |
+ | |
+ /* Propagate the origin */ | |
+ if(M_IsOriginValid(state->regData[rs].v) && | |
+ M_IsOriginValid(state->regData[rn].v)) | |
+ { | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ /* Perform calculation */ | |
+ if(op) | |
+ { | |
+ state->regData[rd].v = state->regData[rs].v - rn; | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].v = state->regData[rs].v + rn; | |
+ } | |
+ | |
+ /* Propagate the origin */ | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ } | |
+ } | |
+ /* Format 3: move/compare/add/subtract immediate | |
+ * MOV Rd, #Offset8 | |
+ * CMP Rd, #Offset8 | |
+ * ADD Rd, #Offset8 | |
+ * SUB Rd, #Offset8 | |
+ */ | |
+ else if((instr & 0xe000) == 0x2000) | |
+ { | |
+ Int8 op = (instr & 0x1800) >> 11; | |
+ Int8 rd = (instr & 0x0700) >> 8; | |
+ Int8 offset8 = (instr & 0x00ff); | |
+ | |
+ switch(op) | |
+ { | |
+ case 0: /* MOV */ | |
+ UnwPrintd3("MOV r%d, #0x%x", rd, offset8); | |
+ state->regData[rd].v = offset8; | |
+ state->regData[rd].o = REG_VAL_FROM_CONST; | |
+ break; | |
+ | |
+ case 1: /* CMP */ | |
+ /* Irrelevant to unwinding */ | |
+ UnwPrintd1("CMP ???"); | |
+ break; | |
+ | |
+ case 2: /* ADD */ | |
+ UnwPrintd5("ADD r%d, #0x%x\t; r%d %s", | |
+ rd, offset8, rd, M_Origin2Str(state->regData[rd].o)); | |
+ state->regData[rd].v += offset8; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ | |
+ case 3: /* SUB */ | |
+ UnwPrintd5("SUB r%d, #0x%d\t; r%d %s", | |
+ rd, offset8, rd, M_Origin2Str(state->regData[rd].o)); | |
+ state->regData[rd].v += offset8; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ } | |
+ } | |
+ /* Format 4: ALU operations | |
+ * AND Rd, Rs | |
+ * EOR Rd, Rs | |
+ * LSL Rd, Rs | |
+ * LSR Rd, Rs | |
+ * ASR Rd, Rs | |
+ * ADC Rd, Rs | |
+ * SBC Rd, Rs | |
+ * ROR Rd, Rs | |
+ * TST Rd, Rs | |
+ * NEG Rd, Rs | |
+ * CMP Rd, Rs | |
+ * CMN Rd, Rs | |
+ * ORR Rd, Rs | |
+ * MUL Rd, Rs | |
+ * BIC Rd, Rs | |
+ * MVN Rd, Rs | |
+ */ | |
+ else if((instr & 0xfc00) == 0x4000) | |
+ { | |
+ Int8 op = (instr & 0x03c0) >> 6; | |
+ Int8 rs = (instr & 0x0038) >> 3; | |
+ Int8 rd = (instr & 0x0007); | |
+#if defined(UNW_DEBUG) | |
+ static const char * const mnu[16] = | |
+ { "AND", "EOR", "LSL", "LSR", | |
+ "ASR", "ADC", "SBC", "ROR", | |
+ "TST", "NEG", "CMP", "CMN", | |
+ "ORR", "MUL", "BIC", "MVN" }; | |
+#endif | |
+ /* Print the mnemonic and registers */ | |
+ switch(op) | |
+ { | |
+ case 0: /* AND */ | |
+ case 1: /* EOR */ | |
+ case 2: /* LSL */ | |
+ case 3: /* LSR */ | |
+ case 4: /* ASR */ | |
+ case 7: /* ROR */ | |
+ case 9: /* NEG */ | |
+ case 12: /* ORR */ | |
+ case 13: /* MUL */ | |
+ case 15: /* MVN */ | |
+ UnwPrintd8("%s r%d ,r%d\t; r%d %s, r%d %s", | |
+ mnu[op], | |
+ rd, rs, | |
+ rd, M_Origin2Str(state->regData[rd].o), | |
+ rs, M_Origin2Str(state->regData[rs].o)); | |
+ break; | |
+ | |
+ case 5: /* ADC */ | |
+ case 6: /* SBC */ | |
+ UnwPrintd4("%s r%d, r%d", mnu[op], rd, rs); | |
+ break; | |
+ | |
+ case 8: /* TST */ | |
+ case 10: /* CMP */ | |
+ case 11: /* CMN */ | |
+ /* Irrelevant to unwinding */ | |
+ UnwPrintd2("%s ???", mnu[op]); | |
+ break; | |
+ | |
+ case 14: /* BIC */ | |
+ UnwPrintd5("r%d ,r%d\t; r%d %s", | |
+ rd, rs, | |
+ rs, M_Origin2Str(state->regData[rs].o)); | |
+ state->regData[rd].v &= !state->regData[rs].v; | |
+ break; | |
+ } | |
+ | |
+ | |
+ /* Perform operation */ | |
+ switch(op) | |
+ { | |
+ case 0: /* AND */ | |
+ state->regData[rd].v &= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 1: /* EOR */ | |
+ state->regData[rd].v ^= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 2: /* LSL */ | |
+ state->regData[rd].v <<= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 3: /* LSR */ | |
+ state->regData[rd].v >>= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 4: /* ASR */ | |
+ if(state->regData[rd].v & 0x80000000) | |
+ { | |
+ state->regData[rd].v >>= state->regData[rs].v; | |
+ state->regData[rd].v |= 0xffffffff << (32 - state->regData[rs].v); | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].v >>= state->regData[rs].v; | |
+ } | |
+ | |
+ break; | |
+ | |
+ case 5: /* ADC */ | |
+ case 6: /* SBC */ | |
+ case 8: /* TST */ | |
+ case 10: /* CMP */ | |
+ case 11: /* CMN */ | |
+ break; | |
+ case 7: /* ROR */ | |
+ state->regData[rd].v = (state->regData[rd].v >> state->regData[rs].v) | | |
+ (state->regData[rd].v << (32 - state->regData[rs].v)); | |
+ break; | |
+ | |
+ case 9: /* NEG */ | |
+ state->regData[rd].v = -state->regData[rs].v; | |
+ break; | |
+ | |
+ case 12: /* ORR */ | |
+ state->regData[rd].v |= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 13: /* MUL */ | |
+ state->regData[rd].v *= state->regData[rs].v; | |
+ break; | |
+ | |
+ case 14: /* BIC */ | |
+ state->regData[rd].v &= !state->regData[rs].v; | |
+ break; | |
+ | |
+ case 15: /* MVN */ | |
+ state->regData[rd].v = !state->regData[rs].v; | |
+ break; | |
+ } | |
+ | |
+ /* Propagate data origins */ | |
+ switch(op) | |
+ { | |
+ case 0: /* AND */ | |
+ case 1: /* EOR */ | |
+ case 2: /* LSL */ | |
+ case 3: /* LSR */ | |
+ case 4: /* ASR */ | |
+ case 7: /* ROR */ | |
+ case 12: /* ORR */ | |
+ case 13: /* MUL */ | |
+ case 14: /* BIC */ | |
+ if(M_IsOriginValid(state->regData[rs].o) && M_IsOriginValid(state->regData[rs].o)) | |
+ { | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ } | |
+ else | |
+ { | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ } | |
+ break; | |
+ | |
+ case 5: /* ADC */ | |
+ case 6: /* SBC */ | |
+ /* C-bit not tracked */ | |
+ state->regData[rd].o = REG_VAL_INVALID; | |
+ break; | |
+ | |
+ case 8: /* TST */ | |
+ case 10: /* CMP */ | |
+ case 11: /* CMN */ | |
+ /* Nothing propagated */ | |
+ break; | |
+ | |
+ case 9: /* NEG */ | |
+ case 15: /* MVN */ | |
+ state->regData[rd].o = state->regData[rs].o; | |
+ state->regData[rd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ | |
+ } | |
+ | |
+ } | |
+ /* Format 5: Hi register operations/branch exchange | |
+ * ADD Rd, Hs | |
+ * ADD Hd, Rs | |
+ * ADD Hd, Hs | |
+ */ | |
+ else if((instr & 0xfc00) == 0x4400) | |
+ { | |
+ Int8 op = (instr & 0x0300) >> 8; | |
+ Boolean h1 = (instr & 0x0080) ? TRUE: FALSE; | |
+ Boolean h2 = (instr & 0x0040) ? TRUE: FALSE; | |
+ Int8 rhs = (instr & 0x0038) >> 3; | |
+ Int8 rhd = (instr & 0x0007); | |
+ | |
+ /* Adjust the register numbers */ | |
+ if(h2) rhs += 8; | |
+ if(h1) rhd += 8; | |
+ | |
+ if(op != 3 && !h1 && !h2) | |
+ { | |
+ UnwPrintd1("\nError: h1 or h2 must be set for ADD, CMP or MOV\n"); | |
+ return UNWIND_ILLEGAL_INSTR; | |
+ } | |
+ | |
+ switch(op) | |
+ { | |
+ case 0: /* ADD */ | |
+ UnwPrintd5("ADD r%d, r%d\t; r%d %s", | |
+ rhd, rhs, rhs, M_Origin2Str(state->regData[rhs].o)); | |
+ state->regData[rhd].v += state->regData[rhs].v; | |
+ state->regData[rhd].o = state->regData[rhs].o; | |
+ state->regData[rhd].o |= REG_VAL_ARITHMETIC; | |
+ break; | |
+ | |
+ case 1: /* CMP */ | |
+ /* Irrelevant to unwinding */ | |
+ UnwPrintd1("CMP ???"); | |
+ break; | |
+ | |
+ case 2: /* MOV */ | |
+ UnwPrintd5("MOV r%d, r%d\t; r%d %s", | |
+ rhd, rhs, rhd, M_Origin2Str(state->regData[rhs].o)); | |
+ state->regData[rhd].v += state->regData[rhs].v; | |
+ state->regData[rhd].o = state->regData[rhd].o; | |
+ break; | |
+ | |
+ case 3: /* BX */ | |
+ UnwPrintd4("BX r%d\t; r%d %s\n", | |
+ rhs, rhs, M_Origin2Str(state->regData[rhs].o)); | |
+ | |
+ /* Only follow BX if the data was from the stack */ | |
+ if(state->regData[rhs].o == REG_VAL_FROM_STACK) | |
+ { | |
+ UnwPrintd2(" Return PC=0x%x\n", state->regData[rhs].v & (~0x1)); | |
+ | |
+ /* Report the return address, including mode bit */ | |
+ if(!UnwReportRetAddr(state, state->regData[rhs].v)) | |
+ { | |
+ return UNWIND_TRUNCATED; | |
+ } | |
+ | |
+ /* Update the PC */ | |
+ state->regData[15].v = state->regData[rhs].v; | |
+ | |
+ /* Determine the new mode */ | |
+ if(state->regData[rhs].v & 0x1) | |
+ { | |
+ /* Branching to THUMB */ | |
+ | |
+ /* Account for the auto-increment which isn't needed */ | |
+ state->regData[15].v -= 2; | |
+ } | |
+ else | |
+ { | |
+ /* Branch to ARM */ | |
+ return UnwStartArm(state); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ UnwPrintd4("\nError: BX to invalid register: r%d = 0x%x (%s)\n", | |
+ rhs, state->regData[rhs].o, M_Origin2Str(state->regData[rhs].o)); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ } | |
+ } | |
+ /* Format 9: PC-relative load | |
+ * LDR Rd,[PC, #imm] | |
+ */ | |
+ else if((instr & 0xf800) == 0x4800) | |
+ { | |
+ Int8 rd = (instr & 0x0700) >> 8; | |
+ Int8 word8 = (instr & 0x00ff); | |
+ Int32 address; | |
+ | |
+ /* Compute load address, adding a word to account for prefetch */ | |
+ address = (state->regData[15].v & (~0x3)) + 4 + (word8 << 2); | |
+ | |
+ UnwPrintd3("LDR r%d, 0x%08x", rd, address); | |
+ | |
+ if(!UnwMemReadRegister(state, address, &state->regData[rd])) | |
+ { | |
+ return UNWIND_DREAD_W_FAIL; | |
+ } | |
+ } | |
+ /* Format 13: add offset to Stack Pointer | |
+ * ADD sp,#+imm | |
+ * ADD sp,#-imm | |
+ */ | |
+ else if((instr & 0xff00) == 0xB000) | |
+ { | |
+ Int8 value = (instr & 0x7f) * 4; | |
+ | |
+ /* Check the negative bit */ | |
+ if((instr & 0x80) != 0) | |
+ { | |
+ UnwPrintd2("SUB sp,#0x%x", value); | |
+ state->regData[13].v -= value; | |
+ } | |
+ else | |
+ { | |
+ UnwPrintd2("ADD sp,#0x%x", value); | |
+ state->regData[13].v += value; | |
+ } | |
+ } | |
+ /* Format 14: push/pop registers | |
+ * PUSH {Rlist} | |
+ * PUSH {Rlist, LR} | |
+ * POP {Rlist} | |
+ * POP {Rlist, PC} | |
+ */ | |
+ else if((instr & 0xf600) == 0xb400) | |
+ { | |
+ Boolean L = (instr & 0x0800) ? TRUE : FALSE; | |
+ Boolean R = (instr & 0x0100) ? TRUE : FALSE; | |
+ Int8 rList = (instr & 0x00ff); | |
+ | |
+ if(L) | |
+ { | |
+ Int8 r; | |
+ | |
+ /* Load from memory: POP */ | |
+ UnwPrintd2("POP {Rlist%s}\n", R ? ", PC" : ""); | |
+ | |
+ for(r = 0; r < 8; r++) | |
+ { | |
+ if(rList & (0x1 << r)) | |
+ { | |
+ /* Read the word */ | |
+ if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[r])) | |
+ { | |
+ return UNWIND_DREAD_W_FAIL; | |
+ } | |
+ | |
+ /* Alter the origin to be from the stack if it was valid */ | |
+ if(M_IsOriginValid(state->regData[r].o)) | |
+ { | |
+ state->regData[r].o = REG_VAL_FROM_STACK; | |
+ } | |
+ | |
+ state->regData[13].v += 4; | |
+ | |
+ UnwPrintd3(" r%d = 0x%08x\n", r, state->regData[r].v); | |
+ } | |
+ } | |
+ | |
+ /* Check if the PC is to be popped */ | |
+ if(R) | |
+ { | |
+ /* Get the return address */ | |
+ if(!UnwMemReadRegister(state, state->regData[13].v, &state->regData[15])) | |
+ { | |
+ return UNWIND_DREAD_W_FAIL; | |
+ } | |
+ | |
+ /* Alter the origin to be from the stack if it was valid */ | |
+ if(!M_IsOriginValid(state->regData[15].o)) | |
+ { | |
+ /* Return address is not valid */ | |
+ UnwPrintd1("PC popped with invalid address\n"); | |
+ return UNWIND_FAILURE; | |
+ } | |
+ else | |
+ { | |
+ /* The bottom bit should have been set to indicate that | |
+ * the caller was from Thumb. This would allow return | |
+ * by BX for interworking APCS. | |
+ */ | |
+ if((state->regData[15].v & 0x1) == 0) | |
+ { | |
+ UnwPrintd2("Warning: Return address not to Thumb: 0x%08x\n", | |
+ state->regData[15].v); | |
+ | |
+ /* Pop into the PC will not switch mode */ | |
+ return UNWIND_INCONSISTENT; | |
+ } | |
+ | |
+ /* Store the return address */ | |
+ if(!UnwReportRetAddr(state, state->regData[15].v)) | |
+ { | |
+ return UNWIND_TRUNCATED; | |
+ } | |
+ | |
+ /* Now have the return address */ | |
+ UnwPrintd2(" Return PC=%x\n", state->regData[15].v); | |
+ | |
+ /* Update the pc */ | |
+ state->regData[13].v += 4; | |
+ | |
+ /* Compensate for the auto-increment, which isn't needed here */ | |
+ state->regData[15].v -= 2; | |
+ } | |
+ } | |
+ | |
+ } | |
+ else | |
+ { | |
+ SignedInt8 r; | |
+ | |
+ /* Store to memory: PUSH */ | |
+ UnwPrintd2("PUSH {Rlist%s}", R ? ", LR" : ""); | |
+ | |
+ /* Check if the LR is to be pushed */ | |
+ if(R) | |
+ { | |
+ UnwPrintd3("\n lr = 0x%08x\t; %s", | |
+ state->regData[14].v, M_Origin2Str(state->regData[14].o)); | |
+ | |
+ state->regData[13].v -= 4; | |
+ | |
+ /* Write the register value to memory */ | |
+ if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[14])) | |
+ { | |
+ return UNWIND_DWRITE_W_FAIL; | |
+ } | |
+ } | |
+ | |
+ for(r = 7; r >= 0; r--) | |
+ { | |
+ if(rList & (0x1 << r)) | |
+ { | |
+ UnwPrintd4("\n r%d = 0x%08x\t; %s", | |
+ r, state->regData[r].v, M_Origin2Str(state->regData[r].o)); | |
+ | |
+ state->regData[13].v -= 4; | |
+ | |
+ if(!UnwMemWriteRegister(state, state->regData[13].v, &state->regData[r])) | |
+ { | |
+ return UNWIND_DWRITE_W_FAIL; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ /* Format 18: unconditional branch | |
+ * B label | |
+ */ | |
+ else if((instr & 0xf800) == 0xe000) | |
+ { | |
+ SignedInt16 branchValue = signExtend11(instr & 0x07ff); | |
+ | |
+ /* Branch distance is twice that specified in the instruction. */ | |
+ branchValue *= 2; | |
+ | |
+ UnwPrintd2("B %d \n", branchValue); | |
+ | |
+ /* Update PC */ | |
+ state->regData[15].v += branchValue; | |
+ | |
+ /* Need to advance by a word to account for pre-fetch. | |
+ * Advance by a half word here, allowing the normal address | |
+ * advance to account for the other half word. | |
+ */ | |
+ state->regData[15].v += 2; | |
+ | |
+ /* Display PC of next instruction */ | |
+ UnwPrintd2(" New PC=%x", state->regData[15].v + 2); | |
+ | |
+ } | |
+ else | |
+ { | |
+ UnwPrintd1("????"); | |
+ | |
+ /* Unknown/undecoded. May alter some register, so invalidate file */ | |
+ UnwInvalidateRegisterFile(state->regData); | |
+ } | |
+ | |
+ UnwPrintd1("\n"); | |
+ | |
+ /* Should never hit the reset vector */ | |
+ if(state->regData[15].v == 0) return UNWIND_RESET; | |
+ | |
+ /* Check next address */ | |
+ state->regData[15].v += 2; | |
+ | |
+ /* Garbage collect the memory hash (used only for the stack) */ | |
+ UnwMemHashGC(state); | |
+ | |
+ t--; | |
+ if(t == 0) return UNWIND_EXHAUSTED; | |
+ | |
+ } | |
+ while(!found); | |
+ | |
+ return UNWIND_SUCCESS; | |
+} | |
+ | |
+#endif /* UPGRADE_ARM_STACK_UNWIND */ | |
+ | |
+/* END OF FILE */ | |
+ | |
diff --git a/firmware/target/arm/unwarminder/unwarminder.c b/firmware/target/arm/unwarminder/unwarminder.c | |
new file mode 100644 | |
index 0000000..68bd9f3 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarminder.c | |
@@ -0,0 +1,78 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commercially or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liability for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Implementation of the interface into the ARM unwinder. | |
+ **************************************************************************/ | |
+ | |
+#define MODULE_NAME "UNWARMINDER" | |
+ | |
+/*************************************************************************** | |
+ * Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#include <stdio.h> | |
+#include <string.h> | |
+#include "unwarminder.h" | |
+#include "unwarm.h" | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Local Functions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Global Functions | |
+ **************************************************************************/ | |
+ | |
+UnwResult UnwindStart(Int32 pcValue, | |
+ Int32 spValue, | |
+ const UnwindCallbacks *cb, | |
+ void *data) | |
+{ | |
+ UnwState state; | |
+ | |
+ /* Initialise the unwinding state */ | |
+ UnwInitState(&state, cb, data, pcValue, spValue); | |
+ | |
+ /* Check the Thumb bit */ | |
+ if(pcValue & 0x1) | |
+ { | |
+ return UnwStartThumb(&state); | |
+ } | |
+ else | |
+ { | |
+ return UnwStartArm(&state); | |
+ } | |
+} | |
+ | |
+/* END OF FILE */ | |
+ | |
diff --git a/firmware/target/arm/unwarminder/unwarminder.h b/firmware/target/arm/unwarminder/unwarminder.h | |
new file mode 100644 | |
index 0000000..1c5adbf | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarminder.h | |
@@ -0,0 +1,160 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commerically or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liablity for it's use or misuse - this software is without warranty. | |
+ **************************************************************************/ | |
+/** \file | |
+ * Interface to the ARM stack unwinding module. | |
+ **************************************************************************/ | |
+ | |
+#ifndef UNWARMINDER_H | |
+#define UNWARMINDER_H | |
+ | |
+/*************************************************************************** | |
+ * Nested Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+/** \def UNW_DEBUG | |
+ * If this define is set, additional information will be produced while | |
+ * unwinding the stack to allow debug of the unwind module itself. | |
+ */ | |
+/* #define UNW_DEBUG 1 */ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+/** Possible results for UnwindStart to return. | |
+ */ | |
+typedef enum UnwResultTag | |
+{ | |
+ /** Unwinding was successful and complete. */ | |
+ UNWIND_SUCCESS = 0, | |
+ | |
+ /** More than UNW_MAX_INSTR_COUNT instructions were interpreted. */ | |
+ UNWIND_EXHAUSTED, | |
+ | |
+ /** Unwinding stopped because the reporting func returned FALSE. */ | |
+ UNWIND_TRUNCATED, | |
+ | |
+ /** Read data was found to be inconsistent. */ | |
+ UNWIND_INCONSISTENT, | |
+ | |
+ /** Unsupported instruction or data found. */ | |
+ UNWIND_UNSUPPORTED, | |
+ | |
+ /** General failure. */ | |
+ UNWIND_FAILURE, | |
+ | |
+ /** Illegal instruction. */ | |
+ UNWIND_ILLEGAL_INSTR, | |
+ | |
+ /** Unwinding hit the reset vector. */ | |
+ UNWIND_RESET, | |
+ | |
+ /** Failed read for an instruction word. */ | |
+ UNWIND_IREAD_W_FAIL, | |
+ | |
+ /** Failed read for an instruction half-word. */ | |
+ UNWIND_IREAD_H_FAIL, | |
+ | |
+ /** Failed read for an instruction byte. */ | |
+ UNWIND_IREAD_B_FAIL, | |
+ | |
+ /** Failed read for a data word. */ | |
+ UNWIND_DREAD_W_FAIL, | |
+ | |
+ /** Failed read for a data half-word. */ | |
+ UNWIND_DREAD_H_FAIL, | |
+ | |
+ /** Failed read for a data byte. */ | |
+ UNWIND_DREAD_B_FAIL, | |
+ | |
+ /** Failed write for a data word. */ | |
+ UNWIND_DWRITE_W_FAIL | |
+} | |
+UnwResult; | |
+ | |
+/** Type for function pointer for result callback. | |
+ * The function is passed two parameters, the first is a void * pointer, | |
+ * and the second is the return address of the function. The bottom bit | |
+ * of the passed address indicates the execution mode; if it is set, | |
+ * the execution mode at the return address is Thumb, otherwise it is | |
+ * ARM. | |
+ * | |
+ * The return value of this function determines whether unwinding should | |
+ * continue or not. If TRUE is returned, unwinding will continue and the | |
+ * report function maybe called again in future. If FALSE is returned, | |
+ * unwinding will stop with UnwindStart() returning UNWIND_TRUNCATED. | |
+ */ | |
+typedef Boolean (*UnwindReportFunc)(void *data, | |
+ Int32 address); | |
+ | |
+/** Structure that holds memory callback function pointers. | |
+ */ | |
+typedef struct UnwindCallbacksTag | |
+{ | |
+ /** Report an unwind result. */ | |
+ UnwindReportFunc report; | |
+ | |
+ /** Read a 32 bit word from memory. | |
+ * The memory address to be read is passed as \a address, and | |
+ * \a *val is expected to be populated with the read value. | |
+ * If the address cannot or should not be read, FALSE can be | |
+ * returned to indicate that unwinding should stop. If TRUE | |
+ * is returned, \a *val is assumed to be valid and unwinding | |
+ * will continue. | |
+ */ | |
+ Boolean (*readW)(const Int32 address, Int32 *val); | |
+ | |
+ /** Read a 16 bit half-word from memory. | |
+ * This function has the same usage as for readW, but is expected | |
+ * to read only a 16 bit value. | |
+ */ | |
+ Boolean (*readH)(const Int32 address, Int16 *val); | |
+ | |
+ /** Read a byte from memory. | |
+ * This function has the same usage as for readW, but is expected | |
+ * to read only an 8 bit value. | |
+ */ | |
+ Boolean (*readB)(const Int32 address, Int8 *val); | |
+ | |
+#if defined(UNW_DEBUG) | |
+ /** Print a formatted line for debug. */ | |
+ int (*printf)(const char *format, ...); | |
+#endif | |
+ | |
+} | |
+UnwindCallbacks; | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+/*************************************************************************** | |
+ * Function Prototypes | |
+ **************************************************************************/ | |
+ | |
+/** Start unwinding the current stack. | |
+ * This will unwind the stack starting at the PC value supplied and | |
+ * the stack pointer value supplied. | |
+ */ | |
+UnwResult UnwindStart(Int32 pcValue, | |
+ Int32 spValue, | |
+ const UnwindCallbacks *cb, | |
+ void *data); | |
+ | |
+#endif /* UNWARMINDER_H */ | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/unwarmmem.c b/firmware/target/arm/unwarminder/unwarmmem.c | |
new file mode 100644 | |
index 0000000..5991720 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarmmem.c | |
@@ -0,0 +1,175 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commerically or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liablity for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Implementation of the memory tracking sub-system. | |
+ **************************************************************************/ | |
+ | |
+#define MODULE_NAME "UNWARMMEM" | |
+ | |
+/*************************************************************************** | |
+ * Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#include <stdio.h> | |
+#include "unwarmmem.h" | |
+#include "unwarm.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Variables | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+#define M_IsIdxUsed(a, v) (((a)[v >> 3] & (1 << (v & 0x7))) ? TRUE : FALSE) | |
+ | |
+#define M_SetIdxUsed(a, v) ((a)[v >> 3] |= (1 << (v & 0x7))) | |
+ | |
+#define M_ClrIdxUsed(a, v) ((a)[v >> 3] &= ~(1 << (v & 0x7))) | |
+ | |
+/*************************************************************************** | |
+ * Local Functions | |
+ **************************************************************************/ | |
+ | |
+/** Search the memory hash to see if an entry is stored in the hash already. | |
+ * This will search the hash and either return the index where the item is | |
+ * stored, or -1 if the item was not found. | |
+ */ | |
+static SignedInt16 memHashIndex(MemData * const memData, | |
+ const Int32 addr) | |
+{ | |
+ const Int16 v = addr % MEM_HASH_SIZE; | |
+ Int16 s = v; | |
+ | |
+ do | |
+ { | |
+ /* Check if the element is occupied */ | |
+ if(M_IsIdxUsed(memData->used, s)) | |
+ { | |
+ /* Check if it is occupied with the sought data */ | |
+ if(memData->a[s] == addr) | |
+ { | |
+ return s; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ /* Item is free, this is where the item should be stored */ | |
+ return s; | |
+ } | |
+ | |
+ /* Search the next entry */ | |
+ s++; | |
+ if(s > MEM_HASH_SIZE) | |
+ { | |
+ s = 0; | |
+ } | |
+ } | |
+ while(s != v); | |
+ | |
+ /* Search failed, hash is full and the address not stored */ | |
+ return -1; | |
+} | |
+ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Global Functions | |
+ **************************************************************************/ | |
+ | |
+Boolean UnwMemHashRead(MemData * const memData, | |
+ Int32 addr, | |
+ Int32 * const data, | |
+ Boolean * const tracked) | |
+{ | |
+ SignedInt16 i = memHashIndex(memData, addr); | |
+ | |
+ if(i >= 0 && M_IsIdxUsed(memData->used, i) && memData->a[i] == addr) | |
+ { | |
+ *data = memData->v[i]; | |
+ *tracked = M_IsIdxUsed(memData->tracked, i); | |
+ return TRUE; | |
+ } | |
+ else | |
+ { | |
+ /* Address not found in the hash */ | |
+ return FALSE; | |
+ } | |
+} | |
+ | |
+Boolean UnwMemHashWrite(MemData * const memData, | |
+ Int32 addr, | |
+ Int32 val, | |
+ Boolean valValid) | |
+{ | |
+ SignedInt16 i = memHashIndex(memData, addr); | |
+ | |
+ if(i < 0) | |
+ { | |
+ /* Hash full */ | |
+ return FALSE; | |
+ } | |
+ else | |
+ { | |
+ /* Store the item */ | |
+ memData->a[i] = addr; | |
+ M_SetIdxUsed(memData->used, i); | |
+ | |
+ if(valValid) | |
+ { | |
+ memData->v[i] = val; | |
+ M_SetIdxUsed(memData->tracked, i); | |
+ } | |
+ else | |
+ { | |
+#if defined(UNW_DEBUG) | |
+ memData->v[i] = 0xdeadbeef; | |
+#endif | |
+ M_ClrIdxUsed(memData->tracked, i); | |
+ } | |
+ | |
+ return TRUE; | |
+ } | |
+} | |
+ | |
+ | |
+void UnwMemHashGC(UnwState * const state) | |
+{ | |
+ const Int32 minValidAddr = state->regData[13].v; | |
+ MemData * const memData = &state->memData; | |
+ Int16 t; | |
+ | |
+ for(t = 0; t < MEM_HASH_SIZE; t++) | |
+ { | |
+ if(M_IsIdxUsed(memData->used, t) && (memData->a[t] < minValidAddr)) | |
+ { | |
+ UnwPrintd3("MemHashGC: Free elem %d, addr 0x%08x\n", | |
+ t, memData->a[t]); | |
+ | |
+ M_ClrIdxUsed(memData->used, t); | |
+ } | |
+ } | |
+} | |
+ | |
+/* END OF FILE */ | |
diff --git a/firmware/target/arm/unwarminder/unwarmmem.h b/firmware/target/arm/unwarminder/unwarmmem.h | |
new file mode 100644 | |
index 0000000..4c02d28 | |
--- /dev/null | |
+++ b/firmware/target/arm/unwarminder/unwarmmem.h | |
@@ -0,0 +1,57 @@ | |
+/*************************************************************************** | |
+ * ARM Stack Unwinder, [email protected] | |
+ * | |
+ * This program is PUBLIC DOMAIN. | |
+ * This means that there is no copyright and anyone is able to take a copy | |
+ * for free and use it as they wish, with or without modifications, and in | |
+ * any context, commerically or otherwise. The only limitation is that I | |
+ * don't guarantee that the software is fit for any purpose or accept any | |
+ * liablity for it's use or misuse - this software is without warranty. | |
+ *************************************************************************** | |
+ * File Description: Interface to the memory tracking sub-system. | |
+ **************************************************************************/ | |
+ | |
+#ifndef UNWARMMEM_H | |
+#define UNWARMMEM_H | |
+ | |
+/*************************************************************************** | |
+ * Nested Include Files | |
+ **************************************************************************/ | |
+ | |
+#include "types.h" | |
+#include "unwarm.h" | |
+ | |
+/*************************************************************************** | |
+ * Manifest Constants | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Type Definitions | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Macros | |
+ **************************************************************************/ | |
+ | |
+ | |
+/*************************************************************************** | |
+ * Function Prototypes | |
+ **************************************************************************/ | |
+ | |
+Boolean UnwMemHashRead (MemData * const memData, | |
+ Int32 addr, | |
+ Int32 * const data, | |
+ Boolean * const tracked); | |
+ | |
+Boolean UnwMemHashWrite (MemData * const memData, | |
+ Int32 addr, | |
+ Int32 val, | |
+ Boolean valValid); | |
+ | |
+void UnwMemHashGC (UnwState * const state); | |
+ | |
+#endif | |
+ | |
+/* END OF FILE */ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment