This is an automated email from Gerrit. "Walter Ji <walter...@oss.cipunited.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/7874
-- gerrit commit 237268e2b7ab3406575f09b441ccbfee5f1d4d28 Author: Walter Ji <walter...@oss.cipunited.com> Date: Mon Aug 28 16:24:17 2023 +0800 target/mips32: add cache, tlb and guest(vz) related operation Add cache invalidate function and its command. Add dump tlb function and its monitor command. Add virtualisation/guest related function and its command. Checkpatch-ignore: CAMELCASE Change-Id: Ib019e05cc66aeced99026f6e95a85f728e5a2058 Signed-off-by: Walter Ji <walter...@oss.cipunited.com> diff --git a/src/target/mips32.c b/src/target/mips32.c index ad7071683c..689626d2c0 100644 --- a/src/target/mips32.c +++ b/src/target/mips32.c @@ -234,6 +234,18 @@ static uint32_t ftlb_sets[16] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 0, 0, 0, 0, #define L2 5 +static const struct { + unsigned int option; + const char *arg; +} invalidate_cmd[5] = { + { ALL, "all", }, + { INST, "inst", }, + { DATA, "data", }, + { ALLNOWB, "allnowb", }, + { DATANOWB, "datanowb", }, +}; + + static int mips32_get_core_reg(struct reg *reg) { int retval; @@ -1768,6 +1780,449 @@ exit: pracc_queue_free(&ctx); return ctx.retval; } + + + +int mips32_pracc_read_tlb_entry(struct mips_ejtag *ejtag_info, uint32_t *data, uint32_t index) +{ + int isa = 0; + + struct pracc_queue_info ctx = { + .max_code = 49, + .ejtag_info = ejtag_info + }; + + pracc_queue_init(&ctx); + if (ctx.retval != ERROR_OK) + goto exit; + + int tlb_read_code[] = { + MIPS32_MFC0(isa, 8, 2, 0), /* Read MIPS32_C0_ENTRYLO0 */ + MIPS32_MFC0(isa, 8, 3, 0), /* Read MIPS32_C0_ENTRYLO1 */ + MIPS32_MFC0(isa, 8, 10, 0), /* Read MIPS32_C0_ENTRYHI */ + MIPS32_MFC0(isa, 8, 5, 0) /* Read MIPS32_C0_PAGEMASK */ + }; + + pracc_add(&ctx, 0, MIPS32_LUI(isa, 15, PRACC_UPPER_BASE_ADDR)); /* $15 = MIP32_PRACC_BASE_ADDR */ + pracc_add(&ctx, 0, MIPS32_LUI(isa, 8, UPPER16(index))); /* Load TLB Index to $8 */ + pracc_add(&ctx, 0, MIPS32_ORI(isa, 8, 8, LOWER16(index))); + + pracc_add(&ctx, 0, MIPS32_MTC0(isa, 8, 0, 0)); /* write MIPS32_C0_Index */ + pracc_add(&ctx, 0, MIPS32_ISA_TLBR()); /* Read TLB entry specified by Index */ + + + for (uint32_t i = 0; i < ARRAY_SIZE(tlb_read_code); i++) { + pracc_add(&ctx, 0, tlb_read_code[i]); + pracc_add(&ctx, MIPS32_PRACC_PARAM_OUT + (i * 4), + MIPS32_SW(isa, 8, PRACC_OUT_OFFSET + (i * 4), 15)); + } + + pracc_add(&ctx, 0, MIPS32_LUI(isa, 8, UPPER16(ejtag_info->reg8))); /* restore upper 16 of $8 */ + pracc_add(&ctx, 0, MIPS32_ORI(isa, 8, 8, LOWER16(ejtag_info->reg8))); /* restore lower 16 of $8 */ + + pracc_add(&ctx, 0, MIPS32_SYNC(isa)); + pracc_add(&ctx, 0, MIPS32_B(isa, NEG16(ctx.code_count + 1))); /* jump to start */ + pracc_add(&ctx, 0, MIPS32_MTC0(isa, 15, 31, 0)); /* load $15 in DeSave */ + + ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, data, 1); +exit: + pracc_queue_free(&ctx); + return ctx.retval; +} + + +int mips32_pracc_invalidate_cache(struct target *target, struct mips_ejtag *ejtag_info, int cache) +{ + int isa = 0; + + uint32_t inv_inst_cache[] = { + /* Determine how big the I$ is */ + MIPS32_MFC0(isa, t7, 16, 1), /* MIPS32_C0_Config1 */ + MIPS32_ADDIU(isa, t1, t7, zero), + MIPS32_SRL(isa, t1, t7, MIPS32_CFG1_ISSHIFT), + MIPS32_ANDI(isa, t1, t1, 0x7), + MIPS32_ADDIU(isa, t0, zero, 64), /* li t0, 64 */ + MIPS32_SLLV(isa, t1, t0, t1), /* I$ Sets per way */ + + MIPS32_SRL(isa, t7, t7, MIPS32_CFG1_IASHIFT), + MIPS32_ANDI(isa, t7, t7, 0x7), + MIPS32_ADDIU(isa, t7, t7, 1), + MIPS32_MUL(isa, t1, t1, t7), /* Total number of sets */ + + /* Clear TagLo/TagHi registers */ + MIPS32_MTC0(isa, zero, MIPS32_C0_ITAGLO, 0), /* MIPS32_C0_ITagLo */ + MIPS32_MTC0(isa, zero, MIPS32_C0_ITAGHI, 0), /* MIPS32_C0_ITagHi */ + MIPS32_MFC0(isa, t7, 16, 1), /* Re-read MIPS32_C0_Config1 */ + + /* Isolate I$ Line Size */ + MIPS32_ADDIU(isa, t0, zero, 2), /* li a2, 2 */ + MIPS32_SRL(isa, t7, t7, MIPS32_CFG1_ILSHIFT), + MIPS32_ANDI(isa, t7, t7, 0x7), + + MIPS32_SLLV(isa, t7, t0, t7), /* Now have true I$ line size in bytes */ + MIPS32_LUI(isa, t0, 0x8000), /* Get a KSeg0 address for cacheops */ + + MIPS32_CACHE(isa, Index_Store_Tag_I, 0, t0), + MIPS32_ADDI(isa, t1, t1, NEG16(1)), /* Decrement set counter */ + MIPS32_BNE(isa, t1, zero, NEG16(3)), + MIPS32_ISA_ADD(t0, t0, t7), + }; + + uint32_t inv_data_cache[] = { + MIPS32_MFC0(isa, t7, 16, 1), /* read MIPS32_C0_Config1 */ + + MIPS32_SRL(isa, t1, t7, MIPS32_CFG1_DSSHIFT), /* extract DS */ + MIPS32_ANDI(isa, t1, t1, 0x7), + MIPS32_ADDIU(isa, t0, zero, 64), /* li t0, 64 */ + MIPS32_SLLV(isa, t1, t0, t1), /* D$ Sets per way */ + + MIPS32_SRL(isa, t7, t7, MIPS32_CFG1_DASHIFT), /* extract DA */ + MIPS32_ANDI(isa, t7, t7, 0x7), + MIPS32_ADDIU(isa, t7, t7, 1), + MIPS32_MUL(isa, t1, t1, t7), /* Total number of sets */ + + /* Clear TagLo/TagHi registers */ + MIPS32_MTC0(isa, zero, MIPS32_C0_TAGLO, 0), /* write MIPS32_C0_TagLo */ + MIPS32_MTC0(isa, zero, MIPS32_C0_TAGHI, 0), /* write MIPS32_C0_TagHi */ + MIPS32_MTC0(isa, zero, MIPS32_C0_TAGLO, 2), /* write MIPS32_C0_DTagLo */ + MIPS32_MTC0(isa, zero, MIPS32_C0_TAGHI, 2), /* write MIPS32_C0_DTagHi */ + + /* Isolate D$ Line Size */ + MIPS32_MFC0(isa, t7, 16, 1), /* Re-read MIPS32_C0_Config1 */ + MIPS32_ADDIU(isa, t0, zero, 2), /* li a2, 2 */ + + MIPS32_SRL(isa, t7, t7, MIPS32_CFG1_DLSHIFT), /* extract DL */ + MIPS32_ANDI(isa, t7, t7, 0x7), + + MIPS32_SLLV(isa, t7, t0, t7), /* Now have true I$ line size in bytes */ + + MIPS32_LUI(isa, t0, 0x8000) /* Get a KSeg0 address for cacheops */ + }; + + uint32_t done[] = { + MIPS32_LUI(isa, t7, UPPER16(MIPS32_PRACC_TEXT)), + MIPS32_ORI(isa, t7, t7, LOWER16(MIPS32_PRACC_TEXT)), + MIPS32_JR(isa, t7), /* jr start */ + MIPS32_MFC0(isa, t7, 31, 0) /* move COP0 DeSave to $15 */ + }; + + struct pracc_queue_info ctx = { + .max_code = 26 + 20, /* alloc memory for the worst case */ + .ejtag_info = ejtag_info + }; + + uint32_t conf; + uint32_t bpl; + + pracc_queue_init(&ctx); + if (ctx.retval != ERROR_OK) { + LOG_ERROR("pracc_queue_init failed"); + goto exit; + } + + /* Read Config1 Register to retrieve cache info */ + mips32_cp0_read(ejtag_info, &conf, 16, 1); + + switch (cache) { + case INST: + /* Extract cache line size */ + bpl = (conf >> MIPS32_CFG1_ILSHIFT) & 7; /* bit 21:19 */ + + /* Core configured with Instruction cache */ + if (bpl == 0) { + LOG_USER("no instructure cache configured"); + ctx.retval = ERROR_OK; + goto exit; + } + + for (unsigned int i = 0; i < ARRAY_SIZE(inv_inst_cache); i++) + pracc_add(&ctx, 0, inv_inst_cache[i]); + + break; + + case DATA: + case ALLNOWB: + case DATANOWB: + /* Extract cache line size */ + bpl = (conf >> MIPS32_CFG1_DLSHIFT) & 7; /* bit 12:10 */ + + /* Core configured with Instruction cache */ + if (bpl == 0) { + LOG_USER("no data cache configured"); + ctx.retval = ERROR_OK; + goto exit; + } + + /* Write exit code */ + for (unsigned int i = 0; i < ARRAY_SIZE(inv_data_cache); i++) + pracc_add(&ctx, 0, inv_data_cache[i]); + + if (cache == DATA) { + // Writes back when invalidating data cache + pracc_add(&ctx, 0, MIPS32_CACHE(isa, Index_Writeback_Inv_D, 0, t0)); + } else { + if (cache == ALLNOWB || cache == DATANOWB) + pracc_add(&ctx, 0, MIPS32_CACHE(isa, Index_Store_Tag_D, 0, t0)); + } + + /* Decrement set counter */ + pracc_add(&ctx, 0, MIPS32_ADDI(isa, t1, t1, NEG16(1))); + pracc_add(&ctx, 0, MIPS32_BNE(isa, t1, zero, NEG16(3))); + pracc_add(&ctx, 0, MIPS32_ISA_ADD(t0, t0, t7)); + break; + + case L2: + break; + } + + /* Write exit code */ + for (unsigned int i = 0; i < ARRAY_SIZE(done); i++) + pracc_add(&ctx, 0, done[i]); + + /* Start code execution */ + ctx.code_count = 0; /* Disable pracc access verification due BNZ instruction */ + ctx.retval = mips32_pracc_queue_exec(ejtag_info, &ctx, NULL, 1); + if (ctx.retval != ERROR_OK) + LOG_DEBUG("mips32_pracc_queue_exec failed - ctx.retval: %d", ctx.retval); + +exit: + pracc_queue_free(&ctx); + return ctx.retval; +} + +static uint32_t mips32_determine_guestid_width(struct mips_ejtag *ejtag_info, uint32_t *width) +{ + /* if GuestCtl1 is implemented { + * save current GuestCtl1 value + * write GuestCtl1, placing all-ones in the ID field + * read GuestCtl1 + * in read value, count the consecutive number of 1 bits (starting from bit position 0) + * restore GuestCtl1 value + * } else width is 0 + */ + + uint32_t err = ERROR_OK; + uint32_t err2 = ERROR_OK; + uint32_t count = 0; + bool guestCtl1Clobbered = false; + uint32_t guestCtl0 = 0; + uint32_t guestCtl1 = 0; + uint32_t tempReg = 0; + + err = mips32_cp0_read(ejtag_info, &guestCtl0, 12, 6); + if (err != ERROR_OK) + goto CLEANUP; + + if (((guestCtl0 >> 22) & 0x1) != 0) { + err = mips32_cp0_read(ejtag_info, &guestCtl1, 10, 4); + if (err != ERROR_OK) + goto CLEANUP; + + + guestCtl1Clobbered = true; + tempReg = guestCtl1; + do { + tempReg = (((tempReg) & ~0xFF) | 0xFF); + } while (0); + + err = mips32_cp0_write(ejtag_info, tempReg, 10, 4); + if (err != ERROR_OK) + goto CLEANUP; + + err = mips32_cp0_read(ejtag_info, &tempReg, 10, 4); + if (err != ERROR_OK) + goto CLEANUP; + + while (count < 8 && (tempReg & 0x1) != 0) { + count++; + tempReg >>= 1; + } + } + +CLEANUP: + if (guestCtl1Clobbered) { + /* restore */ + err2 = mips32_cp0_write(ejtag_info, guestCtl1, 10, 4); + } + *width = count; + return (err != ERROR_OK ? err : err2); +} + +COMMAND_HANDLER(mips32_handle_guest_id) +{ + uint32_t value; + struct target *target = get_current_target(CMD_CTX); + struct mips32_common *mips32 = target_to_mips32(target); + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + + mips32_cp0_read(ejtag_info, &value, 10, 4); + command_print(CMD, "guest id: %d", value & 0xff); + return ERROR_OK; +} + + +COMMAND_HANDLER(mips32_handle_invalidate_cache_command) +{ + int retval = -1; + struct target *target = get_current_target(CMD_CTX); + struct mips32_common *mips32 = target_to_mips32(target); + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + int i = 0; + + static const char * const cache_msg[] = {"all", "instr", "data", "L23", NULL, NULL, NULL}; + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (CMD_ARGC >= 2 || CMD_ARGC == 0) { + LOG_DEBUG("ERROR_COMMAND_SYNTAX_ERROR"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (CMD_ARGC == 1) { + /* PARSE command options - all/inst/data/allnowb/datanowb */ + for (i = 0; i < 5 ; i++) { + if (strcmp(CMD_ARGV[0], invalidate_cmd[i].arg) == 0) { + switch (invalidate_cmd[i].option) { + case ALL: + LOG_INFO("clearing %s cache", cache_msg[1]); + /* For this case - ignore any errors checks, just in case core has no instruction cache */ + mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[1].option); + + /* TODO: Add L2 code */ + + LOG_INFO("clearing %s cache", cache_msg[2]); + /* For this case - ignore any errors checks, just in case core has no data cache */ + mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[2].option); + + break; + + case INST: + LOG_INFO("clearing %s cache", cache_msg[invalidate_cmd[i].option]); + retval = mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[i].option); + if (retval != ERROR_OK) + return retval; + + break; + + case DATA: + LOG_INFO("clearing %s cache", cache_msg[invalidate_cmd[i].option]); + retval = mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[i].option); + if (retval != ERROR_OK) + return retval; + + break; + + case ALLNOWB: + LOG_INFO("invalidating %s cache", cache_msg[invalidate_cmd[1].option]); + retval = mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[1].option); + if (retval != ERROR_OK) + return retval; + + /* TODO: Add L2 code */ + + LOG_INFO("invalidating %s cache - no writeback", cache_msg[2]); + retval = mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[2].option); + if (retval != ERROR_OK) + return retval; + + break; + + case DATANOWB: + LOG_INFO("invalidating %s cache - no writeback", cache_msg[2]); + retval = mips32_pracc_invalidate_cache(target, ejtag_info, invalidate_cmd[i].option); + if (retval != ERROR_OK) + return retval; + break; + + default: + LOG_ERROR("Invalid command argument '%s' not found", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + if (retval == ERROR_FAIL) + return ERROR_FAIL; + break; + } + if (i >= DATANOWB) { + LOG_ERROR("Invalid command argument '%s' not found", CMD_ARGV[0]); + return ERROR_COMMAND_SYNTAX_ERROR; + } + } + } + return ERROR_OK; +} + +COMMAND_HANDLER(mips32_handle_dump_tlb_command) +{ + struct target *target = get_current_target(CMD_CTX); + struct mips32_common *mips32 = target_to_mips32(target); + struct mips_ejtag *ejtag_info = &mips32->ejtag_info; + int retval; + + uint32_t data[4]; + + if (target->state != TARGET_HALTED) { + LOG_WARNING("target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if (CMD_ARGC >= 2) { + LOG_DEBUG("ERROR_COMMAND_SYNTAX_ERROR"); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + uint32_t config; /* cp0 config - 16, 0 */ + uint32_t config1; /* cp0 config1 - 16, 1 */ + + /* Read Config register */ + retval = mips32_cp0_read(ejtag_info, &config, 16, 0); + if (retval != ERROR_OK) + return retval; + + /* Read Config1 register */ + retval = mips32_cp0_read(ejtag_info, &config1, 16, 1); + if (retval != ERROR_OK) + return retval; + + uint32_t mmu_type; + uint32_t tlb_entries; + + mmu_type = (config >> 7) & 7; + if (mmu_type == 0) { + LOG_USER("mmu_type: %d, No TLB configured", mmu_type); + return ERROR_OK; + } + + if (CMD_ARGC == 0 || CMD_ARGC == 1) { + uint32_t i = 0; + + /* Get number of TLB entries */ + if (CMD_ARGC == 1) { + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[0], i); + tlb_entries = (((config1 >> 25) & 0x3f) + 1); + if (i >= tlb_entries) { + LOG_USER("Invalid TLB entry specified - Valid entry #'s are 0-%d", tlb_entries - 1); + return ERROR_COMMAND_SYNTAX_ERROR; + } + + tlb_entries = i + 1; + } else { + // Get number of TLB entries from config1 + tlb_entries = (((config1 >> 25) & 0x3f) + 1); + } + + LOG_USER("index\t entrylo0\t entrylo1\t entryhi\t pagemask"); + for (; i < tlb_entries; i++) { + mips32_pracc_read_tlb_entry(ejtag_info, &data[0], i); + command_print(CMD, " %d\t0x%8.8x\t0x%8.8x\t0x%8.8x\t0x%8.8x", i, data[0], data[1], data[2], data[3]); + } + } + return ERROR_OK; +} + COMMAND_HANDLER(mips32_handle_cpuinfo_command) { int retval; @@ -1842,6 +2297,28 @@ COMMAND_HANDLER(mips32_handle_cpuinfo_command) info.vzase = (config3 & (1 << 23)) ? 1 : 0; /* VZ */ LOG_USER("VZ Module: %d", info.vzase); + /* Check if Virtualization supported */ + /* TODO List */ + if (info.vzase) { + /* Core supports Virtualization - now get Guest Info */ + uint32_t width; + uint32_t guestCtl0; + + retval = mips32_cp0_read(ejtag_info, &guestCtl0, 12, 6); + if (retval != ERROR_OK) + return retval; + + info.guest_ctl1_present = (guestCtl0 >> 22) & 0x1; + + retval = mips32_determine_guestid_width(ejtag_info, &width); + if (retval != ERROR_OK) + return retval; + + info.vz_guest_id_width = width; + + LOG_USER("guest ID width: %d", info.vz_guest_id_width); + } + /* MIPS SIMD Architecture (MSA) */ info.msa = (config3 & 0x10000000) ? 1 : 0; @@ -2591,6 +3068,27 @@ static const struct command_registration mips32_exec_command_handlers[] = { .help = "read ejtag registers", .usage = "", }, + { + .name = "invalidate", + .handler = mips32_handle_invalidate_cache_command, + .mode = COMMAND_EXEC, + .help = "Invalidate either or both the instruction and data caches.", + .usage = "all|inst|data|allnowb|datanowb", + }, + { + .name = "dump_tlb", + .handler = mips32_handle_dump_tlb_command, + .mode = COMMAND_ANY, + .help = "dump_tlb", + .usage = "[entry]", + }, + { + .name = "guest_id", + .handler = mips32_handle_guest_id, + .mode = COMMAND_ANY, + .help = "Display the current guest ID.", + .usage = "", + }, COMMAND_REGISTRATION_DONE }; --