Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 1 | /* |
| 2 | * QEMU SuperH CPU |
| 3 | * |
Andreas Färber | c4bb0f9 | 2012-04-13 02:16:02 +0200 | [diff] [blame] | 4 | * Copyright (c) 2005 Samuel Tardieu |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 5 | * Copyright (c) 2012 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.1 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 |
| 19 | * <http://www.gnu.org/licenses/lgpl-2.1.html> |
| 20 | */ |
| 21 | |
| 22 | #include "cpu.h" |
| 23 | #include "qemu-common.h" |
Andreas Färber | 1e45d31 | 2013-01-20 19:32:33 +0100 | [diff] [blame] | 24 | #include "migration/vmstate.h" |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 25 | |
| 26 | |
Andreas Färber | f45748f | 2013-06-21 19:09:18 +0200 | [diff] [blame] | 27 | static void superh_cpu_set_pc(CPUState *cs, vaddr value) |
| 28 | { |
| 29 | SuperHCPU *cpu = SUPERH_CPU(cs); |
| 30 | |
| 31 | cpu->env.pc = value; |
| 32 | } |
| 33 | |
Andreas Färber | bdf7ae5 | 2013-06-28 19:31:32 +0200 | [diff] [blame] | 34 | static void superh_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) |
| 35 | { |
| 36 | SuperHCPU *cpu = SUPERH_CPU(cs); |
| 37 | |
| 38 | cpu->env.pc = tb->pc; |
| 39 | cpu->env.flags = tb->flags; |
| 40 | } |
| 41 | |
Andreas Färber | 8c2e1b0 | 2013-08-25 18:53:55 +0200 | [diff] [blame] | 42 | static bool superh_cpu_has_work(CPUState *cs) |
| 43 | { |
| 44 | return cs->interrupt_request & CPU_INTERRUPT_HARD; |
| 45 | } |
| 46 | |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 47 | /* CPUClass::reset() */ |
| 48 | static void superh_cpu_reset(CPUState *s) |
| 49 | { |
| 50 | SuperHCPU *cpu = SUPERH_CPU(s); |
| 51 | SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(cpu); |
| 52 | CPUSH4State *env = &cpu->env; |
| 53 | |
| 54 | scc->parent_reset(s); |
| 55 | |
Andreas Färber | f0c3c50 | 2013-08-26 21:22:53 +0200 | [diff] [blame] | 56 | memset(env, 0, offsetof(CPUSH4State, id)); |
Andreas Färber | 00c8cb0 | 2013-09-04 02:19:44 +0200 | [diff] [blame] | 57 | tlb_flush(s, 1); |
Andreas Färber | c4bb0f9 | 2012-04-13 02:16:02 +0200 | [diff] [blame] | 58 | |
| 59 | env->pc = 0xA0000000; |
| 60 | #if defined(CONFIG_USER_ONLY) |
| 61 | env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ |
| 62 | set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ |
| 63 | #else |
| 64 | env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; |
| 65 | env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ |
| 66 | set_float_rounding_mode(float_round_to_zero, &env->fp_status); |
| 67 | set_flush_to_zero(1, &env->fp_status); |
| 68 | #endif |
| 69 | set_default_nan_mode(1, &env->fp_status); |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 70 | } |
| 71 | |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 72 | typedef struct SuperHCPUListState { |
| 73 | fprintf_function cpu_fprintf; |
| 74 | FILE *file; |
| 75 | } SuperHCPUListState; |
| 76 | |
| 77 | /* Sort alphabetically by type name. */ |
| 78 | static gint superh_cpu_list_compare(gconstpointer a, gconstpointer b) |
| 79 | { |
| 80 | ObjectClass *class_a = (ObjectClass *)a; |
| 81 | ObjectClass *class_b = (ObjectClass *)b; |
| 82 | const char *name_a, *name_b; |
| 83 | |
| 84 | name_a = object_class_get_name(class_a); |
| 85 | name_b = object_class_get_name(class_b); |
| 86 | return strcmp(name_a, name_b); |
| 87 | } |
| 88 | |
| 89 | static void superh_cpu_list_entry(gpointer data, gpointer user_data) |
| 90 | { |
| 91 | ObjectClass *oc = data; |
| 92 | SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); |
| 93 | SuperHCPUListState *s = user_data; |
| 94 | |
| 95 | (*s->cpu_fprintf)(s->file, "%s\n", |
| 96 | scc->name); |
| 97 | } |
| 98 | |
| 99 | void sh4_cpu_list(FILE *f, fprintf_function cpu_fprintf) |
| 100 | { |
| 101 | SuperHCPUListState s = { |
| 102 | .cpu_fprintf = cpu_fprintf, |
| 103 | .file = f, |
| 104 | }; |
| 105 | GSList *list; |
| 106 | |
| 107 | list = object_class_get_list(TYPE_SUPERH_CPU, false); |
| 108 | list = g_slist_sort(list, superh_cpu_list_compare); |
| 109 | g_slist_foreach(list, superh_cpu_list_entry, &s); |
| 110 | g_slist_free(list); |
| 111 | } |
| 112 | |
| 113 | static gint superh_cpu_name_compare(gconstpointer a, gconstpointer b) |
| 114 | { |
| 115 | const SuperHCPUClass *scc = SUPERH_CPU_CLASS(a); |
| 116 | const char *name = b; |
| 117 | |
| 118 | return strcasecmp(scc->name, name); |
| 119 | } |
| 120 | |
| 121 | static ObjectClass *superh_cpu_class_by_name(const char *cpu_model) |
| 122 | { |
| 123 | ObjectClass *oc; |
| 124 | GSList *list, *item; |
| 125 | |
| 126 | if (cpu_model == NULL) { |
| 127 | return NULL; |
| 128 | } |
| 129 | if (strcasecmp(cpu_model, "any") == 0) { |
| 130 | return object_class_by_name(TYPE_SH7750R_CPU); |
| 131 | } |
| 132 | |
| 133 | oc = object_class_by_name(cpu_model); |
| 134 | if (oc != NULL && object_class_dynamic_cast(oc, TYPE_SUPERH_CPU) != NULL |
| 135 | && !object_class_is_abstract(oc)) { |
| 136 | return oc; |
| 137 | } |
| 138 | |
| 139 | oc = NULL; |
| 140 | list = object_class_get_list(TYPE_SUPERH_CPU, false); |
| 141 | item = g_slist_find_custom(list, cpu_model, superh_cpu_name_compare); |
| 142 | if (item != NULL) { |
| 143 | oc = item->data; |
| 144 | } |
| 145 | g_slist_free(list); |
| 146 | return oc; |
| 147 | } |
| 148 | |
| 149 | SuperHCPU *cpu_sh4_init(const char *cpu_model) |
| 150 | { |
Andreas Färber | 9262685 | 2014-03-04 03:17:10 +0100 | [diff] [blame] | 151 | return SUPERH_CPU(cpu_generic_init(TYPE_SUPERH_CPU, cpu_model)); |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 152 | } |
| 153 | |
| 154 | static void sh7750r_cpu_initfn(Object *obj) |
| 155 | { |
| 156 | SuperHCPU *cpu = SUPERH_CPU(obj); |
| 157 | CPUSH4State *env = &cpu->env; |
| 158 | |
| 159 | env->id = SH_CPU_SH7750R; |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 160 | env->features = SH_FEATURE_BCR3_AND_BCR4; |
| 161 | } |
| 162 | |
| 163 | static void sh7750r_class_init(ObjectClass *oc, void *data) |
| 164 | { |
| 165 | SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); |
| 166 | |
| 167 | scc->name = "SH7750R"; |
Andreas Färber | b350ab7 | 2012-11-20 16:15:47 +0100 | [diff] [blame] | 168 | scc->pvr = 0x00050000; |
| 169 | scc->prr = 0x00000100; |
| 170 | scc->cvr = 0x00110000; |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 171 | } |
| 172 | |
| 173 | static const TypeInfo sh7750r_type_info = { |
| 174 | .name = TYPE_SH7750R_CPU, |
| 175 | .parent = TYPE_SUPERH_CPU, |
| 176 | .class_init = sh7750r_class_init, |
| 177 | .instance_init = sh7750r_cpu_initfn, |
| 178 | }; |
| 179 | |
| 180 | static void sh7751r_cpu_initfn(Object *obj) |
| 181 | { |
| 182 | SuperHCPU *cpu = SUPERH_CPU(obj); |
| 183 | CPUSH4State *env = &cpu->env; |
| 184 | |
| 185 | env->id = SH_CPU_SH7751R; |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 186 | env->features = SH_FEATURE_BCR3_AND_BCR4; |
| 187 | } |
| 188 | |
| 189 | static void sh7751r_class_init(ObjectClass *oc, void *data) |
| 190 | { |
| 191 | SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); |
| 192 | |
| 193 | scc->name = "SH7751R"; |
Andreas Färber | b350ab7 | 2012-11-20 16:15:47 +0100 | [diff] [blame] | 194 | scc->pvr = 0x04050005; |
| 195 | scc->prr = 0x00000113; |
| 196 | scc->cvr = 0x00110000; /* Neutered caches, should be 0x20480000 */ |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 197 | } |
| 198 | |
| 199 | static const TypeInfo sh7751r_type_info = { |
| 200 | .name = TYPE_SH7751R_CPU, |
| 201 | .parent = TYPE_SUPERH_CPU, |
| 202 | .class_init = sh7751r_class_init, |
| 203 | .instance_init = sh7751r_cpu_initfn, |
| 204 | }; |
| 205 | |
| 206 | static void sh7785_cpu_initfn(Object *obj) |
| 207 | { |
| 208 | SuperHCPU *cpu = SUPERH_CPU(obj); |
| 209 | CPUSH4State *env = &cpu->env; |
| 210 | |
| 211 | env->id = SH_CPU_SH7785; |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 212 | env->features = SH_FEATURE_SH4A; |
| 213 | } |
| 214 | |
| 215 | static void sh7785_class_init(ObjectClass *oc, void *data) |
| 216 | { |
| 217 | SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); |
| 218 | |
| 219 | scc->name = "SH7785"; |
Andreas Färber | b350ab7 | 2012-11-20 16:15:47 +0100 | [diff] [blame] | 220 | scc->pvr = 0x10300700; |
| 221 | scc->prr = 0x00000200; |
| 222 | scc->cvr = 0x71440211; |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 223 | } |
| 224 | |
| 225 | static const TypeInfo sh7785_type_info = { |
| 226 | .name = TYPE_SH7785_CPU, |
| 227 | .parent = TYPE_SUPERH_CPU, |
| 228 | .class_init = sh7785_class_init, |
| 229 | .instance_init = sh7785_cpu_initfn, |
| 230 | }; |
| 231 | |
Andreas Färber | 55acb58 | 2012-04-23 18:16:02 +0200 | [diff] [blame] | 232 | static void superh_cpu_realizefn(DeviceState *dev, Error **errp) |
| 233 | { |
Andreas Färber | 14a10fc | 2013-07-27 02:53:25 +0200 | [diff] [blame] | 234 | CPUState *cs = CPU(dev); |
Andreas Färber | 55acb58 | 2012-04-23 18:16:02 +0200 | [diff] [blame] | 235 | SuperHCPUClass *scc = SUPERH_CPU_GET_CLASS(dev); |
| 236 | |
Andreas Färber | 14a10fc | 2013-07-27 02:53:25 +0200 | [diff] [blame] | 237 | cpu_reset(cs); |
| 238 | qemu_init_vcpu(cs); |
Andreas Färber | 55acb58 | 2012-04-23 18:16:02 +0200 | [diff] [blame] | 239 | |
| 240 | scc->parent_realize(dev, errp); |
| 241 | } |
| 242 | |
Andreas Färber | 2b4b490 | 2012-04-13 02:32:12 +0200 | [diff] [blame] | 243 | static void superh_cpu_initfn(Object *obj) |
| 244 | { |
Andreas Färber | c05efcb | 2013-01-17 12:13:41 +0100 | [diff] [blame] | 245 | CPUState *cs = CPU(obj); |
Andreas Färber | 2b4b490 | 2012-04-13 02:32:12 +0200 | [diff] [blame] | 246 | SuperHCPU *cpu = SUPERH_CPU(obj); |
| 247 | CPUSH4State *env = &cpu->env; |
| 248 | |
Andreas Färber | c05efcb | 2013-01-17 12:13:41 +0100 | [diff] [blame] | 249 | cs->env_ptr = env; |
Andreas Färber | 2b4b490 | 2012-04-13 02:32:12 +0200 | [diff] [blame] | 250 | cpu_exec_init(env); |
| 251 | |
| 252 | env->movcal_backup_tail = &(env->movcal_backup); |
Andreas Färber | aa7408e | 2013-01-20 01:30:32 +0100 | [diff] [blame] | 253 | |
| 254 | if (tcg_enabled()) { |
| 255 | sh4_translate_init(); |
| 256 | } |
Andreas Färber | 2b4b490 | 2012-04-13 02:32:12 +0200 | [diff] [blame] | 257 | } |
| 258 | |
Andreas Färber | 1e45d31 | 2013-01-20 19:32:33 +0100 | [diff] [blame] | 259 | static const VMStateDescription vmstate_sh_cpu = { |
| 260 | .name = "cpu", |
| 261 | .unmigratable = 1, |
| 262 | }; |
| 263 | |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 264 | static void superh_cpu_class_init(ObjectClass *oc, void *data) |
| 265 | { |
Andreas Färber | 1e45d31 | 2013-01-20 19:32:33 +0100 | [diff] [blame] | 266 | DeviceClass *dc = DEVICE_CLASS(oc); |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 267 | CPUClass *cc = CPU_CLASS(oc); |
| 268 | SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); |
| 269 | |
Andreas Färber | 55acb58 | 2012-04-23 18:16:02 +0200 | [diff] [blame] | 270 | scc->parent_realize = dc->realize; |
| 271 | dc->realize = superh_cpu_realizefn; |
| 272 | |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 273 | scc->parent_reset = cc->reset; |
| 274 | cc->reset = superh_cpu_reset; |
Andreas Färber | 1e45d31 | 2013-01-20 19:32:33 +0100 | [diff] [blame] | 275 | |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 276 | cc->class_by_name = superh_cpu_class_by_name; |
Andreas Färber | 8c2e1b0 | 2013-08-25 18:53:55 +0200 | [diff] [blame] | 277 | cc->has_work = superh_cpu_has_work; |
Andreas Färber | 97a8ea5 | 2013-02-02 10:57:51 +0100 | [diff] [blame] | 278 | cc->do_interrupt = superh_cpu_do_interrupt; |
Richard Henderson | f47ede1 | 2014-09-13 09:45:23 -0700 | [diff] [blame] | 279 | cc->cpu_exec_interrupt = superh_cpu_exec_interrupt; |
Andreas Färber | 878096e | 2013-05-27 01:33:50 +0200 | [diff] [blame] | 280 | cc->dump_state = superh_cpu_dump_state; |
Andreas Färber | f45748f | 2013-06-21 19:09:18 +0200 | [diff] [blame] | 281 | cc->set_pc = superh_cpu_set_pc; |
Andreas Färber | bdf7ae5 | 2013-06-28 19:31:32 +0200 | [diff] [blame] | 282 | cc->synchronize_from_tb = superh_cpu_synchronize_from_tb; |
Andreas Färber | 5b50e79 | 2013-06-29 04:18:45 +0200 | [diff] [blame] | 283 | cc->gdb_read_register = superh_cpu_gdb_read_register; |
| 284 | cc->gdb_write_register = superh_cpu_gdb_write_register; |
Andreas Färber | 7510454 | 2013-08-26 03:01:33 +0200 | [diff] [blame] | 285 | #ifdef CONFIG_USER_ONLY |
| 286 | cc->handle_mmu_fault = superh_cpu_handle_mmu_fault; |
| 287 | #else |
Andreas Färber | 00b941e | 2013-06-29 18:55:54 +0200 | [diff] [blame] | 288 | cc->get_phys_page_debug = superh_cpu_get_phys_page_debug; |
| 289 | #endif |
Andreas Färber | 1e45d31 | 2013-01-20 19:32:33 +0100 | [diff] [blame] | 290 | dc->vmsd = &vmstate_sh_cpu; |
Andreas Färber | a0e372f | 2013-06-28 23:18:47 +0200 | [diff] [blame] | 291 | cc->gdb_num_core_regs = 59; |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 292 | } |
| 293 | |
| 294 | static const TypeInfo superh_cpu_type_info = { |
| 295 | .name = TYPE_SUPERH_CPU, |
| 296 | .parent = TYPE_CPU, |
| 297 | .instance_size = sizeof(SuperHCPU), |
Andreas Färber | 2b4b490 | 2012-04-13 02:32:12 +0200 | [diff] [blame] | 298 | .instance_init = superh_cpu_initfn, |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 299 | .abstract = true, |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 300 | .class_size = sizeof(SuperHCPUClass), |
| 301 | .class_init = superh_cpu_class_init, |
| 302 | }; |
| 303 | |
| 304 | static void superh_cpu_register_types(void) |
| 305 | { |
| 306 | type_register_static(&superh_cpu_type_info); |
Andreas Färber | c1b382e | 2012-11-19 02:42:18 +0100 | [diff] [blame] | 307 | type_register_static(&sh7750r_type_info); |
| 308 | type_register_static(&sh7751r_type_info); |
| 309 | type_register_static(&sh7785_type_info); |
Andreas Färber | 339894b | 2012-02-11 17:26:17 +0100 | [diff] [blame] | 310 | } |
| 311 | |
| 312 | type_init(superh_cpu_register_types) |