+// SPDX-License-Identifier: GPL-2.0-or-later
+
/***************************************************************************
* Copyright (C) 2008 by Spencer Oliver *
* spen@spen-soft.co.uk *
* *
* Copyright (C) 2011 by Drasko DRASKOVIC *
* drasko.draskovic@gmail.com *
- * *
- * This program is free software; you can redistribute it and/or modify *
- * it under the terms of the GNU General Public License as published by *
- * the Free Software Foundation; either version 2 of the License, or *
- * (at your option) any later version. *
- * *
- * This program is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU General Public License for more details. *
- * *
- * You should have received a copy of the GNU General Public License *
- * along with this program. If not, see <http://www.gnu.org/licenses/>. *
***************************************************************************/
/*
#include "config.h"
#endif
+#include <helper/align.h>
#include <helper/time_support.h>
+#include <jtag/adapter.h>
+#include "mips_cpu.h"
#include "mips32.h"
#include "mips32_pracc.h"
mips_ejtag_drscan_32_out(ejtag_info, ctrl);
}
-int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info)
+static int mips32_pracc_clean_text_jump(struct mips_ejtag *ejtag_info)
{
uint32_t jt_code = MIPS32_J(ejtag_info->isa, MIPS32_PRACC_TEXT);
pracc_swap16_array(ejtag_info, &jt_code, 1);
return ERROR_OK;
}
-int mips32_pracc_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx,
+static int mips32_pracc_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx,
uint32_t *param_out, bool check_last)
{
int code_count = 0;
return ERROR_JTAG_DEVICE_ERROR;
}
} else
- if (code_count > 10) { /* enough, abandone */
+ if (code_count > 10) { /* enough, abandon */
LOG_DEBUG("execution abandoned, store pending: %d", store_pending);
return ERROR_JTAG_DEVICE_ERROR;
}
if (ctx->retval != ERROR_OK) /* On previous out of memory, return */
return;
if (ctx->code_count == ctx->max_code) {
- void *p = realloc(ctx->pracc_list, sizeof(pa_list) * (ctx->max_code + PRACC_BLOCK));
+ void *p = realloc(ctx->pracc_list, sizeof(struct pa_list) * (ctx->max_code + PRACC_BLOCK));
if (p) {
ctx->max_code += PRACC_BLOCK;
ctx->pracc_list = p;
ctx->store_count++;
}
-void pracc_add_li32(struct pracc_queue_info *ctx, uint32_t reg_num, uint32_t data, bool optimize)
+static void pracc_add_li32(struct pracc_queue_info *ctx, uint32_t reg_num, uint32_t data, bool optimize)
{
if (LOWER16(data) == 0 && optimize)
pracc_add(ctx, 0, MIPS32_LUI(ctx->isa, reg_num, UPPER16(data))); /* load only upper value */
inline void pracc_queue_free(struct pracc_queue_info *ctx)
{
- if (ctx->pracc_list != NULL)
- free(ctx->pracc_list);
+ free(ctx->pracc_list);
}
int mips32_pracc_queue_exec(struct mips_ejtag *ejtag_info, struct pracc_queue_info *ctx,
} scan_32;
} *scan_in = malloc(sizeof(union scan_in) * (ctx->code_count + ctx->store_count));
- if (scan_in == NULL) {
+ if (!scan_in) {
LOG_ERROR("Out of memory");
return ERROR_FAIL;
}
unsigned num_clocks =
- ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
+ ((uint64_t)(ejtag_info->scan_delay) * adapter_get_speed_khz() + 500000) / 1000000;
uint32_t ejtag_ctrl = ejtag_info->ejtag_ctrl & ~EJTAG_CTRL_PRACC;
mips_ejtag_set_instr(ejtag_info, EJTAG_INST_ALL);
fetch_addr += 4;
scan_count++;
- /* check if previous intrucction is a store instruction at dmesg */
+ /* check if previous instruction is a store instruction at dmesg */
if (i > 0 && ctx->pracc_list[i - 1].addr) {
uint32_t store_addr = ctx->pracc_list[i - 1].addr;
ejtag_ctrl = buf_get_u32(scan_in[scan_count].scan_32.ctrl, 0, 32);
return retval;
}
-int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf)
+static int mips32_pracc_read_u32(struct mips_ejtag *ejtag_info, uint32_t addr, uint32_t *buf)
{
struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
pracc_queue_init(&ctx);
pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 8, UPPER16((addr + 0x8000)))); /* load $8 with modified upper addr */
pracc_add(&ctx, 0, MIPS32_LW(ctx.isa, 8, LOWER16(addr), 8)); /* lw $8, LOWER16(addr)($8) */
+ if (mips32_cpu_support_sync(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_SYNC(ctx.isa));
pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET, 15)); /* sw $8,PRACC_OUT_OFFSET($15) */
pracc_add_li32(&ctx, 8, ejtag_info->reg8, 0); /* restore $8 */
uint32_t *data = NULL;
if (size != 4) {
data = malloc(256 * sizeof(uint32_t));
- if (data == NULL) {
+ if (!data) {
LOG_ERROR("Out of memory");
goto exit;
}
else
pracc_add(&ctx, 0, MIPS32_LBU(ctx.isa, 8, LOWER16(addr), 9));
+ if (mips32_cpu_support_sync(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_SYNC(ctx.isa));
pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + i * 4, /* store $8 at param out */
MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + i * 4, 15));
addr += size;
}
exit:
pracc_queue_free(&ctx);
- if (data != NULL)
- free(data);
+ free(data);
return ctx.retval;
}
pracc_queue_init(&ctx);
pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
+ if (mips32_cpu_support_hazard_barrier(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
pracc_add(&ctx, 0, MIPS32_MFC0(ctx.isa, 8, cp0_reg, cp0_sel)); /* move cp0 reg / sel to $8 */
pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
pracc_add_li32(&ctx, 15, val, 0); /* Load val to $15 */
pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, cp0_reg, cp0_sel)); /* write $15 to cp0 reg / sel */
+ if (mips32_cpu_support_hazard_barrier(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa))); /* jump to start */
pracc_add(&ctx, 0, MIPS32_MFC0(ctx.isa, 15, 31, 0)); /* restore $15 from DeSave */
return ctx.retval;
}
+int mips32_cp1_control_read(struct mips_ejtag *ejtag_info, uint32_t *val, uint32_t cp1_c_reg)
+{
+ struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
+ pracc_queue_init(&ctx);
+
+ pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIPS32_PRACC_BASE_ADDR */
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
+ pracc_add(&ctx, 0, MIPS32_CFC1(ctx.isa, 8, cp1_c_reg)); /* move cp1c reg to $8 */
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT,
+ MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET, 15)); /* store $8 to pracc_out */
+ pracc_add(&ctx, 0, MIPS32_MFC0(ctx.isa, 15, 31, 0)); /* restore $15 from DeSave */
+ pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 8, UPPER16(ejtag_info->reg8))); /* restore upper 16 bits of $8 */
+ pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa))); /* jump to start */
+ pracc_add(&ctx, 0, MIPS32_ORI(ctx.isa, 8, 8, LOWER16(ejtag_info->reg8))); /* restore lower 16 bits of $8 */
+
+ ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, val, 1);
+ pracc_queue_free(&ctx);
+ return ctx.retval;
+}
+
/**
* \b mips32_pracc_sync_cache
*
goto exit; /* Nothing to do */
/* make sure clsiz is power of 2 */
- if (clsiz & (clsiz - 1)) {
+ if (!IS_PWR_OF_2(clsiz)) {
LOG_DEBUG("clsiz must be power of 2");
ctx.retval = ERROR_FAIL;
goto exit;
* If we are in the cacheable region and cache is activated,
* we must clean D$ (if Cache Coherency Attribute is set to 3) + invalidate I$ after we did the write,
* so that changes do not continue to live only in D$ (if CCA = 3), but to be
- * replicated in I$ also (maybe we wrote the istructions)
+ * replicated in I$ also (maybe we wrote the instructions)
*/
uint32_t conf = 0;
int cached = 0;
if ((KSEGX(addr) == KSEG1) || ((addr >= 0xff200000) && (addr <= 0xff3fffff)))
return retval; /*Nothing to do*/
+ /* Reads Config0 */
mips32_cp0_read(ejtag_info, &conf, 16, 0);
switch (KSEGX(addr)) {
}
/**
- * Check cachablitiy bits coherency algorithm
+ * Check cacheability bits coherency algorithm
* is the region cacheable or uncached.
* If cacheable we have to synchronize the cache
*/
uint32_t start_addr = addr;
uint32_t end_addr = addr + count * size;
uint32_t rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
- if (rel > 1) {
- LOG_DEBUG("Unknown release in cache code");
+ /* FIXME: In MIPS Release 6, the encoding of CACHE instr has changed */
+ if (rel > MIPS32_RELEASE_2) {
+ LOG_DEBUG("Unsupported MIPS Release ( > 5)");
return ERROR_FAIL;
}
retval = mips32_pracc_synchronize_cache(ejtag_info, start_addr, end_addr, cached, rel);
+ } else {
+ struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
+
+ pracc_queue_init(&ctx);
+ if (mips32_cpu_support_sync(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_SYNC(ctx.isa));
+ if (mips32_cpu_support_hazard_barrier(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
+ pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa))); /* jump to start */
+ pracc_add(&ctx, 0, MIPS32_NOP);
+ ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL, 1);
+ if (ctx.retval != ERROR_OK) {
+ LOG_ERROR("Unable to barrier");
+ retval = ctx.retval;
+ }
+ pracc_queue_free(&ctx);
}
return retval;
}
-int mips32_pracc_write_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
+int mips32_pracc_write_regs(struct mips32_common *mips32)
{
+ struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
+ uint32_t *gprs = mips32->core_regs.gpr;
+ uint32_t *c0rs = mips32->core_regs.cp0;
+ bool fpu_in_64bit = ((c0rs[0] & BIT(MIPS32_CP0_STATUS_FR_SHIFT)) != 0);
+ bool fp_enabled = ((c0rs[0] & BIT(MIPS32_CP0_STATUS_CU1_SHIFT)) != 0);
+ uint32_t rel = (ejtag_info->config[0] & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
+
pracc_queue_init(&ctx);
uint32_t cp0_write_code[] = {
MIPS32_MTC0(ctx.isa, 1, 24, 0), /* move $1 to depc (pc) */
};
+ uint32_t cp0_write_data[] = {
+ /* status */
+ c0rs[0],
+ /* lo */
+ gprs[32],
+ /* hi */
+ gprs[33],
+ /* badvaddr */
+ c0rs[1],
+ /* cause */
+ c0rs[2],
+ /* depc (pc) */
+ c0rs[3],
+ };
+
+ /* Write CP0 Status Register first, changes on EXL or ERL bits
+ * may lead to different behaviour on writing to other CP0 registers.
+ */
+ for (size_t i = 0; i < ARRAY_SIZE(cp0_write_code); i++) {
+ /* load CP0 value in $1 */
+ pracc_add_li32(&ctx, 1, cp0_write_data[i], 0);
+ /* write value from $1 to CP0 register */
+ pracc_add(&ctx, 0, cp0_write_code[i]);
+ }
+
+ if (mips32_cpu_support_hazard_barrier(ejtag_info))
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
+
+ /* store FPRs */
+ if (mips32->fp_imp && fp_enabled) {
+ uint64_t *fprs = mips32->core_regs.fpr;
+ if (fpu_in_64bit) {
+ for (int i = 0; i != MIPS32_REG_FP_COUNT; i++) {
+ uint32_t fp_lo = fprs[i] & 0xffffffff;
+ uint32_t fp_hi = (fprs[i] >> 32) & 0xffffffff;
+ pracc_add_li32(&ctx, 2, fp_lo, 0);
+ pracc_add_li32(&ctx, 3, fp_hi, 0);
+ pracc_add(&ctx, 0, MIPS32_MTC1(ctx.isa, 2, i));
+ pracc_add(&ctx, 0, MIPS32_MTHC1(ctx.isa, 3, i));
+ }
+ } else {
+ for (int i = 0; i != MIPS32_REG_FP_COUNT; i++) {
+ uint32_t fp_lo = fprs[i] & 0xffffffff;
+ pracc_add_li32(&ctx, 2, fp_lo, 0);
+ pracc_add(&ctx, 0, MIPS32_MTC1(ctx.isa, 2, i));
+ }
+ }
+
+ if (rel > MIPS32_RELEASE_1)
+ pracc_add(&ctx, 0, MIPS32_EHB(ctx.isa));
+ }
+
/* load registers 2 to 31 with li32, optimize */
for (int i = 2; i < 32; i++)
- pracc_add_li32(&ctx, i, regs[i], 1);
+ pracc_add_li32(&ctx, i, gprs[i], 1);
- for (int i = 0; i != 6; i++) {
- pracc_add_li32(&ctx, 1, regs[i + 32], 0); /* load CPO value in $1 */
- pracc_add(&ctx, 0, cp0_write_code[i]); /* write value from $1 to CPO register */
- }
- pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, 31, 0)); /* load $15 in DeSave */
- pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 1, UPPER16((regs[1])))); /* load upper half word in $1 */
- pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa))); /* jump to start */
- pracc_add(&ctx, 0, MIPS32_ORI(ctx.isa, 1, 1, LOWER16((regs[1])))); /* load lower half word in $1 */
+ /* load $15 in DeSave */
+ pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, 31, 0));
+ /* load upper half word in $1 */
+ pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 1, UPPER16((gprs[1]))));
+ /* jump to start */
+ pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa)));
+ /* load lower half word in $1 */
+ pracc_add(&ctx, 0, MIPS32_ORI(ctx.isa, 1, 1, LOWER16((gprs[1]))));
ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL, 1);
- ejtag_info->reg8 = regs[8];
- ejtag_info->reg9 = regs[9];
+ ejtag_info->reg8 = gprs[8];
+ ejtag_info->reg9 = gprs[9];
pracc_queue_free(&ctx);
return ctx.retval;
}
-int mips32_pracc_read_regs(struct mips_ejtag *ejtag_info, uint32_t *regs)
+/* Saves content in `$1` to `DeSave(cp0.31.0)` and loads `MIPS32_PRACC_BASE_ADDR` into `$1` */
+static void mips32_pracc_store_regs_set_base_addr(struct pracc_queue_info *ctx)
{
- struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
- pracc_queue_init(&ctx);
+ /* move $1 to COP0 DeSave */
+ pracc_add(ctx, 0, MIPS32_MTC0(ctx->isa, 1, 31, 0));
+ /* $1 = MIP32_PRACC_BASE_ADDR */
+ pracc_add(ctx, 0, MIPS32_LUI(ctx->isa, 1, PRACC_UPPER_BASE_ADDR));
+}
- uint32_t cp0_read_code[] = {
- MIPS32_MFC0(ctx.isa, 8, 12, 0), /* move status to $8 */
- MIPS32_MFLO(ctx.isa, 8), /* move lo to $8 */
- MIPS32_MFHI(ctx.isa, 8), /* move hi to $8 */
- MIPS32_MFC0(ctx.isa, 8, 8, 0), /* move badvaddr to $8 */
- MIPS32_MFC0(ctx.isa, 8, 13, 0), /* move cause to $8 */
- MIPS32_MFC0(ctx.isa, 8, 24, 0), /* move depc (pc) to $8 */
+/* This function assumes the address for saving is stored in `$1`.
+ * And that action is performed in `mips32_pracc_set_save_base_addr`.
+ */
+static void mips32_pracc_store_regs_gpr(struct pracc_queue_info *ctx, unsigned int offset_gpr)
+{
+ for (int i = 2; i != 32; i++)
+ pracc_add(ctx, MIPS32_PRACC_PARAM_OUT + offset_gpr + (i * 4),
+ MIPS32_SW(ctx->isa, i, PRACC_OUT_OFFSET + offset_gpr + (i * 4), 1));
+}
+
+static void mips32_pracc_store_regs_lohi(struct pracc_queue_info *ctx)
+{
+ uint32_t lohi_read_code[] = {
+ MIPS32_MFLO(ctx->isa, 8), /* move lo to $8 */
+ MIPS32_MFHI(ctx->isa, 8), /* move hi to $8 */
};
- pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 1, 31, 0)); /* move $1 to COP0 DeSave */
- pracc_add(&ctx, 0, MIPS32_LUI(ctx.isa, 1, PRACC_UPPER_BASE_ADDR)); /* $1 = MIP32_PRACC_BASE_ADDR */
+ /* store lo & hi */
+ for (int i = 0; i < 2; i++) {
+ /* load COP0 needed registers to $8 */
+ pracc_add(ctx, 0, lohi_read_code[i]);
+ /* store $8 at PARAM OUT */
+ pracc_add(ctx, MIPS32_PRACC_PARAM_OUT + (i + 32) * 4,
+ MIPS32_SW(ctx->isa, 8, PRACC_OUT_OFFSET + (i + 32) * 4, 1));
+ }
+}
+
+/* Saves CP0 registers [status, badvaddr, cause, depc] */
+static void mips32_pracc_store_regs_cp0_context(struct pracc_queue_info *ctx, unsigned int offset_cp0)
+{
+ uint32_t cp0_read_code[] = {
+ MIPS32_MFC0(ctx->isa, 8, 12, 0), /* move status to $8 */
+ MIPS32_MFC0(ctx->isa, 8, 8, 0), /* move badvaddr to $8 */
+ MIPS32_MFC0(ctx->isa, 8, 13, 0), /* move cause to $8 */
+ MIPS32_MFC0(ctx->isa, 8, 24, 0), /* move depc (pc) to $8 */
+ };
- for (int i = 2; i != 32; i++) /* store GPR's 2 to 31 */
- pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i * 4),
- MIPS32_SW(ctx.isa, i, PRACC_OUT_OFFSET + (i * 4), 1));
+ /* store cp0 */
+ for (size_t i = 0; i < ARRAY_SIZE(cp0_read_code); i++) {
+ size_t offset = offset_cp0 + (i * 4);
- for (int i = 0; i != 6; i++) {
- pracc_add(&ctx, 0, cp0_read_code[i]); /* load COP0 needed registers to $8 */
- pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i + 32) * 4, /* store $8 at PARAM OUT */
- MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + (i + 32) * 4, 1));
+ /* load COP0 needed registers to $8 */
+ pracc_add(ctx, 0, cp0_read_code[i]);
+ /* store $8 at PARAM OUT */
+ pracc_add(ctx, MIPS32_PRACC_PARAM_OUT + offset,
+ MIPS32_SW(ctx->isa, 8, PRACC_OUT_OFFSET + offset, 1));
}
- pracc_add(&ctx, 0, MIPS32_MFC0(ctx.isa, 8, 31, 0)); /* move DeSave to $8, reg1 value */
- pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + 4, /* store reg1 value from $8 to param out */
- MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + 4, 1));
+}
- pracc_add(&ctx, 0, MIPS32_MFC0(ctx.isa, 1, 31, 0)); /* move COP0 DeSave to $1, restore reg1 */
- pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa))); /* jump to start */
- pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, 31, 0)); /* load $15 in DeSave */
+/* Loads original content of $1 into $8,
+ * then store it to the batch data access address.
+ * Finally it restores $1 from DeSave.
+ */
+static void mips32_pracc_store_regs_restore(struct pracc_queue_info *ctx)
+{
+ /* move DeSave to $8, reg1 value */
+ pracc_add(ctx, 0, MIPS32_MFC0(ctx->isa, 8, 31, 0));
+ /* store reg1 value from $8 to param out */
+ pracc_add(ctx, MIPS32_PRACC_PARAM_OUT + 4,
+ MIPS32_SW(ctx->isa, 8, PRACC_OUT_OFFSET + 4, 1));
+
+ /* move COP0 DeSave to $1, restore reg1 */
+ pracc_add(ctx, 0, MIPS32_MFC0(ctx->isa, 1, 31, 0));
+}
+
+/* This function performs following actions:
+ * Saves `$1` to `DeSave`,
+ * then load `PRACC_UPPER_BASE_ADDR` for saving the register data structure into `$1`,
+ * Saves `$2` ~ `$31` to `PRACC_UPPER_BASE_ADDR + offset_gpr`
+ * Saves HI and LO,
+ * Saves necessary cp0 registers.
+*/
+static void mips32_pracc_store_regs(struct pracc_queue_info *ctx,
+ unsigned int offset_gpr, unsigned int offset_cp0)
+{
+ mips32_pracc_store_regs_set_base_addr(ctx);
+ mips32_pracc_store_regs_gpr(ctx, offset_gpr);
+ mips32_pracc_store_regs_lohi(ctx);
+ mips32_pracc_store_regs_cp0_context(ctx, offset_cp0);
+ mips32_pracc_store_regs_restore(ctx);
+}
+
+int mips32_pracc_read_regs(struct mips32_common *mips32)
+{
+ struct mips_ejtag *ejtag_info = &mips32->ejtag_info;
+ struct pracc_queue_info ctx = {.ejtag_info = ejtag_info};
+ struct mips32_core_regs *core_regs = &mips32->core_regs;
+ unsigned int offset_gpr = ((uint8_t *)&core_regs->gpr[0]) - (uint8_t *)core_regs;
+ unsigned int offset_cp0 = ((uint8_t *)&core_regs->cp0[0]) - (uint8_t *)core_regs;
+ unsigned int offset_fpr = ((uint8_t *)&core_regs->fpr[0]) - (uint8_t *)core_regs;
+ unsigned int offset_fpcr = ((uint8_t *)&core_regs->fpcr[0]) - (uint8_t *)core_regs;
+ bool fp_enabled;
+
+ /*
+ * This procedure has to be in 2 distinctive steps, because we can
+ * only know whether FP is enabled after reading CP0.
+ *
+ * Step 1: Read everything except CP1 stuff
+ * Step 2: Read CP1 stuff if FP is implemented
+ */
+
+ pracc_queue_init(&ctx);
+
+ mips32_pracc_store_regs(&ctx, offset_gpr, offset_cp0);
- ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, regs, 1);
+ /* jump to start */
+ pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa)));
+ /* load $15 in DeSave */
+ pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, 31, 0));
+
+ ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, (uint32_t *)&mips32->core_regs, 1);
- ejtag_info->reg8 = regs[8]; /* reg8 is saved but not restored, next called function should restore it */
- ejtag_info->reg9 = regs[9];
pracc_queue_free(&ctx);
+
+ /* reg8 is saved but not restored, next called function should restore it */
+ ejtag_info->reg8 = mips32->core_regs.gpr[8];
+ ejtag_info->reg9 = mips32->core_regs.gpr[9];
+
+ if (ctx.retval != ERROR_OK)
+ return ctx.retval;
+
+ /* we only care if FP is actually impl'd and if cp1 is enabled */
+ /* since we already read cp0 in the prev step */
+ /* now we know what's in cp0.status */
+ fp_enabled = (mips32->core_regs.cp0[0] & BIT(MIPS32_CP0_STATUS_CU1_SHIFT)) != 0;
+ if (mips32->fp_imp && fp_enabled) {
+ pracc_queue_init(&ctx);
+
+ mips32_pracc_store_regs_set_base_addr(&ctx);
+
+ /* FCSR */
+ pracc_add(&ctx, 0, MIPS32_CFC1(ctx.isa, 8, 31));
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + offset_fpcr,
+ MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + offset_fpcr, 1));
+
+ /* FIR */
+ pracc_add(&ctx, 0, MIPS32_CFC1(ctx.isa, 8, 0));
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + offset_fpcr + 4,
+ MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + offset_fpcr + 4, 1));
+
+ /* f0 to f31 */
+ if (mips32->fpu_in_64bit) {
+ for (int i = 0; i != 32; i++) {
+ size_t offset = offset_fpr + (i * 8);
+ /* current pracc implementation (or EJTAG itself) only supports 32b access */
+ /* so there is no way to use SDC1 */
+
+ /* lower half */
+ pracc_add(&ctx, 0, MIPS32_MFC1(ctx.isa, 8, i));
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + offset,
+ MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + offset, 1));
+
+ /* upper half */
+ pracc_add(&ctx, 0, MIPS32_MFHC1(ctx.isa, 8, i));
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + offset + 4,
+ MIPS32_SW(ctx.isa, 8, PRACC_OUT_OFFSET + offset + 4, 1));
+ }
+ } else {
+ for (int i = 0; i != 32; i++) {
+ size_t offset = offset_fpr + (i * 8);
+ pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + offset,
+ MIPS32_SWC1(ctx.isa, i, PRACC_OUT_OFFSET + offset, 1));
+ }
+ }
+
+ mips32_pracc_store_regs_restore(&ctx);
+
+ /* jump to start */
+ pracc_add(&ctx, 0, MIPS32_B(ctx.isa, NEG16((ctx.code_count + 1) << ctx.isa)));
+ /* load $15 in DeSave */
+ pracc_add(&ctx, 0, MIPS32_MTC0(ctx.isa, 15, 31, 0));
+
+ ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, (uint32_t *)&mips32->core_regs, 1);
+
+ pracc_queue_free(&ctx);
+ }
return ctx.retval;
}
+/**
+ * mips32_pracc_fastdata_xfer_synchronize_cache - Synchronize cache for fast data transfer
+ * @param[in] ejtag_info: EJTAG information structure
+ * @param[in] addr: Starting address for cache synchronization
+ * @param[in] size: Size of each data element
+ * @param[in] count: Number of data elements
+ *
+ * @brief Synchronizes the cache for fast data transfer based on
+ * the specified address and cache configuration.
+ * If the region is cacheable (write-back cache or write-through cache),
+ * it synchronizes the cache for the specified range.
+ * The synchronization is performed using the MIPS32 cache synchronization function.
+ *
+ * @return ERROR_OK on success; error code on failure.
+ */
+static int mips32_pracc_fastdata_xfer_synchronize_cache(struct mips_ejtag *ejtag_info,
+ uint32_t addr, int size, int count)
+{
+ int retval = ERROR_OK;
+
+ if ((KSEGX(addr) == KSEG1) || (addr >= 0xff200000 && addr <= 0xff3fffff)) // DESEG?
+ return retval; /*Nothing to do*/
+
+ int cached = 0;
+ uint32_t conf = 0;
+
+ mips32_cp0_read(ejtag_info, &conf, 16, 0);
+
+ switch (KSEGX(addr)) {
+ case KUSEG:
+ cached = (conf & MIPS32_CONFIG0_KU_MASK) >> MIPS32_CONFIG0_KU_SHIFT;
+ break;
+ case KSEG0:
+ cached = (conf & MIPS32_CONFIG0_K0_MASK) >> MIPS32_CONFIG0_K0_SHIFT;
+ break;
+ case KSEG2:
+ case KSEG3:
+ cached = (conf & MIPS32_CONFIG0_K23_MASK) >> MIPS32_CONFIG0_K23_SHIFT;
+ break;
+ default:
+ /* what ? */
+ break;
+ }
+
+ /**
+ * Check cacheability bits coherency algorithm
+ * is the region cacheable or uncached.
+ * If cacheable we have to synchronize the cache
+ */
+ if (cached == 3 || cached == 0) { /* Write back cache or write through cache */
+ uint32_t start_addr = addr;
+ uint32_t end_addr = addr + count * size;
+ uint32_t rel = (conf & MIPS32_CONFIG0_AR_MASK) >> MIPS32_CONFIG0_AR_SHIFT;
+ /* FIXME: In MIPS Release 6, the encoding of CACHE instr has changed */
+ if (rel > MIPS32_RELEASE_2) {
+ LOG_DEBUG("Unsupported MIPS Release ( > 5)");
+ return ERROR_FAIL;
+ }
+ retval = mips32_pracc_synchronize_cache(ejtag_info, start_addr, end_addr, cached, rel);
+ }
+
+ return retval;
+}
+
/* fastdata upload/download requires an initialized working area
* to load the download code; it should not be called otherwise
* fetch order from the fastdata area
/* start of fastdata area in t0 */
MIPS32_LUI(isa, 8, UPPER16(MIPS32_PRACC_FASTDATA_AREA)),
MIPS32_ORI(isa, 8, 8, LOWER16(MIPS32_PRACC_FASTDATA_AREA)),
- MIPS32_LW(isa, 9, 0, 8), /* start addr in t1 */
- MIPS32_LW(isa, 10, 0, 8), /* end addr to t2 */
- /* loop: */
+ MIPS32_LW(isa, 9, 0, 8), /* start addr in t1 */
+ mips32_cpu_support_sync(ejtag_info) ? MIPS32_SYNC(isa) : MIPS32_NOP, /* barrier for ordering */
+ MIPS32_LW(isa, 10, 0, 8), /* end addr to t2 */
+ mips32_cpu_support_sync(ejtag_info) ? MIPS32_SYNC(isa) : MIPS32_NOP, /* barrier for ordering */
+ /* loop: */
write_t ? MIPS32_LW(isa, 11, 0, 8) : MIPS32_LW(isa, 11, 0, 9), /* from xfer area : from memory */
write_t ? MIPS32_SW(isa, 11, 0, 9) : MIPS32_SW(isa, 11, 0, 8), /* to memory : to xfer area */
- MIPS32_BNE(isa, 10, 9, NEG16(3 << isa)), /* bne $t2,t1,loop */
+ mips32_cpu_support_sync(ejtag_info) ? MIPS32_SYNC(isa) : MIPS32_NOP, /* barrier for ordering */
+
+ MIPS32_BNE(isa, 10, 9, NEG16(4 << isa)), /* bne $t2,t1,loop */
MIPS32_ADDI(isa, 9, 9, 4), /* addi t1,t1,4 */
MIPS32_LW(isa, 8, MIPS32_FASTDATA_HANDLER_SIZE - 4, 15),
MIPS32_LUI(isa, 15, UPPER16(MIPS32_PRACC_TEXT)),
MIPS32_ORI(isa, 15, 15, LOWER16(MIPS32_PRACC_TEXT) | isa), /* isa bit for JR instr */
- MIPS32_JR(isa, 15), /* jr start */
+ mips32_cpu_support_hazard_barrier(ejtag_info)
+ ? MIPS32_JRHB(isa, 15)
+ : MIPS32_JR(isa, 15), /* jr start */
MIPS32_MFC0(isa, 15, 31, 0), /* move COP0 DeSave to $15 */
};
uint32_t jmp_code[] = {
MIPS32_LUI(isa, 15, UPPER16(source->address)), /* load addr of jump in $15 */
MIPS32_ORI(isa, 15, 15, LOWER16(source->address) | isa), /* isa bit for JR instr */
- MIPS32_JR(isa, 15), /* jump to ram program */
+ mips32_cpu_support_hazard_barrier(ejtag_info)
+ ? MIPS32_JRHB(isa, 15)
+ : MIPS32_JR(isa, 15), /* jump to ram program */
isa ? MIPS32_XORI(isa, 15, 15, 1) : MIPS32_NOP, /* drop isa bit, needed for LW/SW instructions */
};
unsigned num_clocks = 0; /* like in legacy code */
if (ejtag_info->mode != 0)
- num_clocks = ((uint64_t)(ejtag_info->scan_delay) * jtag_get_speed_khz() + 500000) / 1000000;
+ num_clocks = ((uint64_t)(ejtag_info->scan_delay) * adapter_get_speed_khz() + 500000) / 1000000;
for (int i = 0; i < count; i++) {
jtag_add_clocks(num_clocks);
if (ejtag_info->pa_addr != MIPS32_PRACC_TEXT)
LOG_ERROR("mini program did not return to start");
- return retval;
+ return mips32_pracc_fastdata_xfer_synchronize_cache(ejtag_info, addr, 4, count);
}