1 | /* |
2 | * QEMU OpenRISC CPU |
3 | * |
4 | * Copyright (c) 2012 Jia Liu <proljc@gmail.com> |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Lesser General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2.1 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Lesser General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Lesser General Public |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
18 | */ |
19 | |
20 | #include "qemu/osdep.h" |
21 | #include "qapi/error.h" |
22 | #include "qemu/qemu-print.h" |
23 | #include "cpu.h" |
24 | |
25 | static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) |
26 | { |
27 | OpenRISCCPU *cpu = OPENRISC_CPU(cs); |
28 | |
29 | cpu->env.pc = value; |
30 | cpu->env.dflag = 0; |
31 | } |
32 | |
33 | static bool openrisc_cpu_has_work(CPUState *cs) |
34 | { |
35 | return cs->interrupt_request & (CPU_INTERRUPT_HARD | |
36 | CPU_INTERRUPT_TIMER); |
37 | } |
38 | |
39 | static void openrisc_disas_set_info(CPUState *cpu, disassemble_info *info) |
40 | { |
41 | info->print_insn = print_insn_or1k; |
42 | } |
43 | |
44 | /* CPUClass::reset() */ |
45 | static void openrisc_cpu_reset(CPUState *s) |
46 | { |
47 | OpenRISCCPU *cpu = OPENRISC_CPU(s); |
48 | OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(cpu); |
49 | |
50 | occ->parent_reset(s); |
51 | |
52 | memset(&cpu->env, 0, offsetof(CPUOpenRISCState, end_reset_fields)); |
53 | |
54 | cpu->env.pc = 0x100; |
55 | cpu->env.sr = SR_FO | SR_SM; |
56 | cpu->env.lock_addr = -1; |
57 | s->exception_index = -1; |
58 | cpu_set_fpcsr(&cpu->env, 0); |
59 | |
60 | #ifndef CONFIG_USER_ONLY |
61 | cpu->env.picmr = 0x00000000; |
62 | cpu->env.picsr = 0x00000000; |
63 | |
64 | cpu->env.ttmr = 0x00000000; |
65 | #endif |
66 | } |
67 | |
68 | static void openrisc_cpu_realizefn(DeviceState *dev, Error **errp) |
69 | { |
70 | CPUState *cs = CPU(dev); |
71 | OpenRISCCPUClass *occ = OPENRISC_CPU_GET_CLASS(dev); |
72 | Error *local_err = NULL; |
73 | |
74 | cpu_exec_realizefn(cs, &local_err); |
75 | if (local_err != NULL) { |
76 | error_propagate(errp, local_err); |
77 | return; |
78 | } |
79 | |
80 | qemu_init_vcpu(cs); |
81 | cpu_reset(cs); |
82 | |
83 | occ->parent_realize(dev, errp); |
84 | } |
85 | |
86 | static void openrisc_cpu_initfn(Object *obj) |
87 | { |
88 | OpenRISCCPU *cpu = OPENRISC_CPU(obj); |
89 | |
90 | cpu_set_cpustate_pointers(cpu); |
91 | } |
92 | |
93 | /* CPU models */ |
94 | |
95 | static ObjectClass *openrisc_cpu_class_by_name(const char *cpu_model) |
96 | { |
97 | ObjectClass *oc; |
98 | char *typename; |
99 | |
100 | typename = g_strdup_printf(OPENRISC_CPU_TYPE_NAME("%s" ), cpu_model); |
101 | oc = object_class_by_name(typename); |
102 | g_free(typename); |
103 | if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_OPENRISC_CPU) || |
104 | object_class_is_abstract(oc))) { |
105 | return NULL; |
106 | } |
107 | return oc; |
108 | } |
109 | |
110 | static void or1200_initfn(Object *obj) |
111 | { |
112 | OpenRISCCPU *cpu = OPENRISC_CPU(obj); |
113 | |
114 | cpu->env.vr = 0x13000008; |
115 | cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP | UPR_PMP; |
116 | cpu->env.cpucfgr = CPUCFGR_NSGF | CPUCFGR_OB32S | CPUCFGR_OF32S | |
117 | CPUCFGR_EVBARP; |
118 | |
119 | /* 1Way, TLB_SIZE entries. */ |
120 | cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) |
121 | | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); |
122 | cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) |
123 | | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); |
124 | } |
125 | |
126 | static void openrisc_any_initfn(Object *obj) |
127 | { |
128 | OpenRISCCPU *cpu = OPENRISC_CPU(obj); |
129 | |
130 | cpu->env.vr = 0x13000040; /* Obsolete VER + UVRP for new SPRs */ |
131 | cpu->env.vr2 = 0; /* No version specific id */ |
132 | cpu->env.avr = 0x01030000; /* Architecture v1.3 */ |
133 | |
134 | cpu->env.upr = UPR_UP | UPR_DMP | UPR_IMP | UPR_PICP | UPR_TTP | UPR_PMP; |
135 | cpu->env.cpucfgr = CPUCFGR_NSGF | CPUCFGR_OB32S | CPUCFGR_OF32S | |
136 | CPUCFGR_AVRP | CPUCFGR_EVBARP | CPUCFGR_OF64A32S; |
137 | |
138 | /* 1Way, TLB_SIZE entries. */ |
139 | cpu->env.dmmucfgr = (DMMUCFGR_NTW & (0 << 2)) |
140 | | (DMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); |
141 | cpu->env.immucfgr = (IMMUCFGR_NTW & (0 << 2)) |
142 | | (IMMUCFGR_NTS & (ctz32(TLB_SIZE) << 2)); |
143 | } |
144 | |
145 | static void openrisc_cpu_class_init(ObjectClass *oc, void *data) |
146 | { |
147 | OpenRISCCPUClass *occ = OPENRISC_CPU_CLASS(oc); |
148 | CPUClass *cc = CPU_CLASS(occ); |
149 | DeviceClass *dc = DEVICE_CLASS(oc); |
150 | |
151 | device_class_set_parent_realize(dc, openrisc_cpu_realizefn, |
152 | &occ->parent_realize); |
153 | occ->parent_reset = cc->reset; |
154 | cc->reset = openrisc_cpu_reset; |
155 | |
156 | cc->class_by_name = openrisc_cpu_class_by_name; |
157 | cc->has_work = openrisc_cpu_has_work; |
158 | cc->do_interrupt = openrisc_cpu_do_interrupt; |
159 | cc->cpu_exec_interrupt = openrisc_cpu_exec_interrupt; |
160 | cc->dump_state = openrisc_cpu_dump_state; |
161 | cc->set_pc = openrisc_cpu_set_pc; |
162 | cc->gdb_read_register = openrisc_cpu_gdb_read_register; |
163 | cc->gdb_write_register = openrisc_cpu_gdb_write_register; |
164 | cc->tlb_fill = openrisc_cpu_tlb_fill; |
165 | #ifndef CONFIG_USER_ONLY |
166 | cc->get_phys_page_debug = openrisc_cpu_get_phys_page_debug; |
167 | dc->vmsd = &vmstate_openrisc_cpu; |
168 | #endif |
169 | cc->gdb_num_core_regs = 32 + 3; |
170 | cc->tcg_initialize = openrisc_translate_init; |
171 | cc->disas_set_info = openrisc_disas_set_info; |
172 | } |
173 | |
174 | /* Sort alphabetically by type name, except for "any". */ |
175 | static gint openrisc_cpu_list_compare(gconstpointer a, gconstpointer b) |
176 | { |
177 | ObjectClass *class_a = (ObjectClass *)a; |
178 | ObjectClass *class_b = (ObjectClass *)b; |
179 | const char *name_a, *name_b; |
180 | |
181 | name_a = object_class_get_name(class_a); |
182 | name_b = object_class_get_name(class_b); |
183 | if (strcmp(name_a, "any-" TYPE_OPENRISC_CPU) == 0) { |
184 | return 1; |
185 | } else if (strcmp(name_b, "any-" TYPE_OPENRISC_CPU) == 0) { |
186 | return -1; |
187 | } else { |
188 | return strcmp(name_a, name_b); |
189 | } |
190 | } |
191 | |
192 | static void openrisc_cpu_list_entry(gpointer data, gpointer user_data) |
193 | { |
194 | ObjectClass *oc = data; |
195 | const char *typename; |
196 | char *name; |
197 | |
198 | typename = object_class_get_name(oc); |
199 | name = g_strndup(typename, |
200 | strlen(typename) - strlen("-" TYPE_OPENRISC_CPU)); |
201 | qemu_printf(" %s\n" , name); |
202 | g_free(name); |
203 | } |
204 | |
205 | void cpu_openrisc_list(void) |
206 | { |
207 | GSList *list; |
208 | |
209 | list = object_class_get_list(TYPE_OPENRISC_CPU, false); |
210 | list = g_slist_sort(list, openrisc_cpu_list_compare); |
211 | qemu_printf("Available CPUs:\n" ); |
212 | g_slist_foreach(list, openrisc_cpu_list_entry, NULL); |
213 | g_slist_free(list); |
214 | } |
215 | |
216 | #define DEFINE_OPENRISC_CPU_TYPE(cpu_model, initfn) \ |
217 | { \ |
218 | .parent = TYPE_OPENRISC_CPU, \ |
219 | .instance_init = initfn, \ |
220 | .name = OPENRISC_CPU_TYPE_NAME(cpu_model), \ |
221 | } |
222 | |
223 | static const TypeInfo openrisc_cpus_type_infos[] = { |
224 | { /* base class should be registered first */ |
225 | .name = TYPE_OPENRISC_CPU, |
226 | .parent = TYPE_CPU, |
227 | .instance_size = sizeof(OpenRISCCPU), |
228 | .instance_init = openrisc_cpu_initfn, |
229 | .abstract = true, |
230 | .class_size = sizeof(OpenRISCCPUClass), |
231 | .class_init = openrisc_cpu_class_init, |
232 | }, |
233 | DEFINE_OPENRISC_CPU_TYPE("or1200" , or1200_initfn), |
234 | DEFINE_OPENRISC_CPU_TYPE("any" , openrisc_any_initfn), |
235 | }; |
236 | |
237 | DEFINE_TYPES(openrisc_cpus_type_infos) |
238 | |