mips32: add micromips breakpoints support
[fw/openocd] / src / target / mips_m4k.c
index 7ab30fe0ea983d7abc570e049b1b58fae8e5603b..e2007d99efdc58d400698d8a780daef9cb4cbd2c 100644 (file)
@@ -631,6 +631,11 @@ static int mips_m4k_set_breakpoint(struct target *target,
                comparator_list[bp_num].used = 1;
                comparator_list[bp_num].bp_value = breakpoint->address;
 
+               if (breakpoint->length != 4)                    /* make sure isa bit set */
+                       comparator_list[bp_num].bp_value |= 1;
+               else                                            /* make sure isa bit cleared */
+                       comparator_list[bp_num].bp_value &= ~1;
+
                /* EJTAG 2.0 uses 30bit IBA. First 2 bits are reserved.
                 * Warning: there is no IB ASID registers in 2.0.
                 * Do not set it! :) */
@@ -648,41 +653,77 @@ static int mips_m4k_set_breakpoint(struct target *target,
                                  bp_num, comparator_list[bp_num].bp_value);
        } else if (breakpoint->type == BKPT_SOFT) {
                LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id);
-               if (breakpoint->length == 4) {
+
+               uint32_t isa_req = breakpoint->length & 1;      /* micro mips request bit */
+               uint32_t bplength = breakpoint->length & ~1;    /* drop micro mips request bit for length */
+               uint32_t bpaddr = breakpoint->address & ~1;     /* drop isa bit from address, if set */
+
+               if (bplength == 4) {
                        uint32_t verify = 0xffffffff;
+                       uint32_t sdbbp32_instr = MIPS32_SDBBP(isa_req);
+                       if (ejtag_info->endianness && isa_req)
+                               sdbbp32_instr = SWAP16(sdbbp32_instr);
 
-                       retval = target_read_memory(target, breakpoint->address, breakpoint->length, 1,
-                                       breakpoint->orig_instr);
-                       if (retval != ERROR_OK)
-                               return retval;
-                       retval = target_write_u32(target, breakpoint->address, MIPS32_SDBBP(ejtag_info->isa));
-                       if (retval != ERROR_OK)
-                               return retval;
+                       if ((breakpoint->address & 3) == 0) {   /* word alligned */
 
-                       retval = target_read_u32(target, breakpoint->address, &verify);
-                       if (retval != ERROR_OK)
-                               return retval;
-                       if (verify != MIPS32_SDBBP(ejtag_info->isa)) {
-                               LOG_ERROR("Unable to set 32-bit breakpoint at address " TARGET_ADDR_FMT
-                                               " - check that memory is read/writable", breakpoint->address);
+                               retval = target_read_memory(target, bpaddr, bplength, 1, breakpoint->orig_instr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               retval = target_write_u32(target, bpaddr, sdbbp32_instr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               retval = target_read_u32(target, bpaddr, &verify);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               if (verify != sdbbp32_instr)
+                                       verify = 0;
+
+                       } else {        /* 16 bit aligned */
+                               retval = target_read_memory(target, bpaddr, 2, 2, breakpoint->orig_instr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               uint8_t sdbbp_buf[4];
+                               target_buffer_set_u32(target, sdbbp_buf, sdbbp32_instr);
+
+                               retval = target_write_memory(target, bpaddr, 2, 2, sdbbp_buf);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               retval = target_read_memory(target, bpaddr, 2, 2, sdbbp_buf);
+                               if (retval != ERROR_OK)
+                                       return retval;
+
+                               if (target_buffer_get_u32(target, sdbbp_buf) != sdbbp32_instr)
+                                       verify = 0;
+                       }
+
+                       if (verify == 0) {
+                               LOG_ERROR("Unable to set 32bit breakpoint at address %08" PRIx64
+                                       " - check that memory is read/writable", breakpoint->address);
                                return ERROR_OK;
                        }
+
                } else {
                        uint16_t verify = 0xffff;
 
-                       retval = target_read_memory(target, breakpoint->address, breakpoint->length, 1,
-                                       breakpoint->orig_instr);
+                       retval = target_read_memory(target, bpaddr, bplength, 1, breakpoint->orig_instr);
                        if (retval != ERROR_OK)
                                return retval;
-                       retval = target_write_u16(target, breakpoint->address, MIPS16_SDBBP(ejtag_info->isa));
+
+                       retval = target_write_u16(target, bpaddr, MIPS16_SDBBP(isa_req));
                        if (retval != ERROR_OK)
                                return retval;
 
-                       retval = target_read_u16(target, breakpoint->address, &verify);
+                       retval = target_read_u16(target, bpaddr, &verify);
                        if (retval != ERROR_OK)
                                return retval;
-                       if (verify != MIPS16_SDBBP(ejtag_info->isa)) {
-                               LOG_ERROR("Unable to set 16-bit breakpoint at address " TARGET_ADDR_FMT
+
+                       if (verify != MIPS16_SDBBP(isa_req)) {
+                               LOG_ERROR("Unable to set 16bit breakpoint at address %08" PRIx64
                                                " - check that memory is read/writable", breakpoint->address);
                                return ERROR_OK;
                        }
@@ -725,46 +766,58 @@ static int mips_m4k_unset_breakpoint(struct target *target,
 
        } else {
                /* restore original instruction (kept in target endianness) */
+               uint32_t isa_req = breakpoint->length & 1;
+               uint32_t bplength = breakpoint->length & ~1;
+               uint8_t current_instr[4];
                LOG_DEBUG("bpid: %" PRIu32, breakpoint->unique_id);
-               if (breakpoint->length == 4) {
-                       uint32_t current_instr;
-
-                       /* check that user program has not modified breakpoint instruction */
-                       retval = target_read_memory(target, breakpoint->address, 4, 1,
-                                       (uint8_t *)&current_instr);
-                       if (retval != ERROR_OK)
-                               return retval;
-
-                       /**
-                        * target_read_memory() gets us data in _target_ endianess.
-                        * If we want to use this data on the host for comparisons with some macros
-                        * we must first transform it to _host_ endianess using target_buffer_get_u32().
-                        */
-                       current_instr = target_buffer_get_u32(target, (uint8_t *)&current_instr);
-
-                       if (current_instr == MIPS32_SDBBP(ejtag_info->isa)) {
-                               retval = target_write_memory(target, breakpoint->address, 4, 1,
-                                               breakpoint->orig_instr);
+               if (bplength == 4) {
+                       uint32_t sdbbp32_instr =  MIPS32_SDBBP(isa_req);
+                       if (ejtag_info->endianness && isa_req)
+                               sdbbp32_instr = SWAP16(sdbbp32_instr);
+
+                       if ((breakpoint->address & 3) == 0) {           /* 32bit aligned */
+                               /* check that user program has not modified breakpoint instruction */
+                               retval = target_read_memory(target, breakpoint->address, 4, 1, current_instr);
+                               if (retval != ERROR_OK)
+                                       return retval;
+                               /**
+                               * target_read_memory() gets us data in _target_ endianess.
+                               * If we want to use this data on the host for comparisons with some macros
+                               * we must first transform it to _host_ endianess using target_buffer_get_u16().
+                               */
+                               if (sdbbp32_instr == target_buffer_get_u32(target, current_instr)) {
+                                       retval = target_write_memory(target, breakpoint->address, 4, 1,
+                                                                               breakpoint->orig_instr);
+                                       if (retval != ERROR_OK)
+                                               return retval;
+                               }
+                       } else {        /* 16bit alligned */
+                               retval = target_read_memory(target, breakpoint->address, 2, 2, current_instr);
                                if (retval != ERROR_OK)
                                        return retval;
+
+                               if (sdbbp32_instr == target_buffer_get_u32(target, current_instr)) {
+                                       retval = target_write_memory(target, breakpoint->address, 2, 2,
+                                                                               breakpoint->orig_instr);
+                                       if (retval != ERROR_OK)
+                                               return retval;
+                               }
                        }
                } else {
-                       uint16_t current_instr;
-
                        /* check that user program has not modified breakpoint instruction */
-                       retval = target_read_memory(target, breakpoint->address, 2, 1,
-                                       (uint8_t *)&current_instr);
+                       retval = target_read_memory(target, breakpoint->address, 2, 1, current_instr);
                        if (retval != ERROR_OK)
                                return retval;
-                       current_instr = target_buffer_get_u16(target, (uint8_t *)&current_instr);
-                       if (current_instr == MIPS16_SDBBP(ejtag_info->isa)) {
+
+                       if (target_buffer_get_u16(target, current_instr) == MIPS16_SDBBP(isa_req)) {
                                retval = target_write_memory(target, breakpoint->address, 2, 1,
-                                               breakpoint->orig_instr);
+                                                                       breakpoint->orig_instr);
                                if (retval != ERROR_OK)
                                        return retval;
                        }
                }
        }
+
        breakpoint->set = 0;
 
        return ERROR_OK;
@@ -774,6 +827,12 @@ static int mips_m4k_add_breakpoint(struct target *target, struct breakpoint *bre
 {
        struct mips32_common *mips32 = target_to_mips32(target);
 
+       if ((breakpoint->length > 5 || breakpoint->length < 2) ||               /* out of range */
+               (breakpoint->length == 4 && (breakpoint->address & 2)) ||       /* mips32 unaligned */
+               (mips32->isa_imp == MIPS32_ONLY && breakpoint->length != 4) ||  /* misp32 specific */
+               ((mips32->isa_imp & 1) != (breakpoint->length & 1)))            /* isa not implemented */
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+
        if (breakpoint->type == BKPT_HARD) {
                if (mips32->num_inst_bpoints_avail < 1) {
                        LOG_INFO("no hardware breakpoint available");