| /* | 
 |  * Xtensa gdb server stub | 
 |  * | 
 |  * Copyright (c) 2003-2005 Fabrice Bellard | 
 |  * Copyright (c) 2013 SUSE LINUX Products GmbH | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library 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 | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, see <http://www.gnu.org/licenses/>. | 
 |  */ | 
 | #include "qemu/osdep.h" | 
 | #include "cpu.h" | 
 | #include "gdbstub/helpers.h" | 
 | #include "qemu/log.h" | 
 |  | 
 | enum { | 
 |   xtRegisterTypeArRegfile = 1,  /* Register File ar0..arXX.  */ | 
 |   xtRegisterTypeSpecialReg,     /* CPU states, such as PS, Booleans, (rsr).  */ | 
 |   xtRegisterTypeUserReg,        /* User defined registers (rur).  */ | 
 |   xtRegisterTypeTieRegfile,     /* User define register files.  */ | 
 |   xtRegisterTypeTieState,       /* TIE States (mapped on user regs).  */ | 
 |   xtRegisterTypeMapped,         /* Mapped on Special Registers.  */ | 
 |   xtRegisterTypeUnmapped,       /* Special case of masked registers.  */ | 
 |   xtRegisterTypeWindow,         /* Live window registers (a0..a15).  */ | 
 |   xtRegisterTypeVirtual,        /* PC, FP.  */ | 
 |   xtRegisterTypeUnknown | 
 | }; | 
 |  | 
 | #define XTENSA_REGISTER_FLAGS_PRIVILEGED        0x0001 | 
 | #define XTENSA_REGISTER_FLAGS_READABLE          0x0002 | 
 | #define XTENSA_REGISTER_FLAGS_WRITABLE          0x0004 | 
 | #define XTENSA_REGISTER_FLAGS_VOLATILE          0x0008 | 
 |  | 
 | void xtensa_count_regs(const XtensaConfig *config, | 
 |                        unsigned *n_regs, unsigned *n_core_regs) | 
 | { | 
 |     unsigned i; | 
 |     bool count_core_regs = true; | 
 |  | 
 |     for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) { | 
 |         if (config->gdb_regmap.reg[i].type != xtRegisterTypeTieState && | 
 |             config->gdb_regmap.reg[i].type != xtRegisterTypeMapped && | 
 |             config->gdb_regmap.reg[i].type != xtRegisterTypeUnmapped) { | 
 |             ++*n_regs; | 
 |             if (count_core_regs) { | 
 |                 if ((config->gdb_regmap.reg[i].flags & | 
 |                      XTENSA_REGISTER_FLAGS_PRIVILEGED) == 0) { | 
 |                     ++*n_core_regs; | 
 |                 } else { | 
 |                     count_core_regs = false; | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 | } | 
 |  | 
 | int xtensa_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) | 
 | { | 
 |     CPUXtensaState *env = cpu_env(cs); | 
 |     const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; | 
 | #ifdef CONFIG_USER_ONLY | 
 |     int num_regs = env->config->gdb_regmap.num_core_regs; | 
 | #else | 
 |     int num_regs = env->config->gdb_regmap.num_regs; | 
 | #endif | 
 |     unsigned i; | 
 |  | 
 |     if (n < 0 || n >= num_regs) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     switch (reg->type) { | 
 |     case xtRegisterTypeVirtual: /*pc*/ | 
 |         return gdb_get_reg32(mem_buf, env->pc); | 
 |  | 
 |     case xtRegisterTypeArRegfile: /*ar*/ | 
 |         xtensa_sync_phys_from_window(env); | 
 |         return gdb_get_reg32(mem_buf, env->phys_regs[(reg->targno & 0xff) | 
 |                                                      % env->config->nareg]); | 
 |  | 
 |     case xtRegisterTypeSpecialReg: /*SR*/ | 
 |         return gdb_get_reg32(mem_buf, env->sregs[reg->targno & 0xff]); | 
 |  | 
 |     case xtRegisterTypeUserReg: /*UR*/ | 
 |         return gdb_get_reg32(mem_buf, env->uregs[reg->targno & 0xff]); | 
 |  | 
 |     case xtRegisterTypeTieRegfile: /*f*/ | 
 |         i = reg->targno & 0x0f; | 
 |         switch (reg->size) { | 
 |         case 4: | 
 |             return gdb_get_reg32(mem_buf, | 
 |                                  float32_val(env->fregs[i].f32[FP_F32_LOW])); | 
 |         case 8: | 
 |             return gdb_get_reg64(mem_buf, float64_val(env->fregs[i].f64)); | 
 |         default: | 
 |             qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported size %d\n", | 
 |                           __func__, n, reg->size); | 
 |             return gdb_get_zeroes(mem_buf, reg->size); | 
 |         } | 
 |  | 
 |     case xtRegisterTypeWindow: /*a*/ | 
 |         return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); | 
 |  | 
 |     default: | 
 |         qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported type %d\n", | 
 |                       __func__, n, reg->type); | 
 |         return gdb_get_zeroes(mem_buf, reg->size); | 
 |     } | 
 | } | 
 |  | 
 | int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) | 
 | { | 
 |     CPUXtensaState *env = cpu_env(cs); | 
 |     uint32_t tmp; | 
 |     const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; | 
 | #ifdef CONFIG_USER_ONLY | 
 |     int num_regs = env->config->gdb_regmap.num_core_regs; | 
 | #else | 
 |     int num_regs = env->config->gdb_regmap.num_regs; | 
 | #endif | 
 |  | 
 |     if (n < 0 || n >= num_regs) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     tmp = ldl_p(mem_buf); | 
 |  | 
 |     switch (reg->type) { | 
 |     case xtRegisterTypeVirtual: /*pc*/ | 
 |         env->pc = tmp; | 
 |         break; | 
 |  | 
 |     case xtRegisterTypeArRegfile: /*ar*/ | 
 |         env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; | 
 |         xtensa_sync_window_from_phys(env); | 
 |         break; | 
 |  | 
 |     case xtRegisterTypeSpecialReg: /*SR*/ | 
 |         env->sregs[reg->targno & 0xff] = tmp; | 
 |         break; | 
 |  | 
 |     case xtRegisterTypeUserReg: /*UR*/ | 
 |         env->uregs[reg->targno & 0xff] = tmp; | 
 |         break; | 
 |  | 
 |     case xtRegisterTypeTieRegfile: /*f*/ | 
 |         switch (reg->size) { | 
 |         case 4: | 
 |             env->fregs[reg->targno & 0x0f].f32[FP_F32_LOW] = make_float32(tmp); | 
 |             return 4; | 
 |         case 8: | 
 |             env->fregs[reg->targno & 0x0f].f64 = make_float64(tmp); | 
 |             return 8; | 
 |         default: | 
 |             qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported size %d\n", | 
 |                           __func__, n, reg->size); | 
 |             return reg->size; | 
 |         } | 
 |  | 
 |     case xtRegisterTypeWindow: /*a*/ | 
 |         env->regs[reg->targno & 0x0f] = tmp; | 
 |         break; | 
 |  | 
 |     default: | 
 |         qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported type %d\n", | 
 |                       __func__, n, reg->type); | 
 |         return reg->size; | 
 |     } | 
 |  | 
 |     return 4; | 
 | } |