+/* Create a register list that's the union of all the registers of the SMP
+ * group this target is in. If the target is not part of an SMP group, this
+ * returns the same as target_get_gdb_reg_list_noread().
+ */
+static int smp_reg_list_noread(struct target *target,
+ struct reg **combined_list[], int *combined_list_size,
+ enum target_register_class reg_class)
+{
+ if (!target->smp)
+ return target_get_gdb_reg_list_noread(target, combined_list,
+ combined_list_size, REG_CLASS_ALL);
+
+ unsigned int combined_allocated = 256;
+ struct reg **local_list = malloc(combined_allocated * sizeof(struct reg *));
+ if (!local_list) {
+ LOG_ERROR("malloc(%zu) failed", combined_allocated * sizeof(struct reg *));
+ return ERROR_FAIL;
+ }
+ unsigned int local_list_size = 0;
+
+ struct target_list *head;
+ foreach_smp_target(head, target->smp_targets) {
+ if (!target_was_examined(head->target))
+ continue;
+
+ struct reg **reg_list = NULL;
+ int reg_list_size;
+ int result = target_get_gdb_reg_list_noread(head->target, ®_list,
+ ®_list_size, reg_class);
+ if (result != ERROR_OK) {
+ free(local_list);
+ return result;
+ }
+ for (int i = 0; i < reg_list_size; i++) {
+ bool found = false;
+ struct reg *a = reg_list[i];
+ if (a->exist) {
+ /* Nested loop makes this O(n^2), but this entire function with
+ * 5 RISC-V targets takes just 2ms on my computer. Fast enough
+ * for me. */
+ for (unsigned int j = 0; j < local_list_size; j++) {
+ struct reg *b = local_list[j];
+ if (!strcmp(a->name, b->name)) {
+ found = true;
+ if (a->size != b->size) {
+ LOG_ERROR("SMP register %s is %d bits on one "
+ "target, but %d bits on another target.",
+ a->name, a->size, b->size);
+ free(reg_list);
+ free(local_list);
+ return ERROR_FAIL;
+ }
+ break;
+ }
+ }
+ if (!found) {
+ LOG_DEBUG("[%s] %s not found in combined list", target_name(target), a->name);
+ if (local_list_size >= combined_allocated) {
+ combined_allocated *= 2;
+ local_list = realloc(local_list, combined_allocated * sizeof(struct reg *));
+ if (!local_list) {
+ LOG_ERROR("realloc(%zu) failed", combined_allocated * sizeof(struct reg *));
+ free(reg_list);
+ return ERROR_FAIL;
+ }
+ }
+ local_list[local_list_size] = a;
+ local_list_size++;
+ }
+ }
+ }
+ free(reg_list);
+ }
+
+ if (local_list_size == 0) {
+ LOG_ERROR("Unable to get register list");
+ free(local_list);
+ return ERROR_FAIL;
+ }
+
+ /* Now warn the user about any registers that weren't found in every target. */
+ foreach_smp_target(head, target->smp_targets) {
+ if (!target_was_examined(head->target))
+ continue;
+
+ struct reg **reg_list = NULL;
+ int reg_list_size;
+ int result = target_get_gdb_reg_list_noread(head->target, ®_list,
+ ®_list_size, reg_class);
+ if (result != ERROR_OK) {
+ free(local_list);
+ return result;
+ }
+ for (unsigned int i = 0; i < local_list_size; i++) {
+ bool found = false;
+ struct reg *a = local_list[i];
+ for (int j = 0; j < reg_list_size; j++) {
+ struct reg *b = reg_list[j];
+ if (b->exist && !strcmp(a->name, b->name)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ LOG_WARNING("Register %s does not exist in %s, which is part of an SMP group where "
+ "this register does exist.",
+ a->name, target_name(head->target));
+ }
+ }
+ free(reg_list);
+ }
+
+ *combined_list = local_list;
+ *combined_list_size = local_list_size;
+ return ERROR_OK;
+}
+