1 | /* |
2 | * Xtensa gdb server stub |
3 | * |
4 | * Copyright (c) 2003-2005 Fabrice Bellard |
5 | * Copyright (c) 2013 SUSE LINUX Products GmbH |
6 | * |
7 | * This library is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU Lesser General Public |
9 | * License as published by the Free Software Foundation; either |
10 | * version 2 of the License, or (at your option) any later version. |
11 | * |
12 | * This library is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
15 | * Lesser General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU Lesser General Public |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | #include "qemu/osdep.h" |
21 | #include "cpu.h" |
22 | #include "exec/gdbstub.h" |
23 | #include "qemu/log.h" |
24 | |
25 | enum { |
26 | xtRegisterTypeArRegfile = 1, /* Register File ar0..arXX. */ |
27 | xtRegisterTypeSpecialReg, /* CPU states, such as PS, Booleans, (rsr). */ |
28 | xtRegisterTypeUserReg, /* User defined registers (rur). */ |
29 | xtRegisterTypeTieRegfile, /* User define register files. */ |
30 | xtRegisterTypeTieState, /* TIE States (mapped on user regs). */ |
31 | xtRegisterTypeMapped, /* Mapped on Special Registers. */ |
32 | xtRegisterTypeUnmapped, /* Special case of masked registers. */ |
33 | xtRegisterTypeWindow, /* Live window registers (a0..a15). */ |
34 | xtRegisterTypeVirtual, /* PC, FP. */ |
35 | xtRegisterTypeUnknown |
36 | }; |
37 | |
38 | #define XTENSA_REGISTER_FLAGS_PRIVILEGED 0x0001 |
39 | #define XTENSA_REGISTER_FLAGS_READABLE 0x0002 |
40 | #define XTENSA_REGISTER_FLAGS_WRITABLE 0x0004 |
41 | #define XTENSA_REGISTER_FLAGS_VOLATILE 0x0008 |
42 | |
43 | void xtensa_count_regs(const XtensaConfig *config, |
44 | unsigned *n_regs, unsigned *n_core_regs) |
45 | { |
46 | unsigned i; |
47 | bool count_core_regs = true; |
48 | |
49 | for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) { |
50 | if (config->gdb_regmap.reg[i].type != xtRegisterTypeTieState && |
51 | config->gdb_regmap.reg[i].type != xtRegisterTypeMapped && |
52 | config->gdb_regmap.reg[i].type != xtRegisterTypeUnmapped) { |
53 | ++*n_regs; |
54 | if (count_core_regs) { |
55 | if ((config->gdb_regmap.reg[i].flags & |
56 | XTENSA_REGISTER_FLAGS_PRIVILEGED) == 0) { |
57 | ++*n_core_regs; |
58 | } else { |
59 | count_core_regs = false; |
60 | } |
61 | } |
62 | } |
63 | } |
64 | } |
65 | |
66 | int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) |
67 | { |
68 | XtensaCPU *cpu = XTENSA_CPU(cs); |
69 | CPUXtensaState *env = &cpu->env; |
70 | const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; |
71 | #ifdef CONFIG_USER_ONLY |
72 | int num_regs = env->config->gdb_regmap.num_core_regs; |
73 | #else |
74 | int num_regs = env->config->gdb_regmap.num_regs; |
75 | #endif |
76 | unsigned i; |
77 | |
78 | if (n < 0 || n >= num_regs) { |
79 | return 0; |
80 | } |
81 | |
82 | switch (reg->type) { |
83 | case xtRegisterTypeVirtual: /*pc*/ |
84 | return gdb_get_reg32(mem_buf, env->pc); |
85 | |
86 | case xtRegisterTypeArRegfile: /*ar*/ |
87 | xtensa_sync_phys_from_window(env); |
88 | return gdb_get_reg32(mem_buf, env->phys_regs[(reg->targno & 0xff) |
89 | % env->config->nareg]); |
90 | |
91 | case xtRegisterTypeSpecialReg: /*SR*/ |
92 | return gdb_get_reg32(mem_buf, env->sregs[reg->targno & 0xff]); |
93 | |
94 | case xtRegisterTypeUserReg: /*UR*/ |
95 | return gdb_get_reg32(mem_buf, env->uregs[reg->targno & 0xff]); |
96 | |
97 | case xtRegisterTypeTieRegfile: /*f*/ |
98 | i = reg->targno & 0x0f; |
99 | switch (reg->size) { |
100 | case 4: |
101 | return gdb_get_reg32(mem_buf, |
102 | float32_val(env->fregs[i].f32[FP_F32_LOW])); |
103 | case 8: |
104 | return gdb_get_reg64(mem_buf, float64_val(env->fregs[i].f64)); |
105 | default: |
106 | qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported size %d\n" , |
107 | __func__, n, reg->size); |
108 | memset(mem_buf, 0, reg->size); |
109 | return reg->size; |
110 | } |
111 | |
112 | case xtRegisterTypeWindow: /*a*/ |
113 | return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); |
114 | |
115 | default: |
116 | qemu_log_mask(LOG_UNIMP, "%s from reg %d of unsupported type %d\n" , |
117 | __func__, n, reg->type); |
118 | memset(mem_buf, 0, reg->size); |
119 | return reg->size; |
120 | } |
121 | } |
122 | |
123 | int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) |
124 | { |
125 | XtensaCPU *cpu = XTENSA_CPU(cs); |
126 | CPUXtensaState *env = &cpu->env; |
127 | uint32_t tmp; |
128 | const XtensaGdbReg *reg = env->config->gdb_regmap.reg + n; |
129 | #ifdef CONFIG_USER_ONLY |
130 | int num_regs = env->config->gdb_regmap.num_core_regs; |
131 | #else |
132 | int num_regs = env->config->gdb_regmap.num_regs; |
133 | #endif |
134 | |
135 | if (n < 0 || n >= num_regs) { |
136 | return 0; |
137 | } |
138 | |
139 | tmp = ldl_p(mem_buf); |
140 | |
141 | switch (reg->type) { |
142 | case xtRegisterTypeVirtual: /*pc*/ |
143 | env->pc = tmp; |
144 | break; |
145 | |
146 | case xtRegisterTypeArRegfile: /*ar*/ |
147 | env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; |
148 | xtensa_sync_window_from_phys(env); |
149 | break; |
150 | |
151 | case xtRegisterTypeSpecialReg: /*SR*/ |
152 | env->sregs[reg->targno & 0xff] = tmp; |
153 | break; |
154 | |
155 | case xtRegisterTypeUserReg: /*UR*/ |
156 | env->uregs[reg->targno & 0xff] = tmp; |
157 | break; |
158 | |
159 | case xtRegisterTypeTieRegfile: /*f*/ |
160 | switch (reg->size) { |
161 | case 4: |
162 | env->fregs[reg->targno & 0x0f].f32[FP_F32_LOW] = make_float32(tmp); |
163 | return 4; |
164 | case 8: |
165 | env->fregs[reg->targno & 0x0f].f64 = make_float64(tmp); |
166 | return 8; |
167 | default: |
168 | qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported size %d\n" , |
169 | __func__, n, reg->size); |
170 | return reg->size; |
171 | } |
172 | |
173 | case xtRegisterTypeWindow: /*a*/ |
174 | env->regs[reg->targno & 0x0f] = tmp; |
175 | break; |
176 | |
177 | default: |
178 | qemu_log_mask(LOG_UNIMP, "%s to reg %d of unsupported type %d\n" , |
179 | __func__, n, reg->type); |
180 | return reg->size; |
181 | } |
182 | |
183 | return 4; |
184 | } |
185 | |