Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: ss_common.c | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | /* | |
24 | * Copyright 2007 Sun Microsystems, Inc. All rights reserved. | |
25 | * Use is subject to license terms. | |
26 | */ | |
27 | ||
28 | #pragma ident "@(#)ss_common.c 1.257 07/10/12 SMI" | |
29 | ||
30 | /* | |
31 | * Complete the parsing of a SunSPARC processor. | |
32 | * | |
33 | * Pick out the relevent info and allocate the | |
34 | * simcpu_t structures in the config_proc that | |
35 | * gets handed to us. | |
36 | */ | |
37 | ||
38 | /* | |
39 | * This is where the definitions get complicated. | |
40 | * There is a processor specific component, a generic processor | |
41 | * family component, and then the simulator infrastructure component. | |
42 | * The latter is basically glue to tie a processor to a domain, and | |
43 | * the processor specific info. The family component is general | |
44 | * (ish) e.g. a v9 processor .. with a bunch of data structures | |
45 | * flags etc. only understood by the family code, and then there is | |
46 | * abunch of code and data specific to this CPU. | |
47 | * All of these items get parsed and configured as part of this code. | |
48 | * | |
49 | * Note that this file is compiled three times in order to create | |
50 | * the three processor specific loadable modules: libniagara.so, | |
51 | * libniagara2.so, and librock.so. | |
52 | */ | |
53 | ||
54 | #include <stdio.h> | |
55 | #include <stdlib.h> | |
56 | #include <unistd.h> | |
57 | #include <string.h> /* memcpy/memset */ | |
58 | #include <strings.h> | |
59 | #include <thread.h> | |
60 | #include <sys/types.h> | |
61 | #include <sys/stat.h> | |
62 | #include <fcntl.h> | |
63 | #include <sys/mman.h> | |
64 | ||
65 | #include "ss_common.h" | |
66 | #include "magictraps.h" | |
67 | #include "save_restore.h" | |
68 | ||
69 | /* | |
70 | * The following chip specific header files contain #if ... #endif | |
71 | * statements so we don't have to worry about including one chip's | |
72 | * definitions in the compilation of another's. | |
73 | */ | |
74 | ||
75 | #ifdef NIAGARA1 | |
76 | #include "niagara.h" | |
77 | #endif | |
78 | ||
79 | #ifdef NIAGARA2 | |
80 | #include "niagara2.h" | |
81 | #endif | |
82 | ||
83 | #ifdef ROCK /* { */ | |
84 | #include "rock.h" | |
85 | #endif /* } */ | |
86 | ||
87 | #include "ss_hwtw.h" | |
88 | ||
89 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
90 | /* Need to define this as processor specific code somewhere */ | |
91 | #define PIC_MAX UINT32_MAX | |
92 | ||
93 | /* | |
94 | * defines used for manipulating pcr and pic regs | |
95 | * | |
96 | * PIC1 PIC0 | |
97 | * [63------32][31------0] | |
98 | */ | |
99 | /* Return the appropriate pic value from within the 64bit counter */ | |
100 | #define CPU_COUNTER_TO_PIC0(cntr) ((cntr) & 0xFFFFFFFFULL) | |
101 | #define CPU_COUNTER_TO_PIC1(cntr) ((cntr) >> 32) | |
102 | ||
103 | /* Update the 64bit counter with the appropriate pic value */ | |
104 | #define PIC0_PIC1_TO_CPU_COUNTER(pic0, pic1) ((pic0) | (((uint64_t)(pic1)) << 32)) | |
105 | #endif /* } */ | |
106 | ||
107 | uint64_t* lookup_sim_addr(simcpu_t *sp, uint64_t *sim_addr); | |
108 | ||
109 | ss_trap_list_t ss_trap_list[0x200]; | |
110 | ||
111 | #define DBGP(s) do { s } while (0) | |
112 | ||
113 | static void parse_debug_hook(ss_proc_t *procp); | |
114 | ||
115 | #ifdef HW_TABLEWALK | |
116 | static bool_t ss_hwtw_get_tte_entry(simcpu_t*, uint8_t *, uint64_t*); | |
117 | #endif /* HW_TABLEWALK */ | |
118 | static void ss_tlb_init(ss_tlb_t * tlbp, uint_t nentries); | |
119 | static void ss_l1_cache_init(ss_l1_cache_t * cachep, uint32_t cachesize, | |
120 | bool_t is_immu); | |
121 | ||
122 | /* | |
123 | * Global array for calculating page size shift bits | |
124 | * The sun4v page size is 4 bits | |
125 | */ | |
126 | #if defined(ROCK) | |
127 | uint8_t ss_page_size_shift[16]={ 13, 16, 19, 22, 25, 28, 31, 34 }; | |
128 | #elif defined(NIAGARA1) || defined(NIAGARA2) | |
129 | uint8_t ss_page_size_shift[16]={ 13, 16, 0, 22, 0, 28, 0, 0 }; | |
130 | #else | |
131 | #error "No valid processor defined." | |
132 | #endif | |
133 | ||
134 | ||
135 | uint64_t ss_ext_signal(config_proc_t * config_procp, ext_sig_t sigtype, | |
136 | void *vp); | |
137 | ||
138 | void * ss_dbgr_attach(domain_t *, config_proc_t *, char *); | |
139 | void ss_dbgr_detach(void *); | |
140 | ||
141 | bool_t ss_dbgr_regread(void*, uint_t, uint64_t *); | |
142 | bool_t ss_dbgr_regwrite(void*, uint_t, uint64_t); | |
143 | uint64_t ss_dbgr_mem_read(void*, tvaddr_t, bool_t, uint8_t *, uint64_t); | |
144 | uint64_t ss_dbgr_mem_write(void*, tvaddr_t, bool_t, uint8_t *, uint64_t); | |
145 | uint64_t ss_dbgr_mem_clear(void*, tvaddr_t, bool_t, uint64_t); | |
146 | void ss_dbgr_set_break(void*, tvaddr_t); | |
147 | void ss_dbgr_clear_break(void*, tvaddr_t); | |
148 | ||
149 | static ss_proc_t * ss_proc_alloc(config_proc_t *); | |
150 | ||
151 | ||
152 | /* genuinely static functions */ | |
153 | ||
154 | /* VtoP tranlsation used by debugger interface */ | |
155 | static bool_t ss_vtop_translate( ss_proc_t *, sparcv9_cpu_t * strandp, tvaddr_t va, tpaddr_t *pap, tvaddr_t * vlenp); | |
156 | ||
157 | static tpaddr_t ss_pmem_op( | |
158 | domain_t * domainp, | |
159 | ss_proc_t * procp, | |
160 | sparcv9_cpu_t * strandp, | |
161 | dbgr_mem_op_t memop, | |
162 | tpaddr_t pa, | |
163 | uint8_t * bufp, | |
164 | tpaddr_t size ); | |
165 | ||
166 | static void ss_xdc_miss(simcpu_t *, uint64_t * regp, tvaddr_t, maccess_t op); | |
167 | static void ss_read_state_reg(simcpu_t * sp, uint_t rdest, uint_t state_reg); | |
168 | static void ss_write_state_reg(simcpu_t * sp, uint_t state_reg, uint64_t val); | |
169 | static void ss_read_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg); | |
170 | static void ss_write_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val); | |
171 | static void ss_read_hyp_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg); | |
172 | static void ss_write_hyp_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val); | |
173 | ||
174 | static void ss_done_retry(simcpu_t * sp, bool_t); | |
175 | static void ss_jpriv(simcpu_t * sp, tvaddr_t); | |
176 | ||
177 | static void ss_post_precise_trap(simcpu_t *, sparcv9_trap_type_t); | |
178 | static void ss_reset_trap(simcpu_t * sp, ss_trap_type_t tt); | |
179 | ||
180 | static void ss_cycle_target_match(simcpu_t *); | |
181 | ||
182 | #define NA_HPSTATE_ENB_BIT 11 /* ENB bit in HPSTATE is bit 11 */ | |
183 | ||
184 | #define V9_TSTATE_GL_MASK ((uint64_t)0x7) | |
185 | #define V9_TSTATE_GL_SHIFT 40 | |
186 | #define V9_TSTATE_CCR_MASK ((uint64_t)0xff) | |
187 | #define V9_TSTATE_CCR_SHIFT 32 | |
188 | #define V9_TSTATE_ASI_MASK ((uint64_t)0xff) | |
189 | #define V9_TSTATE_ASI_SHIFT 24 | |
190 | #define V9_TSTATE_PSTATE_MASK ((uint64_t)0x7ff) /* FIXME */ | |
191 | #define V9_TSTATE_PSTATE_SHIFT 8 | |
192 | #define V9_TSTATE_CWP_MASK ((uint64_t)0x1f) | |
193 | #define V9_TSTATE_CWP_SHIFT 0 | |
194 | ||
195 | #define V9_CCR_MASK (0xffLL) | |
196 | #define V9_ASI_MASK (0xffLL) | |
197 | #define V9_PIL_MASK (0xfLL) | |
198 | ||
199 | ||
200 | ||
201 | /* | |
202 | * Macros used everywhere that we change the processor state | |
203 | * employ these to make sure that other variables that are defined by | |
204 | * the cpu state are kept consistent | |
205 | */ | |
206 | ||
207 | #if 0 /* { */ | |
208 | #define V9_PSTATE_CHANGED(_v9p) | |
209 | ||
210 | /* the mmu_bypass flag for niagara depends whether we are in hpriv or red state or not */ | |
211 | #define V9_HPSTATE_CHANGED(_sp, _v9p) do { \ | |
212 | ss_strand_t * _nsp; \ | |
213 | _nsp = _v9p->impl_specificp; \ | |
214 | \ | |
215 | _nsp->mmu_bypass = (_v9p->state == V9_HyperPriv) || (_v9p->state == V9_RED); \ | |
216 | DBGHPS( lprintf(_sp->gid, "hpstate = %s\n", sparcv9_state_name[ _v9p->state ]); ); \ | |
217 | } while (0) | |
218 | #endif /* } */ | |
219 | ||
220 | #if ERROR_INJECTION | |
221 | #define SET_ERROR_CHECK(_sp) do { \ | |
222 | if (_sp->error_enabled) \ | |
223 | _sp->error_check = \ | |
224 | (_newstate == _sp->error_priv) ? true : false; \ | |
225 | } while (0) | |
226 | #else | |
227 | #define SET_ERROR_CHECK(_sp) do { } while (0) | |
228 | #endif | |
229 | ||
230 | #define V9_STATE_CHANGE(_isp, _iv9p, _inewstate) do { \ | |
231 | sparcv9_cpu_t * _v9p = (_iv9p); \ | |
232 | sparcv9_state_t _newstate = (_inewstate); \ | |
233 | ss_strand_t * _nsp; \ | |
234 | simcpu_t * _sp = _isp; \ | |
235 | \ | |
236 | PERF_ACCUMULATE_ICOUNT(_v9p); \ | |
237 | \ | |
238 | _nsp = _v9p->impl_specificp; \ | |
239 | _nsp->mmu_bypass = (V9_HyperPriv==_newstate) || (V9_RED==_newstate); \ | |
240 | DBGHPS( extern char * sparcv9_state_name[]; \ | |
241 | lprintf(_sp->gid, "hpstate = %s -> %s (pc=0x%llx)\n", sparcv9_state_name[ _v9p->state ], sparcv9_state_name[ _newstate ], _sp->pc); ); \ | |
242 | _v9p->state = (_newstate); \ | |
243 | xcache_set_tagstate(_sp); \ | |
244 | SET_ERROR_CHECK(_sp); \ | |
245 | \ | |
246 | } while (0) | |
247 | ||
248 | ||
249 | #define V9_PSTATE_AM_CHANGED(_v9p) do { \ | |
250 | } while (0) | |
251 | ||
252 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
253 | ||
254 | /* | |
255 | * The simulated PIC register values are only updated when the new values | |
256 | * are required. To do this, the count source for each counter is sampled | |
257 | * after each update so a delta can be derived. | |
258 | * | |
259 | * The implementation is simplistic in that the count controls are ignored, | |
260 | * but it is enough to test the Solaris PIC code. | |
261 | */ | |
262 | #define UPDATE_PIC( _sp, _nsp, _v9p ) do { \ | |
263 | uint64_t _new; \ | |
264 | _new = RAW_TICK(_v9p); \ | |
265 | (_nsp)->pic0 += (_new - (_nsp)->pic0_sample_base); \ | |
266 | (_nsp)->pic0_sample_base = _new; \ | |
267 | _new = ICOUNT(_sp); \ | |
268 | (_nsp)->pic1 += (ICOUNT(_sp) - (_nsp)->pic1_sample_base); \ | |
269 | (_nsp)->pic1_sample_base = _new; \ | |
270 | } while (0) | |
271 | ||
272 | #define RESET_PIC_SAMPLE_BASES( _sp, _nsp, _v9p ) do { \ | |
273 | (_nsp)->pic0_sample_base = RAW_TICK(_v9p); \ | |
274 | (_nsp)->pic1_sample_base = ICOUNT(_sp); \ | |
275 | } while (0) | |
276 | ||
277 | #endif /* } */ | |
278 | ||
279 | ss_proc_t * ss_proc_alloc(config_proc_t * config_procp) | |
280 | { | |
281 | ss_proc_t * procp; | |
282 | ||
283 | procp = Xcalloc(1, ss_proc_t); | |
284 | procp->config_procp = config_procp; | |
285 | config_procp->procp = procp; | |
286 | ||
287 | procp->rstv_addr = (tvaddr_t)0; | |
288 | procp->has_fpu = true; /* FPU present by default */ | |
289 | ||
290 | procp->nglobals = SS_VER_MAXGL + 1; | |
291 | procp->maxtl = SS_VER_MAXTL; | |
292 | procp->nwins = SS_VER_MAXWIN + 1; | |
293 | ||
294 | procp->itlbspec.nentries = DEFAULT_ITLB_ENTRIES; /* FIXME */ | |
295 | procp->itlbspec.parity = false; | |
296 | procp->dtlbspec.nentries = DEFAULT_DTLB_ENTRIES; | |
297 | procp->dtlbspec.parity = false; | |
298 | #if defined(ROCK) | |
299 | procp->tlbspec.nentries = DEFAULT_TLB_ENTRIES; /* Rock has a unified L2 TLB also */ | |
300 | procp->tlbspec.parity = false; | |
301 | /* | |
302 | * Initialize the real page size info. As per the PRM, for real translations | |
303 | * we probe the TLB 8 times (8 page sizes enabled) and use the following | |
304 | * order: 256MB -> 2GB -> 16GB -> 8KB -> 64KB -> 512KB -> 4MB -> 32MB | |
305 | */ | |
306 | procp->real_page_size.enabled_page_sizes = 8; | |
307 | ||
308 | procp->real_page_size.page_size_shift[0] = PGSZCODE_TO_SHIFT(5); | |
309 | procp->real_page_size.page_size_shift[1] = PGSZCODE_TO_SHIFT(6); | |
310 | procp->real_page_size.page_size_shift[2] = PGSZCODE_TO_SHIFT(7); | |
311 | procp->real_page_size.page_size_shift[3] = PGSZCODE_TO_SHIFT(0); | |
312 | procp->real_page_size.page_size_shift[4] = PGSZCODE_TO_SHIFT(1); | |
313 | procp->real_page_size.page_size_shift[5] = PGSZCODE_TO_SHIFT(2); | |
314 | procp->real_page_size.page_size_shift[6] = PGSZCODE_TO_SHIFT(3); | |
315 | procp->real_page_size.page_size_shift[7] = PGSZCODE_TO_SHIFT(4); | |
316 | ||
317 | /* Initialize jrand48 randstate array to seed values */ | |
318 | procp->randstate[0] = RANDSEED1; | |
319 | procp->randstate[1] = RANDSEED2; | |
320 | procp->randstate[2] = RANDSEED3; | |
321 | ||
322 | procp->axis_console = false; | |
323 | procp->erratum66_monitor = false; | |
324 | procp->no_stingray = false; | |
325 | ||
326 | #ifdef ROCK_FAKE_SP /* { */ | |
327 | /* For 'fake_sp', default chip id is based on conf file order. */ | |
328 | procp->fake_chip_id = procp->config_procp->domainp->systemp->fake_sp ? | |
329 | config_procp->proc_id : 0; | |
330 | #endif /* } */ | |
331 | #endif | |
332 | ||
333 | #if PERFORMANCE_CHECK | |
334 | procp->proc_debug.perf_cycle_gap = PERF_CYCLE_GAP; | |
335 | procp->proc_debug.exit_at = 0x0ULL; | |
336 | #endif | |
337 | return procp; | |
338 | } | |
339 | ||
340 | #if ERROR_INJECTION | |
341 | ||
342 | void extract_error_op(error_conf_t * errorconfp) | |
343 | { | |
344 | errorconfp->op_namep = strdup(lex.strp); | |
345 | ||
346 | if (streq(lex.strp,"ifetch")) | |
347 | errorconfp->op = IFETCH; | |
348 | else if (streq(lex.strp,"ld")) | |
349 | errorconfp->op = LD; | |
350 | else if (streq(lex.strp,"asi_ld")) | |
351 | errorconfp->op = ASI_LD; | |
352 | else if (streq(lex.strp,"st")) | |
353 | errorconfp->op = ST; | |
354 | else if (streq(lex.strp,"any")) | |
355 | errorconfp->op = ANY_OP; | |
356 | else | |
357 | lex_fatal("unknown error type parsing error config"); | |
358 | } | |
359 | ||
360 | void extract_error_priv(error_conf_t * errorconfp) | |
361 | { | |
362 | errorconfp->priv_namep = strdup(lex.strp); | |
363 | ||
364 | if (streq(lex.strp,"hyper")) | |
365 | errorconfp->priv = V9_HyperPriv; | |
366 | else if (streq(lex.strp,"priv")) | |
367 | errorconfp->priv = V9_Priv; | |
368 | else if (streq(lex.strp,"user")) | |
369 | errorconfp->priv = V9_User; | |
370 | else if (streq(lex.strp,"red")) | |
371 | errorconfp->priv = V9_RED; | |
372 | else | |
373 | lex_fatal("unknown error type parsing error config"); | |
374 | } | |
375 | ||
376 | void dump_errconf(error_conf_t * ep) { | |
377 | uint_t count = 1; | |
378 | ||
379 | while (ep != NULL) { | |
380 | lprintf(-1, "Error Config #%u\n",count); | |
381 | lprintf(-1, " cycle: %llu\n",ep->cycle); | |
382 | lprintf(-1, " type: %s\n",ep->type_namep); | |
383 | lprintf(-1, " op: %s\n",ep->op_namep); | |
384 | lprintf(-1, " priv: %s\n",ep->priv_namep); | |
385 | ep = ep->nextp; | |
386 | count++; | |
387 | } | |
388 | } | |
389 | ||
390 | void parse_error(domain_t * domainp) | |
391 | { | |
392 | error_conf_t **pep, *ep, *errorconfp; | |
393 | ||
394 | errorconfp = Xmalloc( sizeof(error_conf_t) ); | |
395 | errorconfp->cycle = ~0ull; | |
396 | errorconfp->priv = V9_HyperPriv; | |
397 | errorconfp->type = NULL; | |
398 | errorconfp->op = NULL; | |
399 | errorconfp->npp = NULL; | |
400 | ||
401 | lex_get(T_L_Brace); | |
402 | ||
403 | do { | |
404 | lexer_tok_t tok; | |
405 | ||
406 | tok = lex_get_token(); | |
407 | ||
408 | if (tok == T_EOF) | |
409 | lex_fatal("unexpected end of file parsing cpu"); | |
410 | if (tok == T_R_Brace) break; | |
411 | if (tok != T_Token) | |
412 | lex_fatal("token expected in SunSPARC processor specification"); | |
413 | ||
414 | if (streq(lex.strp,"cycle")) { | |
415 | errorconfp->cycle = parse_number_assign(); | |
416 | } else | |
417 | if (streq(lex.strp,"type")) { | |
418 | lex_get(T_String); | |
419 | extract_error_type(errorconfp); | |
420 | lex_get(T_S_Colon); | |
421 | } else | |
422 | if (streq(lex.strp,"op")) { | |
423 | lex_get(T_String); | |
424 | extract_error_op(errorconfp); | |
425 | lex_get(T_S_Colon); | |
426 | } else | |
427 | if (streq(lex.strp,"priv")) { | |
428 | lex_get(T_String); | |
429 | extract_error_priv(errorconfp); | |
430 | lex_get(T_S_Colon); | |
431 | } else | |
432 | lex_fatal("unknown token %s in error specification", | |
433 | lex.strp); | |
434 | } while (1); | |
435 | ||
436 | /* insert error_conf_t into list sorted by cycle */ | |
437 | for (pep = &(domainp->errlistp); (ep=*pep) != NULL; pep =&(ep->nextp) ) { | |
438 | if (errorconfp->cycle < ep->cycle) break; | |
439 | } | |
440 | ||
441 | errorconfp->nextp = *pep; | |
442 | *pep = errorconfp; | |
443 | ||
444 | lex_unget(); | |
445 | ||
446 | lex_get(T_R_Brace); | |
447 | } | |
448 | ||
449 | error_conf_t * new_errconf(error_op_t op, error_type_t type) { | |
450 | error_conf_t * ep; | |
451 | ||
452 | ep = Xcalloc(1, error_conf_t); | |
453 | ep->op = op; | |
454 | ep->type = type; | |
455 | ep->op_namep = strdup("unknown"); | |
456 | ep->type_namep = ep->op_namep; | |
457 | ep->priv_namep = ep->op_namep; | |
458 | return ep; | |
459 | } | |
460 | ||
461 | error_conf_t * find_errconf(simcpu_t * sp, error_op_t op, error_type_t type) { | |
462 | error_conf_t * ep; | |
463 | ||
464 | ep = sp->errorp->errlistp; | |
465 | ||
466 | while (ep != NULL) { | |
467 | if ((ep->op & op) && (ep->type & type)) break; | |
468 | ep = ep->nextp; | |
469 | } | |
470 | ||
471 | return ep; | |
472 | } | |
473 | ||
474 | /* | |
475 | * remove error config from the list of current errors | |
476 | * return NULL if no more errors in current error list although there may | |
477 | * still be more in the processor pending error list waiting for a cycle match | |
478 | */ | |
479 | ||
480 | bool_t remove_errconf(simcpu_t * sp, error_conf_t * rmep) { | |
481 | error_conf_t ** pep, * ep; | |
482 | ||
483 | for (pep = &(sp->errorp->errlistp); (ep=*pep) != NULL; pep =&(ep->nextp)) { | |
484 | if (ep == rmep) break; | |
485 | } | |
486 | *pep = ep->nextp; | |
487 | Xfree(rmep); | |
488 | ||
489 | return (sp->errorp->errlistp ? true : false); | |
490 | } | |
491 | ||
492 | void clear_errflags(simcpu_t * sp) { | |
493 | sp->error_enabled = false; | |
494 | sp->error_check = false; | |
495 | sp->errorp->check_xicache = false; | |
496 | sp->errorp->check_xdcache = false; | |
497 | sp->errorp->check_dtlb = false; | |
498 | } | |
499 | #endif /* ERROR_INJECTION */ | |
500 | ||
501 | /* | |
502 | * Complete the creation and parsing of this specific cpu | |
503 | * | |
504 | * .. basically parse and allocate what is necessary. | |
505 | */ | |
506 | ||
507 | void ss_parse(domain_t * domainp, config_proc_t * config_procp) | |
508 | { | |
509 | ss_proc_t *procp; | |
510 | lexer_tok_t tok; | |
511 | char buf[64]; | |
512 | config_dev_t *pd; | |
513 | config_dev_t *overlapp; | |
514 | config_addr_t *ap; | |
515 | bool_t debug_hook_defined = false; | |
516 | uint64_t manuf = SS_VER_MANUF; | |
517 | uint64_t impl = SS_VER_IMPL; | |
518 | uint64_t mask = SS_VER_MASK; | |
519 | uint_t nvthreads = 0, ncores = 0; | |
520 | uint64_t core_mask = 0; | |
521 | ||
522 | sprintf(buf, "cpu-%02d", config_procp->proc_id); | |
523 | DBG( lprintf(-1, "SunSPARC: parsing cpu %s\n", buf); ); | |
524 | ||
525 | /* | |
526 | * fill in with default values where necessary | |
527 | */ | |
528 | ||
529 | procp = ss_proc_alloc(config_procp); | |
530 | ||
531 | do { | |
532 | tok = lex_get_token(); | |
533 | ||
534 | if (tok == T_EOF) lex_fatal("unexpected end of file parsing cpu"); | |
535 | if (tok == T_R_Brace) break; | |
536 | if (tok != T_Token) lex_fatal("token expected in SunSPARC processor specification"); | |
537 | ||
538 | if (streq(lex.strp,"rstv")) { | |
539 | procp->rstv_addr = parse_number_assign(); | |
540 | if ((procp->rstv_addr & 31) != 0) lex_fatal("SunSPARC CPU RSTV addr needs to be 32byte aligned"); | |
541 | } else | |
542 | if (streq(lex.strp,"maxtl")) { | |
543 | procp->maxtl = parse_number_assign(); | |
544 | if (procp->maxtl >= SPARCv9_TLSPACE) fatal("Sorry maxtl must be %u or less (recompile sim for more)", SPARCv9_TLSPACE-1); | |
545 | } else | |
546 | if (streq(lex.strp,"ver")) { | |
547 | tok = lex_get_token(); | |
548 | if (tok == T_Number) { | |
549 | lex_unget(); | |
550 | procp->ver = parse_number_assign() & | |
551 | (0xFFFFFFFFFFULL<<SS_VER_MASK_OFFSET); | |
552 | } else | |
553 | if (tok == T_String) { | |
554 | while (tok != T_S_Colon) { | |
555 | if (streq(lex.strp,"mask")) { | |
556 | lex_get(T_Number); | |
557 | mask = lex.val; | |
558 | } else | |
559 | if (streq(lex.strp,"impl")) { | |
560 | lex_get(T_Number); | |
561 | impl = lex.val; | |
562 | } else | |
563 | if (streq(lex.strp,"manuf")) { | |
564 | lex_get(T_Number); | |
565 | manuf = lex.val; | |
566 | } else { | |
567 | printf("\nUnknown ver option %s\n", lex.strp); | |
568 | } | |
569 | tok = lex_get_token(); | |
570 | } | |
571 | } | |
572 | } else | |
573 | if (streq(lex.strp,"clkfreq")) { | |
574 | procp->clkfreq = parse_number_assign(); | |
575 | } else | |
576 | if (streq(lex.strp,"core_mask")) { | |
577 | core_mask = parse_number_assign(); | |
578 | } else | |
579 | if (streq(lex.strp,"cores")) { | |
580 | ncores = parse_number_assign(); | |
581 | } else | |
582 | if (streq(lex.strp, "vthreads") || streq(lex.strp, "strands")) { | |
583 | nvthreads = parse_number_assign(); | |
584 | } else | |
585 | if (streq(lex.strp,"nwins")) { | |
586 | procp->nwins = parse_number_assign(); | |
587 | } else | |
588 | if (streq(lex.strp,"nglobals")) { | |
589 | procp->nglobals = parse_number_assign(); | |
590 | } else | |
591 | if (streq(lex.strp,"itlb")) { | |
592 | ss_parse_tlb(&procp->itlbspec); | |
593 | } else | |
594 | if (streq(lex.strp,"dtlb")) { | |
595 | ss_parse_tlb(&procp->dtlbspec); | |
596 | } else | |
597 | if (streq(lex.strp,"nofpu")) { | |
598 | procp->has_fpu = false; | |
599 | lex_get(T_S_Colon); | |
600 | } else | |
601 | #ifdef NIAGARA2 | |
602 | if (streq(lex.strp, "crypto_synchronous")) { | |
603 | procp->crypto_synchronous = !!parse_number_assign(); | |
604 | } else | |
605 | #endif /* NIAGARA2 */ | |
606 | ||
607 | #if ERROR_INJECTION /* { */ | |
608 | if (streq(lex.strp,"error")) { | |
609 | parse_error(domainp); | |
610 | } else | |
611 | #endif /* ERROR_INJECTION } */ | |
612 | ||
613 | ||
614 | #if PERFORMANCE_CHECK /* { */ | |
615 | if (streq(lex.strp,"exit_at")) { | |
616 | procp->proc_debug.exit_at = parse_number_assign(); | |
617 | } else | |
618 | ||
619 | if (streq(lex.strp,"perf_cycle_gap")) { | |
620 | procp->proc_debug.perf_cycle_gap = parse_number_assign(); | |
621 | } else | |
622 | #endif /* PERFORMANCE_CHECK } */ | |
623 | ||
624 | if (streq(lex.strp, "debug_hook")) { | |
625 | if (debug_hook_defined) | |
626 | lex_fatal("Cannot have more than one debug_hook definition in conf file"); | |
627 | else | |
628 | debug_hook_defined = true; | |
629 | parse_debug_hook(procp); | |
630 | } else | |
631 | #if ERROR_TRAP_GEN /* { */ | |
632 | if (streq(lex.strp,"error_asi")) { | |
633 | ss_error_asi_parse(procp, false); | |
634 | } else | |
635 | if (streq(lex.strp,"error_event")) { | |
636 | ss_error_event_parse(procp, false); | |
637 | } else | |
638 | if (streq(lex.strp,"error_reload_file_name")) { | |
639 | ss_error_parse_filename(procp); | |
640 | } else | |
641 | #endif /* ERROR_TRAP_GEN } */ | |
642 | if (ss_parse_proc_entry(procp, domainp) == true) { | |
643 | /* Handled processor specific token */ | |
644 | } else | |
645 | lex_fatal("unknown token %s in cpu specification", lex.strp); | |
646 | } while (1); | |
647 | ||
648 | lex_unget(); /* put the r brace back for the caller ! FIXME */ | |
649 | ||
650 | #if ERROR_INJECTION | |
651 | dump_errconf(domainp->errlistp); | |
652 | #endif | |
653 | ||
654 | /* | |
655 | * Round out with values that didn't get set | |
656 | * during parsing. | |
657 | */ | |
658 | ||
659 | if (procp->clkfreq == 0) lex_fatal("clkfreq not specified in processor definition"); | |
660 | /* nvthreads and ncores are for backward compatibility */ | |
661 | if (core_mask == 0) { | |
662 | if (ncores == 0 && nvthreads == 0 ) lex_fatal("core_mask not specified in processor definition"); | |
663 | if (ncores == 0) lex_fatal("ncores not specified in processor definition"); | |
664 | if (ncores > CORESPERCHIP) lex_fatal("ncores must be <= %d", CORESPERCHIP); | |
665 | if (nvthreads == 0) lex_fatal("nvthreads not specified in processor definition"); | |
666 | if (nvthreads > STRANDSPERCORE) lex_fatal("nvthreads must be <= %d", STRANDSPERCORE); | |
667 | core_mask = (1 << nvthreads) - 1; | |
668 | for (ncores--; ncores > 0 ; ncores--) | |
669 | core_mask |= core_mask << STRANDSPERCORE; | |
670 | } else { | |
671 | if (ncores != 0 || nvthreads != 0) | |
672 | lex_fatal("nvthreads/ncores should not be present when using core_mask"); | |
673 | /* check for non-existant strands */ | |
674 | if ((core_mask & VALID_CORE_MASK) != core_mask) | |
675 | lex_fatal("core_mask has bits for non-existant strands"); | |
676 | } | |
677 | procp->core_mask = core_mask; | |
678 | if (procp->nwins != (SS_VER_MAXWIN + 1)) | |
679 | warning("nwins set to non-standard value for this processor"); | |
680 | if (procp->maxtl != SS_VER_MAXTL) | |
681 | warning("maxtl set to non-standard value for this processor"); | |
682 | if (procp->nglobals != (SS_VER_MAXGL + 1)) | |
683 | warning("nglobals set to non-standard value for this processor"); | |
684 | if (procp->ver == 0) | |
685 | procp->ver = SS_MAKE_VER_UPPER40(manuf, impl, mask); | |
686 | ||
687 | procp->ver |= SS_MAKE_VER_LOWER24(procp->nglobals, procp->maxtl, | |
688 | procp->nwins); | |
689 | ||
690 | /* | |
691 | * So now we know we got a specific chip (Niagara or Rock for example) in the domain, | |
692 | * we create the pseudo physical IO devices that this chip has for it's control registers. | |
693 | * For things like the clock unit and memory controllers etc. | |
694 | */ | |
695 | ss_setup_pseudo_devs(domainp, procp); | |
696 | } | |
697 | ||
698 | /* | |
699 | * Parse a TLB description for either the | |
700 | * I or D TLB for the proc. | |
701 | */ | |
702 | ||
703 | void ss_parse_tlb(ss_tlb_spec_t * tlbspecp) | |
704 | { | |
705 | /* just swallow for the moment */ | |
706 | lex_get(T_L_Brace); | |
707 | do { | |
708 | lexer_tok_t tok; | |
709 | ||
710 | tok = lex_get_token(); | |
711 | ||
712 | if (tok == T_EOF) lex_fatal("unexpected end of file parsing SunSPARC CPU tlb spec"); | |
713 | if (tok == T_R_Brace) break; | |
714 | if (tok != T_Token) lex_fatal("token expected in SunSPARC processor tlb specification"); | |
715 | ||
716 | if (streq(lex.strp,"entries")) { | |
717 | uint_t num; | |
718 | num = (uint_t)parse_number_assign(); | |
719 | if (num<1) lex_fatal("TLB requires at least one entry"); | |
720 | if (num>4096) lex_fatal("More than 4096 entries is not allowed"); | |
721 | ||
722 | tlbspecp->nentries = num; | |
723 | } else if (streq(lex.strp,"parity")) { | |
724 | tlbspecp->parity = true; | |
725 | lex_get(T_S_Colon); | |
726 | } else | |
727 | lex_fatal("unknown token %s in cpu TLB specification", lex.strp); | |
728 | } while (1); | |
729 | } | |
730 | ||
731 | ||
732 | ||
733 | ||
734 | ||
735 | ||
736 | static bool_t | |
737 | ss_check_vahole(simcpu_t* sp, tvaddr_t pc) | |
738 | { | |
739 | #if /* defined(NIAGARA1) || */ defined(NIAGARA2) /* { */ | |
740 | if (VA48(pc) != pc) { | |
741 | sparcv9_cpu_t * v9p; | |
742 | ss_strand_t * nsp; | |
743 | ||
744 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
745 | nsp = v9p->impl_specificp; | |
746 | SET_DTLB_FAULT( nsp, VA48(pc) ); | |
747 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t) N2_trap_mem_address_range); | |
748 | return true; | |
749 | } | |
750 | #endif /* } */ | |
751 | return false; | |
752 | } | |
753 | ||
754 | ||
755 | ||
756 | ||
757 | #if WALL_TIME /* { */ | |
758 | ||
759 | hrtime_t base_hrtime; | |
760 | ||
761 | /*ARGSUSED*/ | |
762 | static void | |
763 | init_tick_counter_scale(tick_counter_t *tcp, uint64_t freq) | |
764 | { | |
765 | if (!options.walltime) | |
766 | return; | |
767 | ||
768 | /* We convert gethrtime() to the simulated tick_counter frequency. */ | |
769 | tcp->scale = (double)freq / (double)NANOSEC; | |
770 | tcp->scale_recip = 1.0 / tcp->scale; | |
771 | if (base_hrtime == 0) | |
772 | base_hrtime = RAW_HRTIME; | |
773 | } | |
774 | #endif /* } */ | |
775 | ||
776 | ||
777 | static uint_t | |
778 | count_bits(uint64_t m) | |
779 | { | |
780 | uint_t n; | |
781 | ||
782 | for (n = 0; m != 0; m &= m-1) | |
783 | n++; | |
784 | return n; | |
785 | } | |
786 | ||
787 | static uint_t | |
788 | count_cores(uint64_t m) | |
789 | { | |
790 | uint_t n; | |
791 | ||
792 | for (n = 0; m != 0; m >>= STRANDSPERCORE) | |
793 | n++; | |
794 | return n + 1; | |
795 | } | |
796 | ||
797 | ||
798 | /* | |
799 | * Initialise the processor after parsing is complete | |
800 | * In this case we create a number of SPARC v9 sparccpu_t | |
801 | * one per strand available in this processor. | |
802 | */ | |
803 | ||
804 | void ss_init(domain_t * domainp, config_proc_t * config_procp) | |
805 | { | |
806 | extern void debug_init(simcpu_t *sp); | |
807 | uint_t ncpus; | |
808 | ss_proc_t * pnp; | |
809 | uint_t idx, core, vthread; | |
810 | uint_t vcore_id; | |
811 | uint64_t core_avail = 0, core_running = 0; | |
812 | ss_tlb_t * dtlbp, * itlbp, * tlbp; | |
813 | sparcv9_cpu_t * tick_v9p = NULL; | |
814 | uint_t ncores; | |
815 | uint64_t tmp_core_mask; | |
816 | ||
817 | pnp = (ss_proc_t*)(config_procp->procp); | |
818 | ||
819 | /* | |
820 | * OK for each core and vthread create a sparc v9 CPU | |
821 | * with appropriate call backs to the SunSPARC CPU module for | |
822 | * CPU specific components. | |
823 | */ | |
824 | ||
825 | /* | |
826 | * FIXME: due to core indexing of some arrays, we get the | |
827 | * max core number plus one. | |
828 | */ | |
829 | ncores = count_cores(pnp->core_mask); | |
830 | ncpus = count_bits(pnp->core_mask); | |
831 | pnp->nstrands = ncpus; | |
832 | ||
833 | pnp->strand = Xcalloc(ncpus, sparcv9_cpu_t *); | |
834 | ||
835 | /* Note: allocate actual structs - NOT pointer list */ | |
836 | pnp->ss_strandp = Xcalloc(ncpus, ss_strand_t); | |
837 | ||
838 | /* Init HW strand# to ss_strandp[]/starnd[] index table */ | |
839 | for (idx = 0; idx < STRANDS_PER_CHIP; idx++) | |
840 | pnp->str_to_idx[idx] = NO_STRAND; | |
841 | ||
842 | #ifdef ROCK /* { */ | |
843 | ASSERT(ncpus <= STRANDS_PER_CHIP); | |
844 | /* | |
845 | * Rock has only one Unified (IMMU and DMMU) TLB per chip. | |
846 | */ | |
847 | pnp->tlbp = Xcalloc(1, ss_tlb_t); | |
848 | tlbp = pnp->tlbp; | |
849 | ss_tlb_init(tlbp, pnp->tlbspec.nentries); | |
850 | tlbp->parity = pnp->tlbspec.parity; | |
851 | tlbp->lru_array = Xcalloc(ROCK_TLB_SETS, uint8_t); | |
852 | ||
853 | /* | |
854 | * Create Reverse Mapped Table for Rock interrupts | |
855 | */ | |
856 | pnp->rmt_basep = Xcalloc(STRANDS_PER_CHIP, uint64_t); | |
857 | ||
858 | /* FIXME: set to false by reset, true by running 0->non-0 */ | |
859 | pnp->sp_read_dis = true; | |
860 | pnp->sp_write_dis = true; | |
861 | ||
862 | #endif /* } */ | |
863 | ||
864 | pnp->ndtlb = ncores; | |
865 | pnp->nitlb = ncores; | |
866 | pnp->dtlbp = Xcalloc(pnp->ndtlb, ss_tlb_t); | |
867 | pnp->itlbp = Xcalloc(pnp->nitlb, ss_tlb_t); | |
868 | ||
869 | pnp->icachep = Xcalloc(ncores, ss_l1_cache_t); | |
870 | pnp->dcachep = Xcalloc(ncores, ss_l1_cache_t); | |
871 | ||
872 | #if INTERNAL_BUILD /* { */ | |
873 | #ifdef NIAGARA1 | |
874 | pnp->mod_arith_p = (mod_arith_t *) Xcalloc(ncores, mod_arith_t); | |
875 | (void) pthread_mutex_init(&pnp->mod_arith_p->lock, NULL); | |
876 | #endif | |
877 | #ifdef NIAGARA2 | |
878 | pnp->stream_cwq_p = (asi_stream_CWQ_t *) Xcalloc(ncores, asi_stream_CWQ_t); | |
879 | pnp->mod_arith_p = (asi_stream_MA_t *) Xcalloc(ncores, asi_stream_MA_t); | |
880 | (void) pthread_mutex_init(&pnp->stream_cwq_p->cwq_mtx, NULL); | |
881 | (void) pthread_cond_init(&pnp->stream_cwq_p->cwq_cv, NULL); | |
882 | (void) pthread_mutex_init(&pnp->mod_arith_p->ma_mtx, NULL); | |
883 | (void) pthread_cond_init(&pnp->mod_arith_p->ma_cv, NULL); | |
884 | #endif | |
885 | #endif /* INTERNAL_BUILD } */ | |
886 | ||
887 | #ifdef NIAGARA2 | |
888 | /* Initialised to zeros by default */ | |
889 | pnp->sparc_power_mgmtp = Xcalloc(ncores, sparc_power_mgmt_t); | |
890 | #endif | |
891 | ||
892 | ||
893 | #if ERROR_INJECTION /* { */ | |
894 | pnp->pend_errlistp = domainp->errlistp; | |
895 | pthread_mutex_init(&pnp->err_lock, NULL); | |
896 | pnp->errorp = Xcalloc(1, error_proc_t); | |
897 | pnp->error_check = false; | |
898 | #endif /* } */ | |
899 | ||
900 | #ifdef ROCK /* { */ | |
901 | pthread_mutex_init(&pnp->ucr_lock, NULL); | |
902 | pthread_mutex_init(&pnp->tw_lock, NULL); | |
903 | #endif /* } */ | |
904 | ||
905 | #if ERROR_TRAP_GEN /* { */ | |
906 | ss_error_trap_proc_init(config_procp); | |
907 | #endif /* } ERROR_TRAP_GEN */ | |
908 | ||
909 | #if defined(ROCK) /* { */ | |
910 | tick_v9p = NULL; | |
911 | #endif /* } */ | |
912 | ||
913 | tmp_core_mask = pnp->core_mask; | |
914 | ||
915 | idx = 0; | |
916 | for (core=0; core < CORESPERCHIP; core++, | |
917 | tmp_core_mask >>= STRANDSPERCORE) { | |
918 | ss_l1_cache_t * icachep, * dcachep; | |
919 | ||
920 | /* skip cores with no strands */ | |
921 | if ((tmp_core_mask & ((1u << STRANDSPERCORE) - 1)) == 0) | |
922 | continue; | |
923 | ||
924 | /* First init the TLB structures each core uses */ | |
925 | itlbp = &(pnp->itlbp[core]); | |
926 | dtlbp = &(pnp->dtlbp[core]); | |
927 | ||
928 | ss_tlb_init(itlbp, pnp->itlbspec.nentries); | |
929 | itlbp->parity = pnp->itlbspec.parity; | |
930 | ss_tlb_init(dtlbp, pnp->dtlbspec.nentries); | |
931 | dtlbp->parity = pnp->dtlbspec.parity; | |
932 | ||
933 | icachep = &(pnp->icachep[core]); | |
934 | dcachep = &(pnp->dcachep[core]); | |
935 | ||
936 | ss_l1_cache_init(icachep, SS_ICACHE_SIZE, true); | |
937 | ss_l1_cache_init(dcachep, SS_DCACHE_SIZE, false); | |
938 | ||
939 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
940 | tick_v9p = NULL; | |
941 | #endif /* } */ | |
942 | ||
943 | for (vthread=0; vthread < STRANDSPERCORE; vthread++) { | |
944 | sparcv9_cpu_t * v9p; | |
945 | ss_strand_t * nsp; | |
946 | simcpu_t * sp; | |
947 | ||
948 | /* skip missing strands */ | |
949 | if ((tmp_core_mask & (1u << vthread)) == 0) | |
950 | continue; | |
951 | ||
952 | /* Need something here to determine which | |
953 | * scheduler wheel the simcpu_t ends up on. | |
954 | */ | |
955 | v9p = sparcv9_cpu_alloc(domainp, | |
956 | config_procp, | |
957 | pnp->nwins, pnp->nglobals, pnp->maxtl, | |
958 | pnp->ver, pnp->has_fpu, | |
959 | pnp); | |
960 | ||
961 | pnp->strand[idx] = v9p; | |
962 | ||
963 | /* | |
964 | * Tie to SunSPARC CPU specific info | |
965 | */ | |
966 | ||
967 | nsp = &(pnp->ss_strandp[idx]); | |
968 | sp = v9p->simp; | |
969 | ||
970 | #if 0 | |
971 | EXEC_WARNING(("ss_init: core: %u strand: %u idx: %u nsp: %p sp: %p", core, vthread, idx, nsp, sp)); | |
972 | #endif | |
973 | ||
974 | v9p->impl_specificp = nsp; | |
975 | ||
976 | v9p->read_state_reg = ss_read_state_reg; | |
977 | v9p->write_state_reg = ss_write_state_reg; | |
978 | v9p->read_priv_reg = ss_read_priv_reg; | |
979 | v9p->write_priv_reg = ss_write_priv_reg; | |
980 | v9p->read_hyp_priv_reg = ss_read_hyp_priv_reg; | |
981 | v9p->write_hyp_priv_reg = ss_write_hyp_priv_reg; | |
982 | v9p->check_vahole = ss_check_vahole; | |
983 | v9p->done_retry = ss_done_retry; | |
984 | v9p->jpriv = ss_jpriv; | |
985 | v9p->asi_access = ss_asi_access; | |
986 | v9p->post_precise_trap = ss_post_precise_trap; | |
987 | ||
988 | /* | |
989 | * Initialize shared tick/stick register pointers. | |
990 | */ | |
991 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
992 | if (tick_v9p == NULL) { | |
993 | tick_v9p = v9p; | |
994 | #if WALL_TIME /* { */ | |
995 | init_tick_counter_scale(&tick_v9p->_tick, | |
996 | pnp->clkfreq); | |
997 | #endif /* } */ | |
998 | } | |
999 | /* STICK is an alias of TICK */ | |
1000 | v9p->stick = v9p->tick = &tick_v9p->_tick; | |
1001 | #elif ROCK /* } { */ | |
1002 | if (tick_v9p == NULL) { | |
1003 | tick_v9p = v9p; | |
1004 | #if WALL_TIME /* { */ | |
1005 | init_tick_counter_scale(&tick_v9p->_tick, | |
1006 | pnp->clkfreq); | |
1007 | init_tick_counter_scale(&tick_v9p->_stick, | |
1008 | domainp->sysclkfreq); | |
1009 | #endif /* } */ | |
1010 | } | |
1011 | v9p->tick = &tick_v9p->_tick; | |
1012 | v9p->stick = &tick_v9p->_stick; | |
1013 | #else /* } { */ | |
1014 | #error "No valid processor defined." | |
1015 | #endif /* } */ | |
1016 | ||
1017 | v9p->tick_cmpr.counter = v9p->tick; | |
1018 | v9p->stick_cmpr.counter = v9p->stick; | |
1019 | v9p->hstick_cmpr.counter = v9p->stick; | |
1020 | ||
1021 | sp->cycle_target_match = ss_cycle_target_match; | |
1022 | ||
1023 | /* | |
1024 | * determine virtual core ID | |
1025 | */ | |
1026 | nsp->core = core; | |
1027 | nsp->vthread = vthread; | |
1028 | vcore_id = (core << SS_COREID_SHIFT) | vthread; | |
1029 | nsp->vcore_id = vcore_id; | |
1030 | pnp->str_to_idx[vcore_id] = idx; | |
1031 | core_avail |= 1ull << vcore_id; | |
1032 | /* | |
1033 | * CMP states that the only the lowest numbered | |
1034 | * strand starts executing. | |
1035 | */ | |
1036 | if (core_running == 0) | |
1037 | core_running |= 1ull << vcore_id; | |
1038 | #ifdef ROCK_FAKE_SP /* { */ | |
1039 | if (pnp->config_procp->domainp->systemp->fake_sp) { | |
1040 | /* | |
1041 | * SP initializes the current strand id | |
1042 | * in a hyper scratch pad register. | |
1043 | * Legion temporarily handles this job | |
1044 | * by combining vcore_id and chip_id | |
1045 | * in HSCRATCH0 register. | |
1046 | */ | |
1047 | nsp->strand_reg[SSR_HSCRATCHPAD_INDEX] = | |
1048 | vcore_id | | |
1049 | (pnp->fake_chip_id << RK_CHIP_ID_SHIFT); | |
1050 | DBGSCRATCH( lprintf(sp->gid, "HSCRATCH0 init 0x%llx\n", | |
1051 | nsp->strand_reg[SSR_HSCRATCHPAD_INDEX]); ); | |
1052 | } | |
1053 | #endif /* } */ | |
1054 | ||
1055 | /* | |
1056 | * set up the execution instruction cache for it | |
1057 | */ | |
1058 | sp->xicachep = xicache_alloc(sp); | |
1059 | sp->xic_miss = ss_xic_miss; | |
1060 | ||
1061 | /* | |
1062 | * Now set up the execution data cache .. | |
1063 | */ | |
1064 | sp->xdc.miss = ss_xdc_miss; | |
1065 | ||
1066 | ||
1067 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
1068 | /* | |
1069 | * Init internal strand control registers | |
1070 | */ | |
1071 | nsp->lsu_control_raw = LSU_CTRL_INITVAL; | |
1072 | nsp->dmmu.enabled = (nsp->lsu_control_raw & LSU_CTRL_DMMU_EN) != 0; | |
1073 | nsp->immu.enabled = (nsp->lsu_control_raw & LSU_CTRL_IMMU_EN) != 0; | |
1074 | ||
1075 | /* | |
1076 | * Init the strand's MMU ... | |
1077 | */ | |
1078 | nsp->dmmu.is_immu = false; | |
1079 | nsp->immu.is_immu = true; | |
1080 | ||
1081 | #elif ROCK /* } { */ | |
1082 | /* | |
1083 | * Init internal strand control registers | |
1084 | */ | |
1085 | ||
1086 | nsp->mmu.dmmu_enabled = false; | |
1087 | nsp->mmu.immu_enabled = false; | |
1088 | ||
1089 | /* | |
1090 | * This is_immu field gets updated each time before it is used, but | |
1091 | * just so that we have a starting point... | |
1092 | */ | |
1093 | nsp->mmu.is_immu = false; | |
1094 | nsp->tlbp = tlbp; | |
1095 | tlbp->shares++; | |
1096 | ||
1097 | /* | |
1098 | * Initialize some Transactional Memory stuff. | |
1099 | * N.B. tm_chkpt==NULL is taken to mean that this implementation | |
1100 | * does not support chkpt instruction. | |
1101 | * Similar assumption is made with respect to commit instruction. | |
1102 | */ | |
1103 | nsp->tm_chkpt = ss_tm_chkpt; | |
1104 | nsp->tm_commit = ss_tm_commit; | |
1105 | nsp->tm_fail = ss_tm_fail; | |
1106 | TM_SET_INACTIVE(nsp); | |
1107 | ||
1108 | /* | |
1109 | * TM is disabled by default in Legion. This is | |
1110 | * enabled by hypervisor, dumbreset (reset.s) | |
1111 | */ | |
1112 | pnp->dcucr[core] = 0; | |
1113 | nsp->tm_enabled = false; | |
1114 | nsp->cps = 0; | |
1115 | ||
1116 | /* Floating Point Arithmetic Enable */ | |
1117 | nsp->fpae = false; | |
1118 | ||
1119 | pthread_mutex_init(&nsp->ifucr_lock, NULL); | |
1120 | ||
1121 | #else /* } { */ | |
1122 | #error "No valid processor defined." | |
1123 | #endif /* } */ | |
1124 | /* | |
1125 | * Other misc stuff ... | |
1126 | */ | |
1127 | nsp->dtlbp = dtlbp; | |
1128 | dtlbp->shares++; | |
1129 | nsp->itlbp = itlbp; | |
1130 | itlbp->shares++; | |
1131 | nsp->icachep = icachep; | |
1132 | nsp->dcachep = dcachep; | |
1133 | ||
1134 | #if ERROR_INJECTION /* { */ | |
1135 | if (pnp->pend_errlistp) | |
1136 | sp->error_priv = pnp->pend_errlistp->priv; | |
1137 | #endif /* } */ | |
1138 | ||
1139 | #if PERFORMANCE_CHECK /* { */ | |
1140 | /* FIXME: HACK TO GO AWAY */ | |
1141 | sp->last_hrtime = gethrtime(); | |
1142 | sp->total_hrtime = 0; | |
1143 | sp->perf_target = pnp->proc_debug.perf_cycle_gap; | |
1144 | ||
1145 | sp->xdc_hits = 0; | |
1146 | sp->xdc_misses = 0; | |
1147 | sp->xdc_flushes = 0; | |
1148 | sp->xic_hits = 0; | |
1149 | sp->xic_misses = 0; | |
1150 | sp->xic_flushes = 0; | |
1151 | ||
1152 | #endif /* } */ | |
1153 | ||
1154 | /* | |
1155 | * Per-thread performance counter | |
1156 | * initialization | |
1157 | */ | |
1158 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
1159 | nsp->pic0 = 0; | |
1160 | nsp->pic1 = 0; | |
1161 | nsp->pcr = 0LL; | |
1162 | RESET_PIC_SAMPLE_BASES(sp, nsp, v9p); | |
1163 | #endif /* } */ | |
1164 | ||
1165 | ||
1166 | /* | |
1167 | * Initialise with power on reset | |
1168 | */ | |
1169 | nsp->pending_precise_tt = SS_trap_NONE; | |
1170 | nsp->pending_async_tt = SS_trap_NONE; /* FIXME ? */ | |
1171 | ||
1172 | #if defined(NIAGARA2) || defined(ROCK) /* { */ | |
1173 | /* | |
1174 | * Niagara2 calculates the CMP virtual CORE_ID[5:0] | |
1175 | * and CORE_AVAIL[63:0] (physical core id in [5:3] | |
1176 | * and strand id in [2:0]). | |
1177 | * | |
1178 | * for example, an N2 with 4 physical cores and 2 strands per core, | |
1179 | * the virtual CORE_ID for the 8 strands would be numbered as | |
1180 | * 0,1,8,9,16,17,24,25, and the CORE_AVAIL would be set to 0x3030303. | |
1181 | * | |
1182 | * Rock contains up to 16 cores (micro cores). Each | |
1183 | * contains up to 2 strands. A config file specifies | |
1184 | * 4 cores 1 thread would have strand ids numbered as | |
1185 | * 0,2,4,6. | |
1186 | */ | |
1187 | ||
1188 | /* | |
1189 | * for Niagara 2, CPU's initial state is determined | |
1190 | * by the initial value of asi_core_running_status | |
1191 | * register | |
1192 | */ | |
1193 | if (((1ull << vcore_id) & core_running) == 0) { | |
1194 | SET_PARKED(sp); | |
1195 | } | |
1196 | ||
1197 | DBG( lprintf(sp->gid, "cpu0x%llx (%s)\n", | |
1198 | nsp->vcore_id, | |
1199 | RUNNABLE(sp) ?"Unparked":"Parked"); ); | |
1200 | #endif /* } */ | |
1201 | ||
1202 | ||
1203 | #if defined(NIAGARA1) /* { */ | |
1204 | /* | |
1205 | * For Niagara, the CPU's initial state is that only one thread | |
1206 | * will be running, the other threads are put in the idle state | |
1207 | * and are then parked. | |
1208 | * The Reset code for Niagara expects that only one cpu (can be | |
1209 | * any one) will sign-on after a POR so we just choose the first | |
1210 | * cpu (sp->gid ==0). | |
1211 | * | |
1212 | * Its like 'Highlander' - there can be only one ! | |
1213 | */ | |
1214 | if (sp->gid == 0) { | |
1215 | SET_THREAD_STS_SFSM(pnp, nsp, THREAD_STS_TSTATE_RUN); | |
1216 | CLR_PARKED(sp); | |
1217 | } else { | |
1218 | SET_THREAD_STS_SFSM(pnp, nsp, THREAD_STS_TSTATE_IDLE); | |
1219 | SET_PARKED(sp); | |
1220 | } | |
1221 | ||
1222 | DBG( lprintf(sp->gid, "cpu0x%llx " | |
1223 | "sfsm_state = 0x%llx (%s)\n", | |
1224 | nsp->vcore_id, pnp->sfsm_state[nsp->vcore_id], | |
1225 | RUNNABLE(sp) ?"Unparked":"Parked"); ); | |
1226 | #endif /* } NIAGARA1 */ | |
1227 | ||
1228 | debug_init(sp); | |
1229 | ||
1230 | #if ERROR_TRAP_GEN /* { */ | |
1231 | ss_error_trap_strand_init(config_procp, sp); | |
1232 | #endif /* } ERROR_TRAP_GEN */ | |
1233 | ||
1234 | /* fake up a start of execution */ | |
1235 | sp->config_procp->proc_typep->exec_setup(sp); | |
1236 | ||
1237 | /* do the reset */ | |
1238 | ss_reset_trap( sp, SS_trap_power_on_reset ); | |
1239 | ||
1240 | /* cleanup after "execution" */ | |
1241 | sp->config_procp->proc_typep->exec_cleanup(sp); | |
1242 | ||
1243 | idx ++; | |
1244 | ||
1245 | } /* for vtrhead */ | |
1246 | ||
1247 | } /* for core */ | |
1248 | ||
1249 | #ifdef NIAGARA1 /* { */ | |
1250 | /* | |
1251 | * For IOB: */ | |
1252 | pnp->core_avail = core_avail; | |
1253 | #endif /* } */ | |
1254 | ||
1255 | #ifdef NIAGARA2 /* { */ | |
1256 | /* | |
1257 | * Initialize CMP registers, each one is shared by all virtual cores. | |
1258 | * | |
1259 | * Implementation note on the storage: | |
1260 | * | |
1261 | * - asi_cmp_core_intr_id & asi_cmp_core_id are not stored internally | |
1262 | * as they can be calculated on the fly | |
1263 | * | |
1264 | * - cmp_regs.core_enable_status stores | |
1265 | * | |
1266 | * asi_core_available | |
1267 | * asi_core_enable_status | |
1268 | * asi_core_enable | |
1269 | * | |
1270 | * - cmp_regs.core_running_status stores | |
1271 | * | |
1272 | * asi_core_running_rw | |
1273 | * asi_core_running_w1s | |
1274 | * asi_core_running_w1c | |
1275 | * asi_core_running_status | |
1276 | * | |
1277 | */ | |
1278 | pnp->cmp_regs.core_enable_status = core_avail; | |
1279 | pnp->cmp_regs.xir_steering = core_avail; | |
1280 | pnp->cmp_regs.tick_enable = false; | |
1281 | pnp->cmp_regs.core_running_status = core_running; | |
1282 | ||
1283 | /* Also init tick_stop flag and its lock */ | |
1284 | pnp->tick_stop = true; | |
1285 | pthread_mutex_init(&pnp->tick_en_lock, NULL); | |
1286 | #endif /* } */ | |
1287 | ||
1288 | #ifdef ROCK_FAKE_SP /* { */ | |
1289 | /* | |
1290 | * Revisit it when SP can provide data for these registers. | |
1291 | */ | |
1292 | pnp->cmp_regs.strand_available = core_avail; | |
1293 | if (simstatus.sp.mode != true) { | |
1294 | pnp->cmp_regs.strand_enable = core_avail; | |
1295 | pnp->cmp_regs.strand_running = core_running; | |
1296 | pnp->cmp_regs.strand_running_status = core_running; | |
1297 | } else { | |
1298 | pnp->cmp_regs.strand_enable = 0; | |
1299 | pnp->cmp_regs.strand_running = 0; | |
1300 | ss_change_exec_state(pnp, 0); | |
1301 | pnp->cmp_regs.strand_running_status = 0; | |
1302 | } | |
1303 | #endif /* } */ | |
1304 | ||
1305 | #ifdef NIAGARA1 /* { */ | |
1306 | pthread_mutex_init(&pnp->thread_sts_lock, NULL); | |
1307 | #endif /* } */ | |
1308 | ||
1309 | #if defined(NIAGARA2) || defined(ROCK) /* { */ | |
1310 | pthread_mutex_init(&pnp->cmp_lock, NULL); | |
1311 | #endif /* } */ | |
1312 | ||
1313 | #ifdef ROCK /* { */ | |
1314 | pnp->sp_intr_status = 0; | |
1315 | pnp->spie_status = 1; | |
1316 | #endif /* } */ | |
1317 | } | |
1318 | ||
1319 | ||
1320 | ||
1321 | /************************************************************* | |
1322 | * | |
1323 | * Execution support functions (and instruction impls) | |
1324 | * | |
1325 | *************************************************************/ | |
1326 | ||
1327 | ||
1328 | /* | |
1329 | * When execution starts some simcpu_t setup may be required | |
1330 | */ | |
1331 | ||
1332 | void ss_exec_setup(simcpu_t * sp) | |
1333 | { | |
1334 | sparcv9_cpu_t * v9p = (sparcv9_cpu_t *)(sp->specificp); | |
1335 | ||
1336 | sparcv9_active_window(sp, v9p->cwp); | |
1337 | sparcv9_active_globals(sp, v9p->gl); | |
1338 | } | |
1339 | ||
1340 | ||
1341 | /* | |
1342 | * When execution stop some cleanup of state is required for debugger access | |
1343 | */ | |
1344 | ||
1345 | void ss_exec_cleanup(simcpu_t * sp) | |
1346 | { | |
1347 | sparcv9_active_window(sp, -1); | |
1348 | sparcv9_active_globals(sp, -1); | |
1349 | } | |
1350 | ||
1351 | ||
1352 | ||
1353 | ||
1354 | ||
1355 | ||
1356 | ||
1357 | ||
1358 | ||
1359 | /* | |
1360 | * Functions to dump the SunSPARC processor state | |
1361 | */ | |
1362 | ||
1363 | void ss_dump(domain_t * domainp, config_proc_t * config_procp) | |
1364 | { | |
1365 | ss_proc_t * procp; | |
1366 | ||
1367 | procp = config_procp->procp; | |
1368 | ||
1369 | pi("\t// processor node id %u\n", config_procp->proc_id); | |
1370 | pi("processor \"%s\" {\n", config_procp->proc_typep->proc_type_namep); | |
1371 | dumpinfo.indent++; | |
1372 | ||
1373 | pi("clkfreq 0x%llx ;\n", procp->clkfreq); | |
1374 | pi("core_mask 0x%llx ;\n", procp->core_mask); | |
1375 | pi("nwins %u ;\n", procp->nwins); | |
1376 | pi("maxtl %u ;\n", procp->maxtl); | |
1377 | pi("ver 0x%p ;\n", procp->ver); | |
1378 | if (!procp->has_fpu) pi("nofpu ;\n"); | |
1379 | ||
1380 | pi("rstv 0x%llx ;\n", procp->rstv_addr); | |
1381 | ||
1382 | pi("itlb {\n"); | |
1383 | dumpinfo.indent++; | |
1384 | pi("entries %u;\n", procp->itlbspec.nentries); | |
1385 | if (procp->itlbspec.parity) | |
1386 | pi("parity;\n"); | |
1387 | dumpinfo.indent--; | |
1388 | pi("}\n"); | |
1389 | pi("dtlb {\n"); | |
1390 | dumpinfo.indent++; | |
1391 | pi("entries %u;\n", procp->dtlbspec.nentries); | |
1392 | if (procp->dtlbspec.parity) | |
1393 | pi("parity;\n"); | |
1394 | dumpinfo.indent--; | |
1395 | pi("}\n"); | |
1396 | #if defined(ROCK) /* Rock has a unified L2 TLB also */ | |
1397 | pi("unified_tlb {\n"); | |
1398 | dumpinfo.indent++; | |
1399 | pi("entries %u;\n", procp->tlbspec.nentries); | |
1400 | if (procp->tlbspec.parity) | |
1401 | pi("parity;\n"); | |
1402 | dumpinfo.indent--; | |
1403 | pi("}\n"); | |
1404 | #endif | |
1405 | ||
1406 | dumpinfo.indent--; | |
1407 | pi("}\n"); | |
1408 | } | |
1409 | ||
1410 | ||
1411 | bool_t ss_dev_mem_access(config_proc_t *config_procp, tpaddr_t addr, uint8_t *datap, | |
1412 | uint64_t size, dev_access_t type) | |
1413 | { | |
1414 | domain_t *domainp; | |
1415 | config_addr_t *config_addrp; | |
1416 | tpaddr_t extent; | |
1417 | uint8_t *bufp; | |
1418 | ||
1419 | domainp = config_procp->domainp; | |
1420 | ||
1421 | config_addrp = find_domain_address(domainp, addr); | |
1422 | if (config_addrp == NULL) { | |
1423 | EXEC_WARNING(("ss_dev_mem_access: physical address 0x%llx not valid", addr)); | |
1424 | return false; | |
1425 | } | |
1426 | ||
1427 | /* | |
1428 | * For now only handle cacheable device memory | |
1429 | */ | |
1430 | extent = config_addrp->config_devp->dev_typep->dev_cacheable(config_addrp, type, | |
1431 | addr-config_addrp->baseaddr, &bufp); | |
1432 | if (extent < size) { | |
1433 | EXEC_WARNING(("ss_dev_mem_access: physical address 0x%llx out of range 0x%llx", | |
1434 | addr, extent)); | |
1435 | return false; | |
1436 | } | |
1437 | ||
1438 | /* | |
1439 | * Do memory read/write between physical memory (pointed by 'bufp') | |
1440 | * and device memory (pointed by 'datap') | |
1441 | */ | |
1442 | switch (type) { | |
1443 | case DA_Load: | |
1444 | memcpy(datap, bufp, size); | |
1445 | break; | |
1446 | case DA_Store: | |
1447 | memcpy(bufp, datap, size); | |
1448 | break; | |
1449 | default: | |
1450 | ASSERT(0); | |
1451 | } | |
1452 | ||
1453 | return true; | |
1454 | } | |
1455 | ||
1456 | ||
1457 | ||
1458 | /* | |
1459 | *------------------------------------------------------------ | |
1460 | * | |
1461 | * Debugger and generic interface code .. | |
1462 | * ... mostly vectoring into the V9 code. | |
1463 | * | |
1464 | *------------------------------------------------------------ | |
1465 | */ | |
1466 | ||
1467 | ||
1468 | ||
1469 | void * ss_dbgr_attach(domain_t * domainp, config_proc_t * config_procp, char * attach_strp) | |
1470 | { | |
1471 | ss_dbgr_t * ndp; | |
1472 | ss_proc_t * np; | |
1473 | ||
1474 | np = (ss_proc_t*)(config_procp->procp); | |
1475 | ||
1476 | ndp = Xcalloc(1, ss_dbgr_t); | |
1477 | ||
1478 | /* For now just fake up attaching to core 0 strand 0 FIXME */ | |
1479 | ||
1480 | ndp->domainp = domainp; | |
1481 | ndp->config_procp = config_procp; | |
1482 | ndp->core = 0; | |
1483 | ndp->strand = 0; | |
1484 | ||
1485 | ndp->strandp = np->strand[0]; | |
1486 | ||
1487 | return (void*)ndp; | |
1488 | } | |
1489 | ||
1490 | ||
1491 | ||
1492 | ||
1493 | void ss_dbgr_detach(void * ptr) | |
1494 | { | |
1495 | ss_dbgr_t * ndp = (ss_dbgr_t*)ptr; | |
1496 | ||
1497 | /* Clean up SunSPARC specific debugger state FIXME */ | |
1498 | ||
1499 | Xfree(ndp); | |
1500 | } | |
1501 | ||
1502 | ||
1503 | ||
1504 | /* | |
1505 | * read internal register ... | |
1506 | * ... returns false on failure ... | |
1507 | * should we specific regname, and not a number ? FIXME | |
1508 | */ | |
1509 | ||
1510 | bool_t ss_dbgr_regread(void * ptr, uint_t regnum, uint64_t * valp) | |
1511 | { | |
1512 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1513 | sparcv9_cpu_t * sp = ndp->strandp; | |
1514 | ||
1515 | /* Check for any SunSPARC specific regs first */ | |
1516 | ||
1517 | return sparcv9_regread(sp, regnum, valp); | |
1518 | } | |
1519 | ||
1520 | ||
1521 | /* returns false on failure */ | |
1522 | ||
1523 | bool_t ss_dbgr_regwrite(void * ptr, uint_t regnum, uint64_t val) | |
1524 | { | |
1525 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1526 | sparcv9_cpu_t * sp = ndp->strandp; | |
1527 | ||
1528 | /* Check for any SunSPARC specific regs first */ | |
1529 | ||
1530 | return sparcv9_regwrite(sp, regnum, val); | |
1531 | } | |
1532 | ||
1533 | ||
1534 | uint64_t ss_dbgr_mem_read(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint8_t * bufp, uint64_t len) | |
1535 | { | |
1536 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1537 | ||
1538 | return ss_cpu_mem( | |
1539 | ndp->domainp, | |
1540 | ndp->config_procp->procp, | |
1541 | ndp->strandp, | |
1542 | NA_mem_read, | |
1543 | vaddr, do_translate, | |
1544 | bufp, len); | |
1545 | } | |
1546 | ||
1547 | ||
1548 | uint64_t ss_dbgr_mem_write(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint8_t * bufp, uint64_t len) | |
1549 | { | |
1550 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1551 | ||
1552 | return ss_cpu_mem( | |
1553 | ndp->domainp, | |
1554 | ndp->config_procp->procp, | |
1555 | ndp->strandp, | |
1556 | NA_mem_write, | |
1557 | vaddr, do_translate, | |
1558 | bufp, len); | |
1559 | } | |
1560 | ||
1561 | ||
1562 | ||
1563 | uint64_t ss_dbgr_mem_clear(void * ptr, tvaddr_t vaddr, bool_t do_translate, uint64_t len) | |
1564 | { | |
1565 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1566 | ||
1567 | return ss_cpu_mem( | |
1568 | ndp->domainp, | |
1569 | ndp->config_procp->procp, | |
1570 | ndp->strandp, | |
1571 | NA_mem_clear, | |
1572 | vaddr, do_translate, | |
1573 | (void*)0, len); | |
1574 | } | |
1575 | ||
1576 | ||
1577 | uint64_t | |
1578 | ss_cpu_mem( | |
1579 | domain_t * domainp, | |
1580 | void *procp_param, | |
1581 | sparcv9_cpu_t * strandp, | |
1582 | dbgr_mem_op_t memop, | |
1583 | tvaddr_t addr, | |
1584 | bool_t do_translate, | |
1585 | uint8_t * bp, | |
1586 | uint_t len) | |
1587 | { | |
1588 | uint64_t tx_count; | |
1589 | uint64_t tx_size; | |
1590 | uint64_t tx_left; | |
1591 | uint8_t * bufp; | |
1592 | uint64_t size; | |
1593 | tvaddr_t va; | |
1594 | ss_proc_t *procp = (ss_proc_t *)procp_param; | |
1595 | ||
1596 | va = addr; | |
1597 | bufp = (uint8_t*)bp; | |
1598 | tx_size = len; | |
1599 | tx_left = len; | |
1600 | ||
1601 | /* | |
1602 | * We keep going until either there is no | |
1603 | * translation available, or the target memory | |
1604 | * device fails us. | |
1605 | * *valid_lenp tells us how much succeeded | |
1606 | */ | |
1607 | ||
1608 | for (tx_count = 0LL; tx_count < tx_size; tx_count += size) { | |
1609 | tpaddr_t pa; | |
1610 | uint64_t vlen; | |
1611 | ||
1612 | pa = (tpaddr_t)va; | |
1613 | ||
1614 | size = tx_left; | |
1615 | /* insane max !! */ | |
1616 | if (size >= (1LL<<30)) size = 1LL<<30; | |
1617 | ||
1618 | /* First translate if necessary vtop */ | |
1619 | if (do_translate) { | |
1620 | if (!ss_vtop_translate(procp, strandp, | |
1621 | va, &pa, &vlen)) return false; | |
1622 | if (size > vlen) size = vlen; | |
1623 | } | |
1624 | ||
1625 | /* Note: if cpu had virtual caches we'd have | |
1626 | * to pass in VA here too | |
1627 | */ | |
1628 | size = ss_pmem_op(domainp, procp, strandp, memop, pa, bufp, size); | |
1629 | if (size == 0) break; | |
1630 | ||
1631 | bufp += size; | |
1632 | tx_left -= size; | |
1633 | va += size; | |
1634 | } | |
1635 | ||
1636 | /* returns the amount successfully handled - 0 if failed */ | |
1637 | return tx_count; | |
1638 | } | |
1639 | ||
1640 | /* | |
1641 | * Handles the V to P conversion for us for a given strand. | |
1642 | * Of course = 1:1 if in hypervisor mode. FIXME | |
1643 | */ | |
1644 | ||
1645 | static bool_t ss_vtop_translate( ss_proc_t * np, sparcv9_cpu_t * strandp, tvaddr_t va, tpaddr_t *pap, tvaddr_t * vlenp) | |
1646 | { | |
1647 | /* HACK FOR NOW - FIXME */ | |
1648 | *pap = va; | |
1649 | *vlenp = 0x10000-(va & 0xffff); /* round to end of 64K page ;-) */ | |
1650 | ||
1651 | return true; | |
1652 | } | |
1653 | ||
1654 | ||
1655 | ||
1656 | ||
1657 | ||
1658 | static tpaddr_t ss_pmem_op( | |
1659 | domain_t * domainp, | |
1660 | ss_proc_t * procp, | |
1661 | sparcv9_cpu_t * strandp, | |
1662 | dbgr_mem_op_t memop, | |
1663 | tpaddr_t pa, | |
1664 | uint8_t * bufp, | |
1665 | tpaddr_t size ) | |
1666 | { | |
1667 | config_addr_t *config_addrp; | |
1668 | tpaddr_t offset; | |
1669 | tpaddr_t extent; | |
1670 | uint8_t * cbp; | |
1671 | ||
1672 | /* | |
1673 | * Find the actual domain device | |
1674 | */ | |
1675 | ||
1676 | config_addrp = find_domain_address(domainp, pa); | |
1677 | if (config_addrp == NULL) return false; | |
1678 | ||
1679 | /* | |
1680 | * For now only handle cacheable device memory | |
1681 | */ | |
1682 | extent = config_addrp->config_devp->dev_typep->dev_cacheable(config_addrp, DA_Other, | |
1683 | pa-config_addrp->baseaddr, &cbp); | |
1684 | if (extent == 0) return 0; | |
1685 | ||
1686 | if (size > extent) size = extent; | |
1687 | ||
1688 | ||
1689 | /* | |
1690 | * Handle all the appropriate niagaraness here for the cache | |
1691 | * models we have : FIXME | |
1692 | */ | |
1693 | ||
1694 | /* operate on the cacheable state */ | |
1695 | switch (memop) { | |
1696 | case NA_mem_read: | |
1697 | memcpy( bufp, cbp, size); | |
1698 | break; | |
1699 | ||
1700 | case NA_mem_write: | |
1701 | memcpy( cbp, bufp, size); | |
1702 | break; | |
1703 | ||
1704 | case NA_mem_clear: | |
1705 | memset( cbp, 0, size); | |
1706 | break; | |
1707 | } | |
1708 | ||
1709 | return size; | |
1710 | } | |
1711 | ||
1712 | ||
1713 | ||
1714 | /* | |
1715 | *------------------------------------------------------------ | |
1716 | * Breakpoint support | |
1717 | *------------------------------------------------------------ | |
1718 | */ | |
1719 | ||
1720 | void ss_dbgr_set_break(void * ptr, tvaddr_t bpaddr) | |
1721 | { | |
1722 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1723 | sparcv9_cpu_t * sp = ndp->strandp; | |
1724 | ||
1725 | /* Check for any SunSPARC specific regs first */ | |
1726 | ||
1727 | sparcv9_set_break(sp, bpaddr); | |
1728 | } | |
1729 | ||
1730 | ||
1731 | ||
1732 | void ss_dbgr_clear_break(void * ptr, tvaddr_t bpaddr) | |
1733 | { | |
1734 | ss_dbgr_t * ndp = (ss_dbgr_t *)ptr; | |
1735 | sparcv9_cpu_t * sp = ndp->strandp; | |
1736 | ||
1737 | /* Check for any SunSPARC specific regs first */ | |
1738 | ||
1739 | sparcv9_clear_break(sp, bpaddr); | |
1740 | } | |
1741 | ||
1742 | ||
1743 | ||
1744 | ||
1745 | ||
1746 | ||
1747 | ||
1748 | ||
1749 | ||
1750 | ||
1751 | ||
1752 | ||
1753 | /*------------------------------------------------------------* | |
1754 | * | |
1755 | * Interrupt support | |
1756 | * | |
1757 | *------------------------------------------------------------*/ | |
1758 | ||
1759 | /* | |
1760 | * Typically called after any processor state change, | |
1761 | * or interrupt operation. | |
1762 | * The function will post and call attention to any pending | |
1763 | * device interrupts that may be legitimately delivered | |
1764 | * given the current processor state. | |
1765 | * | |
1766 | * Note that a trap is not invoked here - just the indication | |
1767 | * that the simcpu should pay attention for one | |
1768 | */ | |
1769 | ||
1770 | void ss_check_interrupts(simcpu_t *sp) | |
1771 | { | |
1772 | sparcv9_cpu_t * v9p; | |
1773 | ss_strand_t * nsp; | |
1774 | ||
1775 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
1776 | nsp = v9p->impl_specificp; | |
1777 | ||
1778 | /* OK - if interrupts enabled, can at least trigger timer interrupts */ | |
1779 | ||
1780 | /* FIXME: These trap posts should be in the trap management code */ | |
1781 | /* where priorities are correctly assigned */ | |
1782 | ||
1783 | /* NOTE: | |
1784 | * Do not put returns in this function .. all possible traps should be | |
1785 | * posted. The post_precise_trap function correctly determines priority. | |
1786 | * Also, just because a trap is posted here not not mean that that will be the | |
1787 | * next trap taken. | |
1788 | */ | |
1789 | ||
1790 | switch (v9p->state) { | |
1791 | case V9_User: | |
1792 | case V9_Priv: | |
1793 | /* hstick traps cant be blocked except at hpriv level */ | |
1794 | if (v9p->hstick_cmpr.pending) { | |
1795 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_hstick_match); | |
1796 | } | |
1797 | ||
1798 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
1799 | /* Device interrupts to hypervisor are not blockable in user/priv mode */ | |
1800 | /* vector should not need a lock for test ... */ | |
1801 | if (nsp->irq_vector) { | |
1802 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_interrupt_vector_trap); | |
1803 | } | |
1804 | #endif /* } */ | |
1805 | ||
1806 | #ifdef ROCK /* { */ | |
1807 | /* | |
1808 | * these interrupts are non-maskable in user/priv mode | |
1809 | */ | |
1810 | if (nsp->flag_queue_irq[RK_Q_hpriv_0]) { | |
1811 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_0); | |
1812 | } | |
1813 | if (nsp->flag_queue_irq[RK_Q_hpriv_1]) { | |
1814 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_1); | |
1815 | } | |
1816 | #endif /* } */ | |
1817 | ||
1818 | if (v9p->pstate.int_enabled) { | |
1819 | ||
1820 | if (nsp->flag_queue_irq[NA_Q_mondo]) { | |
1821 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_cpu_mondo_trap); | |
1822 | } | |
1823 | if (nsp->flag_queue_irq[NA_Q_device]) { | |
1824 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_dev_mondo_trap); | |
1825 | } | |
1826 | if (nsp->flag_queue_irq[NA_Q_resumable]) { | |
1827 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_resumable_error); | |
1828 | } | |
1829 | /* | |
1830 | * non-resumable error queue is not checked by HW on niagara | |
1831 | * only delivered by hypervisor | |
1832 | */ | |
1833 | ||
1834 | if ((v9p->stick_cmpr.pending || v9p->tick_cmpr.pending) && (v9p->pil < 14)) { | |
1835 | v9p->post_precise_trap(sp, Sparcv9_trap_interrupt_level_e); | |
1836 | } | |
1837 | if ((v9p->softint >> (v9p->pil +1)) != 0) { | |
1838 | uint_t i; | |
1839 | for (i=15; i > v9p->pil; i--) { | |
1840 | if ((v9p->softint>>i)&1LL) { | |
1841 | v9p->post_precise_trap(sp, Sparcv9_trap_interrupt_level_1+i-1); | |
1842 | goto softint_caught; | |
1843 | } | |
1844 | } | |
1845 | fatal("Internal error: softint triggered, but not found (softint=0x%llx, pil=0x%x)", | |
1846 | (uint64_t)v9p->softint, v9p->pil); | |
1847 | softint_caught:; | |
1848 | } | |
1849 | } | |
1850 | break; | |
1851 | ||
1852 | case V9_HyperPriv: | |
1853 | /* All disrupting traps are blockable in hpriv mode */ | |
1854 | if (v9p->pstate.int_enabled) { | |
1855 | #ifdef ROCK /* { */ | |
1856 | if (nsp->flag_queue_irq[RK_Q_hpriv_0]) { | |
1857 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_0); | |
1858 | } | |
1859 | if (nsp->flag_queue_irq[RK_Q_hpriv_1]) { | |
1860 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)RK_trap_hyperprivileged_queue_1); | |
1861 | } | |
1862 | #endif /* } */ | |
1863 | ||
1864 | if (v9p->hstick_cmpr.pending) { | |
1865 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_hstick_match); | |
1866 | } | |
1867 | ||
1868 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
1869 | if (nsp->irq_vector) { | |
1870 | v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_interrupt_vector_trap); | |
1871 | } | |
1872 | #endif /* } */ | |
1873 | } | |
1874 | break; | |
1875 | ||
1876 | case V9_RED: | |
1877 | if (v9p->pstate.int_enabled) FIXME_WARNING(("Interrupts enabled in RED state not implemented!")); | |
1878 | break; | |
1879 | ||
1880 | default: | |
1881 | ASSERT(0); /* must be in user, priv or H priv states here */ | |
1882 | } | |
1883 | ||
1884 | #if ERROR_TRAP_GEN /* { */ | |
1885 | ss_check_error_traps(sp); | |
1886 | #endif /* } ERROR_TRAP_GEN */ | |
1887 | ||
1888 | } | |
1889 | ||
1890 | ||
1891 | ||
1892 | ||
1893 | ||
1894 | /* | |
1895 | * Very similar to check_interrupts, but only ever called | |
1896 | * from the main execution loop when attention is called, | |
1897 | * and the async_event flag is set. | |
1898 | * This routine is responsible for posting a trap if an | |
1899 | * externally generated (i.e. not by this executing thread) | |
1900 | * asynchronous event has happened, and as a result the | |
1901 | * changed cpu state results in a trap ... | |
1902 | * .... however, we do not call post_precise_trap, since | |
1903 | * this may not be precise, but *especially* since that function | |
1904 | * causes a set_attention ... and we're already responding to that. | |
1905 | * Instead, if a trap is necessary we deliberately just insert the trap | |
1906 | * type into the pending_async_tt and set take_exception instead. | |
1907 | * | |
1908 | * By async, we mean asynchonous to the simcpu execution thread ... | |
1909 | */ | |
1910 | ||
1911 | ||
1912 | void ss_check_async_event(simcpu_t *sp) | |
1913 | { | |
1914 | sp->async_event = false; | |
1915 | ss_check_interrupts(sp); | |
1916 | } | |
1917 | ||
1918 | ||
1919 | /* | |
1920 | * This is the temporary callback function for when we get a cycle count | |
1921 | * match. | |
1922 | * The idea is that one of our cycle counters has reached its target value | |
1923 | * and so we go off to set the appropriate interrupt flags if enabled, and | |
1924 | * then reschedule the next target interrupt. | |
1925 | */ | |
1926 | /* | |
1927 | * Since the cycle_base's count at different rates, the match | |
1928 | * is (now >= target) && !triggered. triggered is cleared | |
1929 | * when *tick_cmpr is written and set on reset. | |
1930 | */ | |
1931 | ||
1932 | static void ss_cycle_target_match(simcpu_t *sp) | |
1933 | { | |
1934 | ss_strand_t * nsp; | |
1935 | ss_proc_t * npp; | |
1936 | sparcv9_cpu_t * v9p; | |
1937 | simcycle_t now; | |
1938 | ticktarg_t now_targ; | |
1939 | error_conf_t * ep; | |
1940 | bool_t chk_intr = false; | |
1941 | ||
1942 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
1943 | nsp = v9p->impl_specificp; | |
1944 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
1945 | ||
1946 | now = sp->cycle; | |
1947 | ||
1948 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
1949 | /* | |
1950 | * Check for Performance Counter match | |
1951 | */ | |
1952 | if (nsp->pcr & SS_PCR_UT_ST) { | |
1953 | uint32_t old_pic0, old_pic1; | |
1954 | uint64_t old_pcr; | |
1955 | ||
1956 | old_pic0 = nsp->pic0; | |
1957 | old_pic1 = nsp->pic1; | |
1958 | UPDATE_PIC(sp, nsp, v9p); | |
1959 | old_pcr = nsp->pcr; | |
1960 | /* approximate overflow detection by testing for PIC decrease */ | |
1961 | if (nsp->pic0 < old_pic0) { | |
1962 | DBGPIC( lprintf(sp->gid, "Performance Counter Overflow pic0 @ cycle=0x%llx," \ | |
1963 | " pic0=0x%x, pic1=0x%x\n", | |
1964 | now, nsp->pic0, nsp->pic1); ); | |
1965 | nsp->pcr |= SS_PCR_OV0; /* set OVFL */ | |
1966 | } | |
1967 | if (nsp->pic1 < old_pic1) { | |
1968 | DBGPIC( lprintf(sp->gid, "Performance Counter Overflow pic1 @ cycle=0x%llx," \ | |
1969 | " pic0=0x%x, pic1=0x%x\n", | |
1970 | now, nsp->pic0, nsp->pic1); ); | |
1971 | nsp->pcr |= SS_PCR_OV1; /* set OVFH */ | |
1972 | } | |
1973 | if (nsp->pcr != old_pcr) { | |
1974 | v9p->softint |= BIT(15); | |
1975 | DBGSOFTINT( lprintf(sp->gid, | |
1976 | "softint pic overflow: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n", | |
1977 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending, | |
1978 | v9p->tick_cmpr.pending); ); | |
1979 | chk_intr = true; | |
1980 | } | |
1981 | } | |
1982 | #endif /* } */ | |
1983 | ||
1984 | ||
1985 | #if ERROR_TRAP_GEN /* { */ | |
1986 | if (now >= sp->error_cycle) { | |
1987 | lprintf(sp->gid, "ERROR_TRAP_GEN: cycle target matched [0x%llx] at "\ | |
1988 | "instn_cnt 0x%llx\n", sp->error_cycle, now); | |
1989 | sp->error_cycle = UINT64_MAX; | |
1990 | sp->error_cycle_reached = true; | |
1991 | } | |
1992 | #endif /* ERROR_TRAP_GEN } */ | |
1993 | ||
1994 | ||
1995 | #if WALL_TIME | |
1996 | if (options.walltime) | |
1997 | now_targ = RAW_HRTIME; | |
1998 | else | |
1999 | #endif /* WALL_TIME */ | |
2000 | now_targ = now; | |
2001 | if (!v9p->tick_cmpr.triggered && now_targ >= v9p->tick_cmpr.target) { | |
2002 | ASSERT( v9p->tick_cmpr.interrupt_enabled ); | |
2003 | v9p->tick_cmpr.triggered = true; | |
2004 | v9p->tick_cmpr.pending = true; | |
2005 | chk_intr = true; | |
2006 | DBGSOFTINT( lprintf(sp->gid, | |
2007 | "softint update: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n", | |
2008 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending, | |
2009 | v9p->tick_cmpr.pending); ); | |
2010 | } | |
2011 | ||
2012 | if (!v9p->stick_cmpr.triggered && now_targ >= v9p->stick_cmpr.target) { | |
2013 | ASSERT( v9p->stick_cmpr.interrupt_enabled ); | |
2014 | v9p->stick_cmpr.triggered = true; | |
2015 | v9p->stick_cmpr.pending = true; | |
2016 | chk_intr = true; | |
2017 | DBGSOFTINT( lprintf(sp->gid, | |
2018 | "softint update: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n", | |
2019 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending, | |
2020 | v9p->tick_cmpr.pending); ); | |
2021 | } | |
2022 | ||
2023 | if (!v9p->hstick_cmpr.triggered && now_targ >= v9p->hstick_cmpr.target) { | |
2024 | ASSERT( v9p->hstick_cmpr.interrupt_enabled ); | |
2025 | v9p->hstick_cmpr.triggered = true; | |
2026 | v9p->hstick_cmpr.pending = true; | |
2027 | chk_intr = true; | |
2028 | DBGSOFTINT( lprintf(sp->gid, | |
2029 | "hintp update: pc=0x%llx, o7=0x%llx, hintp=%u\n", | |
2030 | sp->pc, sp->intreg[Reg_sparcv9_o7], | |
2031 | v9p->hstick_cmpr.pending); ); | |
2032 | } | |
2033 | ||
2034 | if ((now >= options.save_restore.trigger_icount) && | |
2035 | (options.save_restore.trigger_icount != 0)) { | |
2036 | uint64_t c; | |
2037 | ||
2038 | /* | |
2039 | * Any cpu can hit this trigger point but we only | |
2040 | * want one cpu to send the SAVE_STATE trap to all | |
2041 | * strands in all processors. | |
2042 | */ | |
2043 | c = host_cas64(&options.save_restore.trigger_icount, | |
2044 | options.save_restore.trigger_icount, 0); | |
2045 | ||
2046 | if (c != 0) { | |
2047 | int i; | |
2048 | domain_t *dp; | |
2049 | ||
2050 | lprintf(sp->gid, | |
2051 | "options.save_restore.trigger_icount reached. trigger=0x%llx, icount=0x%llx\n", | |
2052 | c, now); | |
2053 | ||
2054 | dp = sp->config_procp->domainp; | |
2055 | for (i=0; i<dp->procs.count; i++) { | |
2056 | config_proc_t * cp; | |
2057 | ||
2058 | cp = LIST_ENTRY(dp->procs, i); | |
2059 | cp->proc_typep->ext_signal(cp, ES_LEGION_SAVE_STATE, NULL); | |
2060 | } | |
2061 | } | |
2062 | } | |
2063 | ||
2064 | #if ERROR_INJECTION /* { */ | |
2065 | /* | |
2066 | * Turn on the error_enabled flag to begin checking for error | |
2067 | * conditions. Flush caches so all subsequent instruction | |
2068 | * fetches and ld/st operations are detected by the error handling | |
2069 | */ | |
2070 | ep = NULL; | |
2071 | pthread_mutex_lock(&npp->err_lock); | |
2072 | if (npp->pend_errlistp && now == npp->pend_errlistp->cycle) { | |
2073 | DBGERR( lprintf(sp->gid, "ss_cycle_target_match(): error cycle\n"); ); | |
2074 | ||
2075 | while (npp->pend_errlistp && now == npp->pend_errlistp->cycle) { | |
2076 | ep = npp->pend_errlistp; | |
2077 | npp->pend_errlistp = ep->nextp; | |
2078 | if (sp->errorp->errlistp) | |
2079 | ep->nextp = sp->errorp->errlistp; | |
2080 | else | |
2081 | ep->nextp = NULL; | |
2082 | sp->errorp->errlistp = ep; | |
2083 | } | |
2084 | } | |
2085 | pthread_mutex_unlock(&npp->err_lock); | |
2086 | ||
2087 | if (ep) { | |
2088 | sp->error_enabled = true; | |
2089 | sp->error_check = (v9p->state == sp->error_priv) ? true : false; | |
2090 | update_errflags(sp); | |
2091 | ||
2092 | DBGERR( dump_errconf(sp->errorp->errlistp); ); | |
2093 | DBGERR( lprintf(sp->gid, "check_xicache = %u check_xdcache = %u\n", | |
2094 | sp->errorp->check_xicache, sp->errorp->check_xdcache); ); | |
2095 | ||
2096 | sp->xicache_trans_flush_pending = true; | |
2097 | sp->xdcache_trans_flush_pending = true; | |
2098 | } | |
2099 | #endif /* ERROR_INJECTION } */ | |
2100 | ||
2101 | #if PERFORMANCE_CHECK /* { */ | |
2102 | if (now >= sp->perf_target) { | |
2103 | hrtime_t hrt, gap; | |
2104 | double mips, amips; | |
2105 | ||
2106 | hrt = gethrtime(); | |
2107 | ||
2108 | gap = hrt - sp->last_hrtime; | |
2109 | sp->total_hrtime += gap; | |
2110 | ||
2111 | mips = (1.0e3*(double)(ICOUNT(sp) - sp->prev_icount)) / (double)gap; | |
2112 | amips =(1.0e3*(double)ICOUNT(sp)) / (double)sp->total_hrtime; | |
2113 | log_lock(); | |
2114 | log_printf(sp->gid, "Simulator mips=%.2lf, average mips=%.2lf, "\ | |
2115 | "total_time=%llu seconds, delta_time=%llu milliseconds\n", | |
2116 | mips, amips, HRT_TO_SEC(sp->total_hrtime), HRT_TO_MILLISEC(gap)); | |
2117 | ||
2118 | sp->config_procp->proc_typep->perf_dump(sp->specificp); | |
2119 | ||
2120 | log_flush_unlock(); | |
2121 | ||
2122 | /* Don't count the time spent printing... */ | |
2123 | sp->last_hrtime = gethrtime(); | |
2124 | ||
2125 | /* | |
2126 | * exit_at is parsed from the conf file. | |
2127 | * If it's non-zero, we exit the simulator when we reach or | |
2128 | * exceed that number of instns | |
2129 | */ | |
2130 | if ((npp->proc_debug.exit_at != 0) && | |
2131 | (ICOUNT(sp) >= npp->proc_debug.exit_at)) { | |
2132 | fatal("Reached the value for exit_at in conf file " | |
2133 | "0x%llx, current icount=llu\n", | |
2134 | npp->proc_debug.exit_at, ICOUNT(sp)); | |
2135 | } | |
2136 | ||
2137 | #if WALL_TIME | |
2138 | if (options.walltime) { | |
2139 | sp->perf_target = now + npp->proc_debug.perf_cycle_gap; | |
2140 | ASSERT(sp->perf_target > sp->cycle); | |
2141 | } else | |
2142 | #endif /* WALL_TIME */ | |
2143 | sp->perf_target += npp->proc_debug.perf_cycle_gap; | |
2144 | } | |
2145 | #endif /* PERFORMANCE_CHECK } */ | |
2146 | ||
2147 | ss_recomp_cycle_target(sp); | |
2148 | if (chk_intr) | |
2149 | ss_check_interrupts(sp); | |
2150 | } | |
2151 | ||
2152 | ||
2153 | ||
2154 | ||
2155 | /*------------------------------------------------------------* | |
2156 | * | |
2157 | * Privileged and status registers accessed here ... | |
2158 | * | |
2159 | *------------------------------------------------------------*/ | |
2160 | ||
2161 | ||
2162 | ||
2163 | uint64_t ss_read_pstate(sparcv9_cpu_t * v9p) | |
2164 | { | |
2165 | uint64_t pstate; | |
2166 | ||
2167 | pstate = v9p->pstate.priv ? (1<<V9_PSTATE_PRIV_BIT) : 0; | |
2168 | pstate |= v9p->pstate.mm << V9_PSTATE_MM_BITS; | |
2169 | pstate |= v9p->pstate.int_enabled ? (1<< V9_PSTATE_IE_BIT) : 0; | |
2170 | pstate |= v9p->pstate.addr_mask ? (1<< V9_PSTATE_AM_BIT) : 0; | |
2171 | pstate |= v9p->pstate.fpu_enabled ? (1<< V9_PSTATE_PEF_BIT) : 0; | |
2172 | pstate |= v9p->pstate.tle ? (1<< V9_PSTATE_TLE_BIT) : 0; | |
2173 | pstate |= v9p->pstate.cle ? (1<< V9_PSTATE_CLE_BIT) : 0; | |
2174 | pstate |= v9p->pstate.tct ? (1<< V9_PSTATE_TCT_BIT) : 0; | |
2175 | ||
2176 | return pstate; | |
2177 | } | |
2178 | ||
2179 | ||
2180 | static uint64_t ss_read_hpstate(sparcv9_cpu_t * v9p) | |
2181 | { | |
2182 | uint64_t hpstate; | |
2183 | ||
2184 | hpstate = v9p->hpstate.hpriv ? (1<<V9_HPSTATE_HPRIV_BIT) : 0; | |
2185 | hpstate |= v9p->hpstate.tlz ? (1<<V9_HPSTATE_TLZ_BIT) : 0; | |
2186 | hpstate |= v9p->hpstate.red ? (1<<V9_HPSTATE_RED_BIT) : 0; | |
2187 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2188 | hpstate |= v9p->hpstate.ibe ? (1<<V9_HPSTATE_IBE_BIT) : 0; | |
2189 | #endif /* } */ | |
2190 | ||
2191 | #if defined(NIAGARA1) | |
2192 | hpstate |= (1LL<<NA_HPSTATE_ENB_BIT); | |
2193 | #elif NIAGARA2 | |
2194 | /* Nothing extra for NIAGARA2 */ | |
2195 | #elif ROCK | |
2196 | { | |
2197 | ss_strand_t * ssr = (ss_strand_t *)v9p->impl_specificp; | |
2198 | hpstate |= ssr->tick_npt ? (1LL<<HPSTATE_TNPT_BIT) : 0; | |
2199 | } | |
2200 | #else | |
2201 | #error "No valid processor defined." | |
2202 | #endif | |
2203 | ||
2204 | return hpstate; | |
2205 | } | |
2206 | ||
2207 | ||
2208 | /* | |
2209 | * This function currently gets called by the done/retry | |
2210 | * implementations as well as the wrpr instruction ... | |
2211 | * ... we may want to settle on a distinct & more | |
2212 | * efficient combined version for done/rety that | |
2213 | * covers both pstate and hpstate -- FIXME | |
2214 | */ | |
2215 | ||
2216 | static void ss_write_pstate(simcpu_t * sp, uint64_t val) | |
2217 | { | |
2218 | sparcv9_cpu_t * v9p; | |
2219 | bool_t was_enabled; | |
2220 | ||
2221 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2222 | ||
2223 | v9p->pstate.tle = BIT_TEST( val, V9_PSTATE_TLE_BIT ) ? true : false; | |
2224 | ||
2225 | was_enabled = v9p->pstate.cle; | |
2226 | v9p->pstate.cle = BIT_TEST( val, V9_PSTATE_CLE_BIT ) ? true : false; | |
2227 | if (was_enabled != v9p->pstate.cle) | |
2228 | sp->xdcache_trans_flush_pending = true; | |
2229 | ||
2230 | was_enabled = v9p->fpu_on; | |
2231 | v9p->pstate.fpu_enabled = BIT_TEST( val, V9_PSTATE_PEF_BIT ) ? true : false; | |
2232 | v9p->fpu_on = v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu; | |
2233 | #ifdef FP_DECODE_DISABLED | |
2234 | /* flush instn cache to force new decodes - thus can trap on FP instructions */ | |
2235 | if ( v9p->fpu_on != was_enabled) { | |
2236 | sp->xicache_instn_flush_pending = true; | |
2237 | set_sync_pending(sp); | |
2238 | } | |
2239 | #endif /* FP_DECODE_DISABLED */ | |
2240 | ||
2241 | #ifdef ROCK | |
2242 | v9p->pstate.mm = (val >> V9_PSTATE_MM_BITS) & V9_PSTATE_MM_MASK; /* Ignore effects */ | |
2243 | #endif | |
2244 | ||
2245 | v9p->pstate.addr_mask = BIT_TEST( val, V9_PSTATE_AM_BIT ) ? true : false; | |
2246 | V9_PSTATE_AM_CHANGED(v9p); | |
2247 | ||
2248 | v9p->pstate.int_enabled = BIT_TEST( val, V9_PSTATE_IE_BIT ) ? true : false; | |
2249 | /* Have potentially just enabled a whole bunch of disrupting traps ... check them here */ | |
2250 | /* - handled by check interrupts call below */ | |
2251 | ||
2252 | v9p->pstate.tct = BIT_TEST( val, V9_PSTATE_TCT_BIT ) ? true : false; | |
2253 | if ( v9p->pstate.tct ) IMPL_WARNING(("wr %%pstate: TCT=1 @ pc=0x%llx - TCT not supported", sp->pc)); | |
2254 | ||
2255 | was_enabled = v9p->pstate.priv; | |
2256 | v9p->pstate.priv = BIT_TEST( val, V9_PSTATE_PRIV_BIT ) ? true : false; | |
2257 | if (was_enabled != v9p->pstate.priv) { | |
2258 | /* Change of execution state ? */ | |
2259 | if (v9p->state!=V9_HyperPriv && v9p->state!=V9_RED) { | |
2260 | V9_STATE_CHANGE(sp, v9p, (v9p->pstate.priv ? V9_Priv : V9_User)); | |
2261 | } | |
2262 | } | |
2263 | ||
2264 | /* V9_PSTATE_CHANGED(v9p); */ | |
2265 | ||
2266 | ss_check_interrupts(sp); /* because of state change */ | |
2267 | ||
2268 | /* | |
2269 | * Note: if interrupts were enabled, it is the next instructions | |
2270 | * that would be observed as taking the trap (so we dont re-execute | |
2271 | * the wrpr %pstate again after the return from trap. | |
2272 | */ | |
2273 | } | |
2274 | ||
2275 | ||
2276 | ||
2277 | ||
2278 | /* | |
2279 | * This function currently gets called by the done/retry | |
2280 | * implementations as well as the wrpr instruction ... | |
2281 | * ... we may want to settle on a distinct & more | |
2282 | * efficient combined version for done/rety that | |
2283 | * covers both pstate and hpstate -- FIXME | |
2284 | */ | |
2285 | ||
2286 | static void ss_write_hpstate(simcpu_t * sp, bool_t from_htstate, uint64_t val) | |
2287 | { | |
2288 | uint64_t hpstate; | |
2289 | sparcv9_cpu_t * v9p; | |
2290 | bool_t flag_icflush = false; | |
2291 | bool_t flag_dcflush = false; | |
2292 | bool_t was_enabled; | |
2293 | sparcv9_state_t new_state; | |
2294 | ||
2295 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2296 | ||
2297 | v9p->hpstate.hpriv = BIT_TEST( val, V9_HPSTATE_HPRIV_BIT ) ? true : false; | |
2298 | v9p->hpstate.tlz = BIT_TEST( val, V9_HPSTATE_TLZ_BIT ) ? true : false; | |
2299 | v9p->hpstate.red = BIT_TEST( val, V9_HPSTATE_RED_BIT ) ? true : false; | |
2300 | ||
2301 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2302 | v9p->hpstate.ibe = BIT_TEST( val, V9_HPSTATE_IBE_BIT ) ? true : false; | |
2303 | ||
2304 | if (v9p->hpstate.ibe) | |
2305 | IMPL_WARNING(("HPSTATE.IBE not implemented (pc=0x%llx)", sp->pc)); | |
2306 | #endif /* } */ | |
2307 | ||
2308 | if (v9p->hpstate.tlz) | |
2309 | IMPL_WARNING(("HPSTATE.TLZ not implemented (pc=0x%llx)", sp->pc)); | |
2310 | ||
2311 | #if defined(NIAGARA1) | |
2312 | ||
2313 | if (!from_htstate && !BIT_TEST(val, NA_HPSTATE_ENB_BIT)) | |
2314 | IMPL_WARNING(("ENB bit in hpstate cleared by executing code, but not implemented by legion (pc=0x%llx)", sp->pc)); | |
2315 | ||
2316 | #elif NIAGARA2 | |
2317 | /* nothing to do */ | |
2318 | #elif ROCK | |
2319 | if (1 /* !from_htstate ?? */) { | |
2320 | ss_strand_t * ssr = (ss_strand_t *)v9p->impl_specificp; | |
2321 | ssr->tick_npt = BIT_TEST( val, HPSTATE_TNPT_BIT ) ? true : false; | |
2322 | v9p->_tick.non_priv_trap = ssr->tick_npt; | |
2323 | v9p->_stick.non_priv_trap = ssr->tick_npt; | |
2324 | } | |
2325 | #else | |
2326 | #error "No valid processor defined." | |
2327 | #endif | |
2328 | ||
2329 | if (v9p->hpstate.red) { | |
2330 | new_state = V9_RED; | |
2331 | } else | |
2332 | if (v9p->hpstate.hpriv) { | |
2333 | new_state = V9_HyperPriv; | |
2334 | } else { | |
2335 | new_state = v9p->pstate.priv ? V9_Priv : V9_User; | |
2336 | } | |
2337 | ||
2338 | if (v9p->state != new_state) { | |
2339 | /* Have potentially just enabled a whole bunch of disrupting traps ... check them here */ | |
2340 | /* - handled by check interrupts call below */ | |
2341 | ||
2342 | V9_STATE_CHANGE(sp, v9p, new_state); | |
2343 | ||
2344 | ss_check_interrupts(sp); /* because of state change */ | |
2345 | } | |
2346 | } | |
2347 | ||
2348 | ||
2349 | ||
2350 | ||
2351 | ||
2352 | ||
2353 | /* tick is read as a pr and asr, so make code common here */ | |
2354 | ||
2355 | uint64_t ss_read_tick(simcpu_t * sp) | |
2356 | { | |
2357 | sparcv9_cpu_t * v9p; | |
2358 | uint64_t val; | |
2359 | ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp; | |
2360 | ||
2361 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2362 | val = v9p->tick->offset + RAW_TICK(v9p); | |
2363 | ||
2364 | #ifdef NIAGARA1 | |
2365 | val &= MASK64(62, 2); /* Low bits are clear on Niagara */ | |
2366 | #endif | |
2367 | ||
2368 | #ifdef NIAGARA2 /* { */ | |
2369 | if (npp->tick_stop) | |
2370 | val = v9p->tick->offset; | |
2371 | #endif /* } */ | |
2372 | ||
2373 | return val; | |
2374 | } | |
2375 | ||
2376 | uint64_t ss_read_stick(simcpu_t * sp) | |
2377 | { | |
2378 | sparcv9_cpu_t * v9p; | |
2379 | uint64_t val; | |
2380 | ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp; | |
2381 | ||
2382 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2383 | val = v9p->stick->offset + RAW_STICK(v9p); | |
2384 | ||
2385 | #ifdef NIAGARA1 | |
2386 | val &= MASK64(62, 2); /* Low bits are clear on Niagara */ | |
2387 | #endif | |
2388 | ||
2389 | #ifdef NIAGARA2 /* { */ | |
2390 | if (npp->tick_stop) | |
2391 | val = v9p->stick->offset; | |
2392 | #endif /* } */ | |
2393 | ||
2394 | return val; | |
2395 | } | |
2396 | ||
2397 | ||
2398 | #if WALL_TIME | |
2399 | #define CALC_TARG(_tcmp) (options.walltime ? \ | |
2400 | (TICK_TO_HRT((_tcmp)->compare - (_tcmp)->counter->offset, \ | |
2401 | (_tcmp)->counter->scale_recip)) : \ | |
2402 | ((_tcmp)->compare - (_tcmp)->counter->offset)) | |
2403 | #else /* WALL_TIME */ | |
2404 | #define CALC_TARG(_tcmp) ((_tcmp)->compare - \ | |
2405 | (_tcmp)->counter->offset) | |
2406 | #endif /* WALL_TIME */ | |
2407 | ||
2408 | #define RECALC_TARG(_sp, _tcmp) \ | |
2409 | (_tcmp)->target = CALC_TARG(_tcmp); \ | |
2410 | (_tcmp)->triggered = ((_tcmp)->target <= (_sp)->cycle) | |
2411 | ||
2412 | uint64_t ss_tick_cmpr_read(simcpu_t * sp, tick_compare_t *tcmp) | |
2413 | { | |
2414 | return (tcmp->interrupt_enabled ? 0ULL : (1ULL << 63)) | | |
2415 | tcmp->compare; | |
2416 | } | |
2417 | ||
2418 | void ss_tick_cmpr_write(simcpu_t * sp, tick_compare_t *tcmp, uint64_t val) | |
2419 | { | |
2420 | ss_proc_t * npp; | |
2421 | ||
2422 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
2423 | ||
2424 | tcmp->compare = val & MASK64(62,0); | |
2425 | tcmp->interrupt_enabled = (val>>63) ? false : true; | |
2426 | if (tcmp->interrupt_enabled) { | |
2427 | #ifdef NIAGARA2 | |
2428 | if (!npp->tick_stop) { | |
2429 | #endif | |
2430 | RECALC_TARG(sp, tcmp); | |
2431 | #ifdef NIAGARA2 /* { */ | |
2432 | } else { | |
2433 | /* | |
2434 | * If tick is stopped, no need to update tick target | |
2435 | * values as only sp->cycle is increasing, but | |
2436 | * tick is stationary, so we will not hit the | |
2437 | * target. The only exception to this is if, | |
2438 | * while the tick is stopped, either the tick | |
2439 | * counter or the tick_cmpr.compare is written | |
2440 | * to such that they are both equal. Then we | |
2441 | * need to have an interrupt happen right away. | |
2442 | */ | |
2443 | if (tcmp->compare == tcmp->counter->offset) | |
2444 | tcmp->target = sp->cycle + 1; | |
2445 | else | |
2446 | tcmp->triggered = true; | |
2447 | tcmp->target = 0ull; | |
2448 | } | |
2449 | #endif /* } */ | |
2450 | } else { | |
2451 | /* "Don't do match compare" */ | |
2452 | tcmp->triggered = true; | |
2453 | tcmp->target = 0; | |
2454 | } | |
2455 | } | |
2456 | ||
2457 | ||
2458 | /* | |
2459 | * Account for TICK/STICK offset changes. | |
2460 | * TODO: must apply to all cmprs sharing the specific TICK/STICK. | |
2461 | */ | |
2462 | void ss_recomp_tick_target(simcpu_t * sp) | |
2463 | { | |
2464 | sparcv9_cpu_t * v9p; | |
2465 | ss_proc_t * npp; | |
2466 | ||
2467 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
2468 | ||
2469 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2470 | ||
2471 | #ifdef NIAGARA2 | |
2472 | if (!npp->tick_stop) { | |
2473 | #endif | |
2474 | ||
2475 | if (v9p->tick_cmpr.interrupt_enabled) { | |
2476 | RECALC_TARG(sp, &v9p->tick_cmpr); | |
2477 | } | |
2478 | if (v9p->stick_cmpr.interrupt_enabled) { | |
2479 | RECALC_TARG(sp, &v9p->stick_cmpr); | |
2480 | } | |
2481 | if (v9p->hstick_cmpr.interrupt_enabled) { | |
2482 | RECALC_TARG(sp, &v9p->hstick_cmpr); | |
2483 | } | |
2484 | #ifdef NIAGARA2 /* { */ | |
2485 | } else { | |
2486 | /* | |
2487 | * If tick is stopped, no need to update tick target | |
2488 | * values as only sp->cycle is increasing, but | |
2489 | * tick is stationary, so we will not hit the | |
2490 | * target. The only exception to this is if, | |
2491 | * while the tick is stopped, either the tick | |
2492 | * counter or the tick_cmpr.compare is written | |
2493 | * to such that they are both equal. Then we | |
2494 | * need to have an interrupt happen right away. | |
2495 | */ | |
2496 | if (v9p->tick_cmpr.compare == v9p->tick_cmpr.counter->offset) | |
2497 | v9p->tick_cmpr.target = sp->cycle + 1; | |
2498 | else | |
2499 | v9p->tick_cmpr.target = 0ull; | |
2500 | if (v9p->stick_cmpr.compare == v9p->stick_cmpr.counter->offset) | |
2501 | v9p->stick_cmpr.target = sp->cycle + 1; | |
2502 | else | |
2503 | v9p->stick_cmpr.target = 0ull; | |
2504 | if (v9p->hstick_cmpr.compare == v9p->hstick_cmpr.counter->offset) | |
2505 | v9p->hstick_cmpr.target = sp->cycle + 1; | |
2506 | else | |
2507 | v9p->hstick_cmpr.target = 0ull; | |
2508 | } | |
2509 | #endif /* } */ | |
2510 | ||
2511 | DBGTICKREAD( lprintf(sp->gid, | |
2512 | "recomp_tick_target: pc=0x%llx, cycle=0x%llx, tick_targ=0x%llx, " | |
2513 | "stick_targ=0x%llx hstick_targ=0x%llx\n", | |
2514 | sp->pc, sp->cycle, v9p->tick_cmpr.target, v9p->stick_cmpr.target, | |
2515 | v9p->hstick_cmpr.target); ); | |
2516 | ||
2517 | ss_recomp_cycle_target(sp); | |
2518 | } | |
2519 | ||
2520 | ||
2521 | void ss_recomp_cycle_target(simcpu_t * sp) | |
2522 | { | |
2523 | sparcv9_cpu_t *v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2524 | simcycle_t now; | |
2525 | simcycle_t old_target; | |
2526 | ||
2527 | #if ERROR_INJECTION /* { */ | |
2528 | ss_proc_t *npp; | |
2529 | uint64_t error_cycle; | |
2530 | #endif /* } */ | |
2531 | ||
2532 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2533 | ss_strand_t *nsp = v9p->impl_specificp; | |
2534 | #endif /* } */ | |
2535 | ||
2536 | now = sp->cycle; | |
2537 | old_target = sp->cycle_target; | |
2538 | sp->cycle_target = UINT64_MAX; | |
2539 | ||
2540 | /* For each of the following figure out the target value - assuming an | |
2541 | * interrupt is enabled, then we figure out which is the nearest target | |
2542 | */ | |
2543 | ||
2544 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2545 | /* | |
2546 | * Since the PIC implementation is not exact and the overflow traps | |
2547 | * are disrupting (can be blocked sometimes), just cause a check | |
2548 | * to happen every PIC_OVERFLOW_CHECK_INTERVAL cycles if counting is | |
2549 | * enabled. | |
2550 | */ | |
2551 | #define PIC_OVERFLOW_CHECK_INTERVAL 1000 | |
2552 | if (nsp->pcr & SS_PCR_UT_ST) { | |
2553 | if ((now + PIC_OVERFLOW_CHECK_INTERVAL) < sp->cycle_target) | |
2554 | sp->cycle_target = (now + PIC_OVERFLOW_CHECK_INTERVAL); | |
2555 | } | |
2556 | #endif /* } */ | |
2557 | ||
2558 | /* | |
2559 | * WALL_TIME_TARGET_POLL_HACK can go away once there is a running MIPS | |
2560 | * average available to predict the right cycle_target. The gethrtime() | |
2561 | * function traps to the kernel, so it would be inefficient to do that | |
2562 | * for each instruction executed. | |
2563 | */ | |
2564 | #define WALL_TIME_TARGET_POLL_HACK 1000 | |
2565 | #define CHECK_CMPR_TARGET(_tcmp) \ | |
2566 | if ((_tcmp)->target > now && \ | |
2567 | ((_tcmp)->target < sp->cycle_target)) \ | |
2568 | sp->cycle_target = options.walltime ? (sp->cycle) + WALL_TIME_TARGET_POLL_HACK : (_tcmp)->target | |
2569 | ||
2570 | CHECK_CMPR_TARGET(&v9p->tick_cmpr); | |
2571 | CHECK_CMPR_TARGET(&v9p->stick_cmpr); | |
2572 | CHECK_CMPR_TARGET(&v9p->hstick_cmpr); | |
2573 | ||
2574 | /* Check if save_restore trigger is the next trigger */ | |
2575 | if ((options.save_restore.trigger_icount > now) && | |
2576 | (options.save_restore.trigger_icount < sp->cycle_target)) | |
2577 | sp->cycle_target = options.save_restore.trigger_icount; | |
2578 | ||
2579 | ||
2580 | #if PERFORMANCE_CHECK | |
2581 | if (sp->perf_target < sp->cycle_target) | |
2582 | sp->cycle_target = sp->perf_target; | |
2583 | #endif | |
2584 | ||
2585 | #if ERROR_TRAP_GEN /* { */ | |
2586 | if (sp->error_cycle>now && sp->error_cycle<sp->cycle_target) | |
2587 | sp->cycle_target = sp->error_cycle; | |
2588 | #endif /* ERROR_TRAP_GEN } */ | |
2589 | ||
2590 | #if ERROR_INJECTION /* { */ | |
2591 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
2592 | ||
2593 | pthread_mutex_lock(&npp->err_lock); | |
2594 | if (npp->pend_errlistp) | |
2595 | error_cycle = npp->pend_errlistp->cycle; | |
2596 | else | |
2597 | error_cycle = 0; | |
2598 | pthread_mutex_unlock(&npp->err_lock); | |
2599 | if ((error_cycle > now) && (error_cycle < sp->cycle_target)) { | |
2600 | sp->cycle_target = error_cycle; | |
2601 | } | |
2602 | #endif /* } */ | |
2603 | ||
2604 | if (sp->cycle_target != old_target) { | |
2605 | if (sp->cycle_target != UINT64_MAX) { | |
2606 | ASSERT(sp->cycle_target >= sp->cycle); | |
2607 | sp->exec_loop_reset = true; | |
2608 | if (old_target == UINT64_MAX) { | |
2609 | DBGTICK( lprintf(sp->gid, | |
2610 | "recomp_cycle_target: pc=0x%llx, " | |
2611 | "cycle=0x%llx cycle_target was never\n", | |
2612 | sp->pc, sp->cycle); ); | |
2613 | } | |
2614 | } else { | |
2615 | DBGTICK( lprintf(sp->gid, "recomp_cycle_target: " | |
2616 | "pc=0x%llx, cycle=0x%llx cycle_target set to " | |
2617 | "never\n", sp->pc, sp->cycle); ); | |
2618 | } | |
2619 | } | |
2620 | ||
2621 | DBGTICKREAD( lprintf(sp->gid, | |
2622 | "recomp_cycle_target: pc=0x%llx, cycle=0x%llx, " | |
2623 | "cycle_target-cycle=0x%llx\n", | |
2624 | sp->pc, sp->cycle, sp->cycle_target - sp->cycle); ); | |
2625 | } | |
2626 | ||
2627 | ||
2628 | ||
2629 | ||
2630 | ||
2631 | void ss_write_tick(simcpu_t * sp, uint64_t val) | |
2632 | { | |
2633 | sparcv9_cpu_t * v9p; | |
2634 | ||
2635 | ss_proc_t * npp = (ss_proc_t *)sp->config_procp->procp; | |
2636 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2637 | ||
2638 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2639 | /* NPT bit is per-strand, stick aliases to tick */ | |
2640 | v9p->_tick.non_priv_trap = (val >> 63) ? true : false; | |
2641 | v9p->_stick.non_priv_trap = v9p->_tick.non_priv_trap; | |
2642 | #endif /* } */ | |
2643 | val &= 0x7fffffffffffffffLL; | |
2644 | v9p->tick->offset = val - RAW_TICK(v9p); | |
2645 | ||
2646 | #ifdef NIAGARA2 | |
2647 | if (npp->tick_stop) | |
2648 | v9p->tick->offset = val; | |
2649 | #endif | |
2650 | ss_recomp_tick_target(sp); | |
2651 | } | |
2652 | ||
2653 | void ss_write_stick(simcpu_t * sp, uint64_t val) | |
2654 | { | |
2655 | sparcv9_cpu_t * v9p; | |
2656 | ss_proc_t *npp; | |
2657 | ||
2658 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
2659 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2660 | ||
2661 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2662 | /* NPT bit is per-strand, stick aliases to tick */ | |
2663 | v9p->_tick.non_priv_trap = (val >> 63) ? true : false; | |
2664 | v9p->_stick.non_priv_trap = v9p->_tick.non_priv_trap; | |
2665 | #endif /* } */ | |
2666 | val &= 0x7fffffffffffffffLL; | |
2667 | v9p->stick->offset = val - RAW_STICK(v9p); | |
2668 | ||
2669 | #ifdef NIAGARA2 | |
2670 | if (npp->tick_stop) | |
2671 | v9p->stick->offset = val; | |
2672 | #endif | |
2673 | ||
2674 | ss_recomp_tick_target(sp); | |
2675 | } | |
2676 | ||
2677 | static void ss_read_state_reg(simcpu_t * sp, uint_t rdest, uint_t state_reg) | |
2678 | { | |
2679 | uint64_t val; | |
2680 | sparcv9_cpu_t * v9p; | |
2681 | ss_strand_t * nsp; | |
2682 | ||
2683 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2684 | nsp = v9p->impl_specificp; | |
2685 | ||
2686 | switch (state_reg) { | |
2687 | case 0x00: /* y */ | |
2688 | #ifdef ROCK | |
2689 | if (!nsp->fpae) { | |
2690 | v9p->post_precise_trap(sp, | |
2691 | Sparcv9_trap_illegal_instruction); | |
2692 | return; | |
2693 | } | |
2694 | #endif | |
2695 | val = sp->v9_y; | |
2696 | break; | |
2697 | case 0x02: /* ccr */ | |
2698 | val = sp->v9_ccr; | |
2699 | break; | |
2700 | case 0x03: /* asi */ | |
2701 | val = sp->v9_asi; | |
2702 | break; | |
2703 | case 0x04: /* tick */ | |
2704 | if (v9p->state == V9_User && v9p->_tick.non_priv_trap) { | |
2705 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action); | |
2706 | return; | |
2707 | } | |
2708 | val = ss_read_tick(sp); | |
2709 | ||
2710 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2711 | if (v9p->_tick.non_priv_trap) val |= 1ULL<<63; | |
2712 | #endif /* } */ | |
2713 | DBGTICKREAD( lprintf(sp->gid, | |
2714 | "tick read asr: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2715 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2716 | break; | |
2717 | case 0x05: /* pc */ | |
2718 | val = sp->pc; /* Use a macro - FIXME */ | |
2719 | if (v9p->pstate.addr_mask) val &= MASK64(31,0); /* FIXME: SV9_ID125 */ | |
2720 | break; | |
2721 | case 0x06: /* fprs */ | |
2722 | /* Looks like that even if there is no FPU, we still need an FPRS register. | |
2723 | * There doesn't seem to be scope even for emulation. | |
2724 | */ | |
2725 | ||
2726 | val = ((v9p->fprs.fef) ? (1LL << V9_FPRS_FEF_BIT) :0LL) | | |
2727 | (1LL << V9_FPRS_DU_BIT) | /* for now du & dl are always 1 if fef */ | |
2728 | (1LL << V9_FPRS_DL_BIT); | |
2729 | DBGFPRS( lprintf(sp->gid, "ss_read_state_reg: %%fprs=%x @ pc=0x%llx\n", val, sp->pc); ); | |
2730 | break; | |
2731 | ||
2732 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2733 | case 0x10: /* PCR */ | |
2734 | if (v9p->state == V9_User) { | |
2735 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2736 | return; | |
2737 | } | |
2738 | val = nsp->pcr; | |
2739 | DBGPIC( lprintf(sp->gid, "ss_read_state_reg: (rd %%pcr @ pc=0x%llx, val=0x%llx)\n", | |
2740 | sp->pc, val); ); | |
2741 | nsp->pcr &= ~SS_PCR_CLEAR_ON_READ; | |
2742 | break; | |
2743 | ||
2744 | case 0x11: /* PIC */ | |
2745 | /* | |
2746 | * Need to create a proper pic value containing [pic1][pic0] where each pic | |
2747 | * is a 32bit number. | |
2748 | * | |
2749 | * pic0 needs to be a 32bit Instr count based on the sp->pic0 value | |
2750 | * For now, pic1 can be anything as long as it's increasing | |
2751 | */ | |
2752 | if (v9p->state == V9_User && (nsp->pcr & SS_PCR_PRIV) != 0) { | |
2753 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action); | |
2754 | return; | |
2755 | } | |
2756 | if (nsp->pcr & SS_PCR_UT_ST) { | |
2757 | UPDATE_PIC(sp, nsp, v9p); | |
2758 | } | |
2759 | ||
2760 | val = PIC0_PIC1_TO_CPU_COUNTER(nsp->pic0, nsp->pic1); | |
2761 | DBGPIC( lprintf(sp->gid, "ss_read_state_reg: rd %%pic @ pc=0x%llx, val=0x%llx [cycle=0x%llx]\n", | |
2762 | sp->pc, val, sp->cycle); ); | |
2763 | break; | |
2764 | #endif /* } */ | |
2765 | ||
2766 | case 0x13: | |
2767 | #ifdef ROCK | |
2768 | if (!nsp->fpae) { | |
2769 | v9p->post_precise_trap(sp, | |
2770 | Sparcv9_trap_illegal_instruction); | |
2771 | return; | |
2772 | } | |
2773 | #endif | |
2774 | if (!v9p->fpu_on) { | |
2775 | v9p->post_precise_trap(sp, Sparcv9_trap_fp_disabled); | |
2776 | return; | |
2777 | } | |
2778 | val = sp->v9_gsr; | |
2779 | break; | |
2780 | ||
2781 | case 0x16: /* softint */ | |
2782 | if (v9p->state == V9_User) { | |
2783 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2784 | return; | |
2785 | } | |
2786 | val = ((v9p->stick_cmpr.pending) ? (1LL<<16) : 0LL) | | |
2787 | v9p->softint | | |
2788 | ((v9p->tick_cmpr.pending) ? 1LL : 0LL ); | |
2789 | break; | |
2790 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2791 | case 0x17: /* tick_cmpr */ | |
2792 | if (v9p->state == V9_User) { | |
2793 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2794 | return; | |
2795 | } | |
2796 | val = ss_tick_cmpr_read(sp, &v9p->tick_cmpr); | |
2797 | DBGTICKREAD( lprintf(sp->gid, | |
2798 | "tick_cmpr read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2799 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2800 | break; | |
2801 | #endif /* } */ | |
2802 | case 0x18: /* stick */ | |
2803 | if (v9p->state == V9_User && v9p->_stick.non_priv_trap) { | |
2804 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action); | |
2805 | return; | |
2806 | } | |
2807 | val = ss_read_stick(sp); | |
2808 | ||
2809 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2810 | if (v9p->_stick.non_priv_trap) val |= 1ULL<<63; | |
2811 | #endif /* } */ | |
2812 | DBGTICKREAD( lprintf(sp->gid, | |
2813 | "stick read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2814 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2815 | break; | |
2816 | case 0x19: /* stick_cmpr */ | |
2817 | if (v9p->state == V9_User) { | |
2818 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2819 | return; | |
2820 | } | |
2821 | val = ss_tick_cmpr_read(sp, &v9p->stick_cmpr); | |
2822 | DBGTICKREAD( lprintf(sp->gid, | |
2823 | "stick_cmpr read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2824 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2825 | break; | |
2826 | #ifdef NIAGARA1 | |
2827 | case 0x1a: /* strand_sts_reg */ | |
2828 | switch (v9p->state) { | |
2829 | case V9_User: | |
2830 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2831 | return; | |
2832 | case V9_Priv: | |
2833 | /* If a strand is reading it, it must be active... */ | |
2834 | val = THREAD_STS_ACTIVE; | |
2835 | IMPL_WARNING(("ss_read_state_reg: (rd %%tsr @ pc=0x%llx) deprecated access to strand status register", sp->pc)); | |
2836 | break; | |
2837 | case V9_HyperPriv: | |
2838 | case V9_RED: | |
2839 | { | |
2840 | ss_proc_t *npp; | |
2841 | uint_t i, ie; | |
2842 | ||
2843 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
2844 | val = 0; | |
2845 | i = nsp->core * STRANDSPERCORE; | |
2846 | ie = i + STRANDSPERCORE; | |
2847 | while (i < ie) { | |
2848 | val <<= THREAD_STS_TSTATE_BITS; | |
2849 | val |= npp->sfsm_state[i++]; | |
2850 | } | |
2851 | val <<= THREAD_STS_TSTATE_SHIFT; | |
2852 | val |= (nsp->vcore_id << THREAD_STS_SHIFT); | |
2853 | if (nsp->spec_en) | |
2854 | val |= THREAD_STS_SPEC_EN; | |
2855 | val |= THREAD_STS_ACTIVE; | |
2856 | } | |
2857 | break; | |
2858 | default: | |
2859 | abort(); | |
2860 | } | |
2861 | DBGCMP( lprintf(sp->gid, | |
2862 | "%%tsr read: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2863 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2864 | break; | |
2865 | #endif | |
2866 | ||
2867 | #ifdef ROCK | |
2868 | case 0x1c: /* cps */ | |
2869 | val = nsp->cps; | |
2870 | DBGTM( lprintf(sp->gid, "%%cps read: " | |
2871 | "pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
2872 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
2873 | break; | |
2874 | #endif | |
2875 | ||
2876 | #if 0 | |
2877 | /* No unimplemented registers currently. */ | |
2878 | case 0x??: | |
2879 | FIXME_WARNING(("ss_read_state_reg: (rd) Unimplemented register 0x%x @ pc=0x%llx", state_reg, sp->pc)); | |
2880 | #endif | |
2881 | default: | |
2882 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
2883 | return; | |
2884 | } | |
2885 | ||
2886 | if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val; /* Use a macro - FIXME */ | |
2887 | ||
2888 | NEXT_INSTN(sp); | |
2889 | } | |
2890 | ||
2891 | ||
2892 | ||
2893 | ||
2894 | ||
2895 | ||
2896 | static void ss_write_state_reg(simcpu_t * sp, uint_t state_reg, uint64_t val) | |
2897 | { | |
2898 | sparcv9_cpu_t * v9p; | |
2899 | ss_strand_t * nsp; | |
2900 | bool_t prev_val; | |
2901 | ||
2902 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
2903 | nsp = (ss_strand_t *)v9p->impl_specificp; | |
2904 | ||
2905 | #ifdef ROCK | |
2906 | if (TM_ACTIVE(nsp)) { | |
2907 | EXEC_WARNING(("TM ERROR: TM active in 'ss_write_state_reg' function pc=0x%llx.", sp->pc)); | |
2908 | nsp->tm_fail(sp, V9_CPS_INSTR); | |
2909 | return; | |
2910 | } | |
2911 | #endif | |
2912 | ||
2913 | switch (state_reg) { | |
2914 | case 0x0: /* y */ | |
2915 | #ifdef ROCK | |
2916 | if (!nsp->fpae) { | |
2917 | v9p->post_precise_trap(sp, | |
2918 | Sparcv9_trap_illegal_instruction); | |
2919 | return; | |
2920 | } | |
2921 | #endif | |
2922 | sp->v9_y = val & MASK64(31,0); | |
2923 | break; | |
2924 | case 0x2: /* ccr */ | |
2925 | sp->v9_ccr = val & V9_CCR_MASK; | |
2926 | break; | |
2927 | case 0x3: /* asi */ | |
2928 | sp->v9_asi = val & V9_ASI_MASK; | |
2929 | break; | |
2930 | case 0x4: /* tick */ | |
2931 | #if defined(NIAGARA1) || defined(NIAGARA2) | |
2932 | switch (v9p->state) { | |
2933 | case V9_User: | |
2934 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2935 | return; | |
2936 | case V9_Priv: | |
2937 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
2938 | return; | |
2939 | case V9_HyperPriv: | |
2940 | case V9_RED: | |
2941 | break; | |
2942 | } | |
2943 | /* On niagara stick is an alias for tick */ | |
2944 | ss_write_tick(sp, val); | |
2945 | DBGTICK( lprintf(sp->gid, | |
2946 | "tick write asr: pc=0x%llx, o7=0x%llx, val=0x%llx tick=0x%llx\n", | |
2947 | sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_tick(sp)); ); | |
2948 | break; | |
2949 | #elif ROCK | |
2950 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
2951 | return; | |
2952 | #else | |
2953 | #error "No valid processor defined." | |
2954 | #endif | |
2955 | case 0x6: /* fprs */ | |
2956 | /* Looks like that even if there is no FPU, we still need an FPRS register. | |
2957 | * There doesn't seem to be scope even for emulation. | |
2958 | */ | |
2959 | ||
2960 | prev_val = v9p->fpu_on; | |
2961 | DBGFPRS( lprintf(sp->gid, "ss_write_state_reg: %%fprs=%x @ pc=0x%llx\n", val, sp->pc); ); | |
2962 | v9p->fprs.fef = ((val >> V9_FPRS_FEF_BIT)&0x1) ? true : false; | |
2963 | ||
2964 | v9p->fpu_on = (v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu); | |
2965 | #ifdef FP_DECODE_DISABLED | |
2966 | /* if FPU was enabled, we may have just changed the behaviour of FP instns */ | |
2967 | /* in which case flush the decoded instruction cache */ | |
2968 | if (v9p->fpu_on != prev_val) { | |
2969 | sp->xicache_instn_flush_pending = true; | |
2970 | set_sync_pending(sp); | |
2971 | } | |
2972 | #endif /* FP_DECODE_DISABLED */ | |
2973 | ||
2974 | v9p->fprs.du = ((val >> V9_FPRS_DU_BIT)&0x1) ? true : false; | |
2975 | v9p->fprs.dl = ((val >> V9_FPRS_DL_BIT)&0x1) ? true : false; | |
2976 | break; | |
2977 | ||
2978 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
2979 | case 0x10: /* PCR */ | |
2980 | if (v9p->state == V9_User) { | |
2981 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
2982 | return; | |
2983 | } | |
2984 | val &= SS_PCR_MASK; | |
2985 | DBGPIC( lprintf(sp->gid, "ss_write_state_reg: " | |
2986 | "(wr %%pcr @ pc=0x%llx, 0x%llx -> 0x%llx)\n", | |
2987 | sp->pc, nsp->pcr, val); ); | |
2988 | ||
2989 | /* | |
2990 | * If the pcr is active, we need to start counting instns | |
2991 | * so that when the pic is read, it counts the number of | |
2992 | * instns. | |
2993 | * As soon as the pic starts counting, we need to catch | |
2994 | * when it will overflow so that we can trigger a PIL 15 intr | |
2995 | */ | |
2996 | if (val & SS_PCR_UT_ST) { | |
2997 | /* Starting to count? Sample from now. */ | |
2998 | if ((nsp->pcr & SS_PCR_UT_ST) == 0) | |
2999 | RESET_PIC_SAMPLE_BASES(sp, nsp, v9p); | |
3000 | nsp->pcr = val; | |
3001 | ss_recomp_cycle_target(sp); /* catch next overflow event */ | |
3002 | } else { | |
3003 | /* Stopping counting? Collect last samples. */ | |
3004 | if ((nsp->pcr & SS_PCR_UT_ST) != 0) | |
3005 | UPDATE_PIC(sp, nsp, v9p); | |
3006 | nsp->pcr = val; | |
3007 | } | |
3008 | ||
3009 | if (SS_PCR_TEST_OVF_PENDING(nsp->pcr)) { | |
3010 | v9p->softint |= BIT(15); | |
3011 | DBGSOFTINT( lprintf(sp->gid, | |
3012 | "softint wr pcr overflow: pc=0x%llx, o7=0x%llx, val=0x%llx, stick=%u, tick=%u\n", | |
3013 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, v9p->stick_cmpr.pending, | |
3014 | v9p->tick_cmpr.pending); ); | |
3015 | ss_check_interrupts(sp); | |
3016 | } | |
3017 | break; | |
3018 | ||
3019 | case 0x11: /* PIC */ | |
3020 | if (v9p->state == V9_User && (nsp->pcr & SS_PCR_PRIV) != 0) { | |
3021 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_action); | |
3022 | return; | |
3023 | } | |
3024 | DBGPIC( lprintf(sp->gid, "ss_write_state_reg: (wr %%pic @ pc=0x%llx, val=0x%llx)\n", sp->pc, val); ); | |
3025 | nsp->pic0 = CPU_COUNTER_TO_PIC0(val); | |
3026 | nsp->pic1 = CPU_COUNTER_TO_PIC1(val); | |
3027 | RESET_PIC_SAMPLE_BASES(sp, nsp, v9p); | |
3028 | ss_recomp_cycle_target(sp); /* catch next overflow event */ | |
3029 | ||
3030 | break; | |
3031 | #endif /* } */ | |
3032 | ||
3033 | case 0x13: /* GSR */ | |
3034 | #ifdef ROCK | |
3035 | if (!nsp->fpae) { | |
3036 | v9p->post_precise_trap(sp, | |
3037 | Sparcv9_trap_illegal_instruction); | |
3038 | return; | |
3039 | } | |
3040 | #endif | |
3041 | if (!v9p->fpu_on) { | |
3042 | v9p->post_precise_trap(sp, Sparcv9_trap_fp_disabled); | |
3043 | return; | |
3044 | } | |
3045 | DBGFSR( lprintf(sp->gid, "ss_write_state_reg: pc=0x%llx, gsr=0x%llx (was 0x%llx)\n", sp->pc, val, sp->v9_gsr); ); | |
3046 | sp->v9_gsr = val & V9_GSR_REG_MASK; | |
3047 | break; | |
3048 | ||
3049 | case 0x14: /* set softint register */ | |
3050 | if (v9p->state == V9_User) { | |
3051 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3052 | return; | |
3053 | } | |
3054 | if ((val>>16)&1LL) v9p->stick_cmpr.pending = true; | |
3055 | if ((val>>0)&1LL) v9p->tick_cmpr.pending = true; | |
3056 | v9p->softint |= val & MASK64(15,1); | |
3057 | DBGSOFTINT( lprintf(sp->gid, | |
3058 | "softint write set: pc=0x%llx, o7=0x%llx, val=0x%llx, " | |
3059 | "softint=0x%llx, stick=%d, tick=%d\n", | |
3060 | sp->pc, sp->intreg[Reg_sparcv9_o7], val, v9p->softint, | |
3061 | v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); ); | |
3062 | ss_check_interrupts(sp); | |
3063 | break; | |
3064 | case 0x15: /* clear softint register */ | |
3065 | if (v9p->state == V9_User) { | |
3066 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3067 | return; | |
3068 | } | |
3069 | if ((val>>16)&1LL) v9p->stick_cmpr.pending = false; | |
3070 | if ((val>>0)&1LL) v9p->tick_cmpr.pending = false; | |
3071 | v9p->softint &= (~val) & MASK64(15,1); | |
3072 | DBGSOFTINT( lprintf(sp->gid, | |
3073 | "softint write clear: pc=0x%llx, o7=0x%llx, val=0x%llx, " | |
3074 | "softint=0x%llx, stick=%d, tick=%d\n", | |
3075 | sp->pc, sp->intreg[Reg_sparcv9_o7], val, v9p->softint, | |
3076 | v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); ); | |
3077 | ss_check_interrupts(sp); | |
3078 | break; | |
3079 | case 0x16: /* softint */ | |
3080 | if (v9p->state == V9_User) { | |
3081 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3082 | return; | |
3083 | } | |
3084 | v9p->stick_cmpr.pending = ((val >> 16)&1LL) ? true : false; | |
3085 | v9p->tick_cmpr.pending = ((val >> 0)&1LL) ? true : false; | |
3086 | v9p->softint = val & MASK64(15,1); | |
3087 | DBGSOFTINT( lprintf(sp->gid, | |
3088 | "softint write: pc=0x%llx, o7=0x%llx, softint=0x%llx, " | |
3089 | "stick=%d, tick=%d\n", | |
3090 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->softint, | |
3091 | v9p->stick_cmpr.pending, v9p->tick_cmpr.pending); ); | |
3092 | ss_check_interrupts(sp); | |
3093 | break; | |
3094 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3095 | case 0x17: /* tick_cmpr */ | |
3096 | if (v9p->state == V9_User) { | |
3097 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3098 | return; | |
3099 | } | |
3100 | ss_tick_cmpr_write(sp, &v9p->tick_cmpr, val); | |
3101 | DBGTICK( lprintf(sp->gid, | |
3102 | "tick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-tick=0x%llx\n", | |
3103 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->tick_cmpr.compare, v9p->tick_cmpr.interrupt_enabled, | |
3104 | v9p->tick_cmpr.compare - ss_read_tick(sp)); ); | |
3105 | ss_recomp_cycle_target(sp); | |
3106 | break; | |
3107 | #endif /* } */ | |
3108 | case 0x18: /* stick */ | |
3109 | #if defined(NIAGARA1) || defined(NIAGARA2) | |
3110 | switch (v9p->state) { | |
3111 | case V9_User: | |
3112 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3113 | return; | |
3114 | case V9_Priv: | |
3115 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3116 | return; | |
3117 | case V9_HyperPriv: | |
3118 | case V9_RED: | |
3119 | break; | |
3120 | } | |
3121 | ss_write_stick(sp, val); | |
3122 | DBGTICK( lprintf(sp->gid, | |
3123 | "stick write: pc=0x%llx, o7=0x%llx, val=0x%llx stick=0x%llx\n", | |
3124 | sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_stick(sp)); ); | |
3125 | break; | |
3126 | #elif defined(ROCK) | |
3127 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3128 | return; | |
3129 | #else | |
3130 | #error "No valid processor defined." | |
3131 | #endif | |
3132 | case 0x19: /* stick_cmpr */ | |
3133 | if (v9p->state == V9_User) { | |
3134 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3135 | return; | |
3136 | } | |
3137 | ss_tick_cmpr_write(sp, &v9p->stick_cmpr, val); | |
3138 | DBGTICK( lprintf(sp->gid, | |
3139 | "stick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-stick=0x%llx\n", | |
3140 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->stick_cmpr.compare, v9p->stick_cmpr.interrupt_enabled, | |
3141 | v9p->stick_cmpr.compare - ss_read_stick(sp)); ); | |
3142 | ss_recomp_cycle_target(sp); | |
3143 | break; | |
3144 | #ifdef NIAGARA1 | |
3145 | case 0x1a: /* strand_sts_reg */ | |
3146 | switch (v9p->state) { | |
3147 | case V9_User: | |
3148 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3149 | return; | |
3150 | case V9_Priv: | |
3151 | DBGCMP( lprintf(sp->gid, "%%tsr write priv: " | |
3152 | "pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
3153 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
3154 | IMPL_WARNING(("ss_write_state_reg: (wr %%tsr @ pc=0x%llx) deprecated access to strand status register", sp->pc)); | |
3155 | goto thread_halt; | |
3156 | case V9_HyperPriv: | |
3157 | case V9_RED: | |
3158 | DBGCMP( lprintf(sp->gid, "%%tsr write: " | |
3159 | "pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
3160 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
3161 | nsp->spec_en = BIT_TEST( val, THREAD_STS_SPEC_EN_BIT ) ? true : false; | |
3162 | thread_halt: if (!(val & THREAD_STS_ACTIVE)) { | |
3163 | IMPL_WARNING(("ss_write_state_reg: (wr %%tsr @ pc=0x%llx) thread halt not supported", sp->pc)); | |
3164 | } | |
3165 | break; | |
3166 | default: | |
3167 | abort(); | |
3168 | } | |
3169 | break; | |
3170 | #endif | |
3171 | ||
3172 | #ifdef ROCK | |
3173 | case 0x1c: /* cps */ | |
3174 | DBGTM( lprintf(sp->gid, "%%cps write: " | |
3175 | "pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
3176 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
3177 | nsp->cps = val & V9_CPS_REG_MASK; | |
3178 | break; | |
3179 | #endif | |
3180 | ||
3181 | #if 0 | |
3182 | /* No unimplemented registers currently. */ | |
3183 | case 0x??: | |
3184 | FIXME_WARNING(("ss_write_state_reg: (wr) Unimplemented register 0x%x @ pc=0x%llx", state_reg, sp->pc)); | |
3185 | #endif | |
3186 | default: | |
3187 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3188 | return; | |
3189 | } | |
3190 | ||
3191 | NEXT_INSTN(sp); | |
3192 | } | |
3193 | ||
3194 | ||
3195 | ||
3196 | ||
3197 | static void ss_read_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg) | |
3198 | { | |
3199 | sparcv9_cpu_t * v9p; | |
3200 | uint64_t val; | |
3201 | ||
3202 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3203 | ||
3204 | ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]); | |
3205 | ||
3206 | if (v9p->state == V9_User) { | |
3207 | ASSERT( !v9p->pstate.priv && !v9p->hpstate.hpriv ); | |
3208 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3209 | return; | |
3210 | } | |
3211 | ||
3212 | switch (priv_reg) { | |
3213 | case 0: /* tpc */ | |
3214 | if (v9p->tl == 0) goto illegal_instruction; | |
3215 | val = N_TPC(v9p, v9p->tl); | |
3216 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3217 | VA48_ASSERT(val); | |
3218 | #endif /* } */ | |
3219 | if (v9p->pstate.addr_mask) { | |
3220 | val &= MASK64(31,0); /* FIXME: SV9_ID125 */ | |
3221 | EXEC_WARNING(("@ pc=0x%llx : TPC read with pstate.am == 1", sp->pc)); | |
3222 | } | |
3223 | break; | |
3224 | case 1: /* tnpc */ | |
3225 | if (v9p->tl == 0) goto illegal_instruction; | |
3226 | val = N_TNPC(v9p, v9p->tl); | |
3227 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3228 | VA48_ASSERT(val); | |
3229 | #endif /* } */ | |
3230 | if (v9p->pstate.addr_mask) { | |
3231 | val &= MASK64(31,0); /* FIXME: SV9_ID125 */ | |
3232 | EXEC_WARNING(("@ pc=0x%llx : TNPC read with pstate.am == 1", sp->pc)); | |
3233 | } | |
3234 | break; | |
3235 | case 2: | |
3236 | if (v9p->tl == 0) goto illegal_instruction; | |
3237 | val = N_TSTATE(v9p, v9p->tl); | |
3238 | break; | |
3239 | case 3: | |
3240 | if (v9p->tl == 0) goto illegal_instruction; | |
3241 | val = N_TT(v9p, v9p->tl); | |
3242 | break; | |
3243 | case 4: /* tick */ | |
3244 | val = ss_read_tick(sp); | |
3245 | ||
3246 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3247 | if (v9p->_tick.non_priv_trap) val |= 1ULL<<63; | |
3248 | #endif /* } */ | |
3249 | DBGTICKREAD( lprintf(sp->gid, | |
3250 | "tick read priv: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
3251 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
3252 | break; | |
3253 | case 5: /* tba */ | |
3254 | val = v9p->tba; | |
3255 | break; | |
3256 | case 6: /* pstate */ | |
3257 | val = ss_read_pstate(v9p); | |
3258 | break; | |
3259 | case 7: /* tl */ | |
3260 | val = v9p->tl; | |
3261 | break; | |
3262 | case 8: /* pil */ | |
3263 | val = v9p->pil; | |
3264 | break; | |
3265 | case 9: /* cwp */ | |
3266 | val = v9p->cwp; | |
3267 | break; | |
3268 | case 0xa: /* cansave */ | |
3269 | val = v9p->cansave; | |
3270 | break; | |
3271 | case 0xb: /* canrestore */ | |
3272 | val = v9p->canrestore; | |
3273 | break; | |
3274 | case 0xc: /* cleanwin */ | |
3275 | val = v9p->cleanwin; | |
3276 | break; | |
3277 | case 0xd: /* otherwin */ | |
3278 | val = v9p->otherwin; | |
3279 | break; | |
3280 | case 0xe: | |
3281 | val = (v9p->wstate_normal << V9_WSTATE_NORMAL_BITS) | | |
3282 | (v9p->wstate_other << V9_WSTATE_OTHER_BITS); | |
3283 | break; | |
3284 | case 0x10: | |
3285 | val = v9p->gl; | |
3286 | break; | |
3287 | default: | |
3288 | illegal_instruction: | |
3289 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3290 | return; | |
3291 | } | |
3292 | ||
3293 | if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val; | |
3294 | ||
3295 | NEXT_INSTN(sp); | |
3296 | } | |
3297 | ||
3298 | ||
3299 | ||
3300 | static void ss_write_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val) | |
3301 | { | |
3302 | sparcv9_cpu_t * v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3303 | uint64_t old_val; | |
3304 | ss_strand_t * nsp = (ss_strand_t *)(v9p->impl_specificp); | |
3305 | ||
3306 | ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]); | |
3307 | ||
3308 | if (v9p->state == V9_User) { | |
3309 | ASSERT( !v9p->pstate.priv && !v9p->hpstate.hpriv ); | |
3310 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3311 | return; | |
3312 | } | |
3313 | ||
3314 | ||
3315 | #ifdef ROCK | |
3316 | if (TM_ACTIVE(nsp)) { | |
3317 | EXEC_WARNING(("TM ERROR: TM active in 'ss_write_priv_reg' function pc=0x%llx.", sp->pc)); | |
3318 | nsp->tm_fail(sp, V9_CPS_INSTR); | |
3319 | return; | |
3320 | } | |
3321 | #endif | |
3322 | ||
3323 | switch (priv_reg) { | |
3324 | case 0: /* tpc */ | |
3325 | if (v9p->tl == 0) goto illegal_instruction; | |
3326 | if (v9p->pstate.addr_mask) EXEC_WARNING(("writing to tpc with pstate.am=1")); | |
3327 | val &= ~3; | |
3328 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3329 | val = VA48(val); | |
3330 | #endif /* } */ | |
3331 | N_TPC(v9p, v9p->tl) = val; /* FIXME: SV9_ID125 issue ? */ | |
3332 | break; | |
3333 | case 1: /* tnpc */ | |
3334 | if (v9p->tl == 0) goto illegal_instruction; | |
3335 | if (v9p->pstate.addr_mask) EXEC_WARNING(("writing to tnpc with pstate.am=1")); | |
3336 | val &= ~3; | |
3337 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3338 | val = VA48(val); | |
3339 | #endif /* } */ | |
3340 | N_TNPC(v9p, v9p->tl) = val; /* FIXME: SV9_ID125 issue ? */ | |
3341 | break; | |
3342 | case 2: /* tstate */ | |
3343 | if (v9p->tl == 0) goto illegal_instruction; | |
3344 | N_TSTATE(v9p, v9p->tl) = val & | |
3345 | ((V9_TSTATE_GL_MASK<<V9_TSTATE_GL_SHIFT) | | |
3346 | (V9_TSTATE_CCR_MASK<<V9_TSTATE_CCR_SHIFT) | | |
3347 | (V9_TSTATE_ASI_MASK<<V9_TSTATE_ASI_SHIFT) | | |
3348 | (V9_TSTATE_PSTATE_MASK<<V9_TSTATE_PSTATE_SHIFT) | | |
3349 | (V9_TSTATE_CWP_MASK<<V9_TSTATE_CWP_SHIFT) ); | |
3350 | break; | |
3351 | case 3: /* tt */ | |
3352 | if (v9p->tl == 0) goto illegal_instruction; | |
3353 | DBGTSTACK( | |
3354 | log_lock(); | |
3355 | log_printf(sp->gid, "Pre-TT write pc=0x%llx\n", sp->pc); | |
3356 | sparcv9_dump_state(sp); ); | |
3357 | if ((val & V9_TT_MASK) != N_TT(v9p, v9p->tl)) { | |
3358 | N_TT(v9p, v9p->tl) = val & V9_TT_MASK; | |
3359 | DBGTSTACK(log_printf(sp->gid, "Post-TT write\n"); sparcv9_dump_state(sp);); | |
3360 | } else { | |
3361 | DBGTSTACK(log_printf(sp->gid, "Post-TT write - TT unchanged\n");); | |
3362 | } | |
3363 | DBGTSTACK( log_unlock(); ); | |
3364 | break; | |
3365 | case 0x4: /* tick */ | |
3366 | #if defined(NIAGARA1) || defined(NIAGARA2) | |
3367 | switch (v9p->state) { | |
3368 | default: | |
3369 | case V9_Priv: | |
3370 | goto illegal_instruction; | |
3371 | case V9_HyperPriv: | |
3372 | case V9_RED: | |
3373 | break; | |
3374 | } | |
3375 | /* On niagara stick is an alias for tick */ | |
3376 | ss_write_tick(sp, val); | |
3377 | DBGTICK( lprintf(sp->gid, | |
3378 | "tick write priv: pc=0x%llx, o7=0x%llx, val=0x%llx tick=0x%llx\n", | |
3379 | sp->pc, sp->intreg[Reg_sparcv9_o7], val, ss_read_tick(sp)); ); | |
3380 | break; | |
3381 | #elif ROCK | |
3382 | goto illegal_instruction; | |
3383 | #else | |
3384 | #error "No valid processor defined." | |
3385 | #endif | |
3386 | case 5: /* tba */ | |
3387 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3388 | val = VA48(val); | |
3389 | #endif /* } */ | |
3390 | v9p->tba = val & V9_TBA_MASK; | |
3391 | break; | |
3392 | case 6: /* pstate */ | |
3393 | ss_write_pstate(sp, val); /* like to change states */ | |
3394 | break; /* pc updated as usual */ | |
3395 | case 7: /* tl - saturates */ | |
3396 | if (v9p->state == V9_Priv) { | |
3397 | if (val > Q_MAXPTL) { | |
3398 | EXEC_WARNING(("privileged write to %%tl of %u saturated to %u (%%pc=0x%llx)", val, Q_MAXPTL, sp->pc)); | |
3399 | val = Q_MAXPTL; | |
3400 | } | |
3401 | #if DEBUG_TL_RAISE | |
3402 | /* Unsafe raising of %tl, trash values */ | |
3403 | if (val > v9p->tl) { | |
3404 | /* bogus values must pass the VA48_ASSERT */ | |
3405 | N_TPC(v9p, val) = 0xffffbeefdeadbeecull; | |
3406 | N_TNPC(v9p, val) = 0xffffbeefdeadbeecull; | |
3407 | N_TSTATE(v9p, val) = | |
3408 | ((V9_TSTATE_GL_MASK<<V9_TSTATE_GL_SHIFT) | | |
3409 | (V9_TSTATE_CCR_MASK<<V9_TSTATE_CCR_SHIFT) | | |
3410 | (V9_TSTATE_ASI_MASK<<V9_TSTATE_ASI_SHIFT) | | |
3411 | (V9_TSTATE_PSTATE_MASK<<V9_TSTATE_PSTATE_SHIFT) | | |
3412 | (V9_TSTATE_CWP_MASK<<V9_TSTATE_CWP_SHIFT) ); | |
3413 | N_TT(v9p, val) = 0xffff & V9_TT_MASK; | |
3414 | } | |
3415 | #endif /* DEBUG_TL_RAISE */ | |
3416 | v9p->tl = val; | |
3417 | } else { | |
3418 | if (val > v9p->maxtl) { | |
3419 | EXEC_WARNING(("hyperprivileged write to %%tl of %u saturated to %u (%%pc=0x%llx)", val, v9p->maxtl, sp->pc)); | |
3420 | } | |
3421 | v9p->tl = (val>v9p->maxtl) ? v9p->maxtl : val; | |
3422 | } | |
3423 | xcache_set_tagstate(sp); | |
3424 | break; | |
3425 | case 8: /* pil */ | |
3426 | v9p->pil = val & V9_PIL_MASK; | |
3427 | ss_check_interrupts(sp); | |
3428 | break; | |
3429 | case 9: /* cwp - saturates */ | |
3430 | old_val = val; | |
3431 | val &= v9p->nwins_mask; | |
3432 | if (val >= v9p->nwins) { | |
3433 | val = (v9p->nwins - 1); | |
3434 | #if 0 /* this is expected */ | |
3435 | EXEC_WARNING(("write to %%cwp of %u saturated to %u (%%pc=0x%llx)", | |
3436 | old_val, val , sp->pc)); | |
3437 | #endif | |
3438 | } | |
3439 | if (v9p->cwp != val) { | |
3440 | v9p->cwp = val; | |
3441 | sparcv9_active_window(sp, v9p->cwp); | |
3442 | } | |
3443 | break; | |
3444 | case 10: /* cansave - saturates */ | |
3445 | old_val = val; | |
3446 | val &= v9p->nwins_mask; | |
3447 | if (val >= v9p->nwins) { | |
3448 | val = (v9p->nwins - 1); | |
3449 | EXEC_WARNING(("write to %%cansave of %u saturated to %u (%%pc=0x%llx)", | |
3450 | old_val, val, sp->pc)); | |
3451 | } | |
3452 | v9p->cansave = val; | |
3453 | break; | |
3454 | case 11: /* canrestore - saturates */ | |
3455 | old_val = val; | |
3456 | val &= v9p->nwins_mask; | |
3457 | if (val >= v9p->nwins) { | |
3458 | val = (v9p->nwins - 1); | |
3459 | EXEC_WARNING(("write to %%canrestore of %u saturated to %u (%%pc=0x%llx)", | |
3460 | old_val, val, sp->pc)); | |
3461 | } | |
3462 | v9p->canrestore = val; | |
3463 | break; | |
3464 | case 12: /* cleanwin - saturates */ | |
3465 | old_val = val; | |
3466 | val &= v9p->nwins_mask; | |
3467 | if (val >= v9p->nwins) { | |
3468 | val = (v9p->nwins - 1); | |
3469 | EXEC_WARNING(("write to %%cleanwin of %u saturated to %u(%%pc=0x%llx)", | |
3470 | old_val, val, sp->pc)); | |
3471 | } | |
3472 | v9p->cleanwin = val; | |
3473 | break; | |
3474 | case 13: /* otherwin - saturates */ | |
3475 | old_val = val; | |
3476 | val &= v9p->nwins_mask; | |
3477 | if (val >= v9p->nwins) { | |
3478 | val = (v9p->nwins - 1); | |
3479 | EXEC_WARNING(("write to %%otherwin of %u saturated to %u (%%pc=0x%llx)", | |
3480 | old_val, val, sp->pc)); | |
3481 | } | |
3482 | v9p->otherwin = val; | |
3483 | break; | |
3484 | case 14: /* wstate */ | |
3485 | v9p->wstate_normal = (val >> V9_WSTATE_NORMAL_BITS) & V9_WSTATE_MASK; | |
3486 | v9p->wstate_other = (val >> V9_WSTATE_OTHER_BITS) & V9_WSTATE_MASK; | |
3487 | break; | |
3488 | case 16: | |
3489 | switch(v9p->state) { | |
3490 | default: | |
3491 | case V9_User: abort(); /* shouldn't get here */ | |
3492 | case V9_Priv: | |
3493 | if (val > Q_MAXPGL) { | |
3494 | EXEC_WARNING(("privileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val, Q_MAXPGL, sp->pc)); | |
3495 | val = Q_MAXPGL; | |
3496 | } | |
3497 | break; | |
3498 | case V9_HyperPriv: | |
3499 | case V9_RED: | |
3500 | if (val >= v9p->nglobals) { | |
3501 | EXEC_WARNING(("hyperprivileged write to %%gl of %u saturated to %u (%%pc=0x%llx)", val, v9p->nglobals - 1, sp->pc)); | |
3502 | val = v9p->nglobals-1; | |
3503 | } | |
3504 | break; | |
3505 | } | |
3506 | #if DEBUG_GL_RAISE | |
3507 | /* Unsafe raising of %gl, trash values */ | |
3508 | if (v9p->state == V9_Priv && val > v9p->gl) { | |
3509 | uint_t j, i; | |
3510 | uint64_t *arch_regp; | |
3511 | ||
3512 | for (j = val; j > v9p->gl; j--) { | |
3513 | arch_regp = &(v9p->globalsp[(val)*V9_GLOBAL_GROUP]); | |
3514 | arch_regp++; /* skip %g0 */ | |
3515 | for (i = V9_GLOBAL_GROUP - 1; i > 0;i-- ) { | |
3516 | *arch_regp++ = 0xdeadbeefdeadbeefull; | |
3517 | } | |
3518 | } | |
3519 | } | |
3520 | #endif /* DEBUG_GL_RAISE */ | |
3521 | if (v9p->gl != val) { | |
3522 | v9p->gl = val; | |
3523 | sparcv9_active_globals(sp, v9p->gl); | |
3524 | } | |
3525 | break; | |
3526 | ||
3527 | default: | |
3528 | illegal_instruction: | |
3529 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3530 | return; | |
3531 | } | |
3532 | ||
3533 | NEXT_INSTN(sp); | |
3534 | } | |
3535 | ||
3536 | ||
3537 | ||
3538 | ||
3539 | ||
3540 | ||
3541 | ||
3542 | ||
3543 | ||
3544 | ||
3545 | static void ss_read_hyp_priv_reg(simcpu_t * sp, uint_t rdest, uint_t priv_reg) | |
3546 | { | |
3547 | sparcv9_cpu_t * v9p; | |
3548 | uint64_t val; | |
3549 | ss_strand_t * nsp; | |
3550 | ||
3551 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3552 | nsp = v9p->impl_specificp; | |
3553 | ||
3554 | if (V9_HyperPriv!=v9p->state && V9_RED!=v9p->state) { | |
3555 | ASSERT( !v9p->hpstate.hpriv ); | |
3556 | goto illegal_instruction; | |
3557 | } | |
3558 | ||
3559 | switch (priv_reg) { | |
3560 | case 0x0: /* hpstate */ | |
3561 | val = ss_read_hpstate(v9p); | |
3562 | break; | |
3563 | case 0x1: /* htstate */ | |
3564 | if (v9p->tl == 0) goto illegal_instruction; | |
3565 | val = N_HTSTATE(v9p, v9p->tl); | |
3566 | break; | |
3567 | case 0x3: | |
3568 | val = v9p->hstick_cmpr.pending ? 1 : 0; | |
3569 | break; | |
3570 | case 0x5: /* htba */ | |
3571 | val = v9p->htba; | |
3572 | break; | |
3573 | #if !defined(ROCK) | |
3574 | case 0x6: /* hver */ | |
3575 | val = v9p->ver; | |
3576 | break; | |
3577 | #endif | |
3578 | ||
3579 | #ifdef NIAGARA2 /* { */ | |
3580 | case 0x1e: /* halt */ | |
3581 | val = 0; | |
3582 | IMPL_WARNING(("ss_read_hyp_priv_reg: (rdhpr %%halt, <reg> @ pc=0x%llx) halt not implemented", sp->pc)); | |
3583 | break; | |
3584 | #endif /* } */ | |
3585 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3586 | case 0x1f: /* hstick_cmpr */ | |
3587 | val = ss_tick_cmpr_read(sp, &v9p->hstick_cmpr); | |
3588 | DBGTICKREAD( lprintf(sp->gid, | |
3589 | "hstick_cmpr read: pc=0x%llx, o7=0x%llx, val=0x%llx\n", | |
3590 | sp->pc, sp->intreg[Reg_sparcv9_o7], val); ); | |
3591 | break; | |
3592 | #endif /* } */ | |
3593 | default: | |
3594 | illegal_instruction: | |
3595 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3596 | return; | |
3597 | } | |
3598 | ||
3599 | if (rdest != Reg_sparcv9_g0) sp->intreg[rdest] = val; | |
3600 | ||
3601 | NEXT_INSTN(sp); | |
3602 | } | |
3603 | ||
3604 | ||
3605 | static void ss_write_hyp_priv_reg(simcpu_t * sp, uint_t priv_reg, uint64_t val) | |
3606 | { | |
3607 | sparcv9_cpu_t * v9p; | |
3608 | ss_strand_t * nsp; | |
3609 | ||
3610 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3611 | nsp = v9p->impl_specificp; | |
3612 | ||
3613 | if (V9_HyperPriv!=v9p->state && V9_RED!=v9p->state) { | |
3614 | ASSERT( !v9p->hpstate.hpriv ); | |
3615 | goto illegal_instruction; | |
3616 | } | |
3617 | ||
3618 | #ifdef ROCK | |
3619 | if (TM_ACTIVE(nsp)) { | |
3620 | EXEC_WARNING(("TM ERROR: TM active in 'ss_write_hyp_priv_reg' function pc=0x%llx.", sp->pc)); | |
3621 | nsp->tm_fail(sp, V9_CPS_INSTR); | |
3622 | return; | |
3623 | } | |
3624 | #endif | |
3625 | ||
3626 | switch (priv_reg) { | |
3627 | case 0x0: /* hpstate */ | |
3628 | ss_write_hpstate(sp, false, val); | |
3629 | break; | |
3630 | case 0x1: /* htstate */ | |
3631 | if (v9p->tl == 0) goto illegal_instruction; | |
3632 | #if defined(NIAGARA1) | |
3633 | #define SS_HTSTATE_MASK 0x0425 | |
3634 | #elif defined(NIAGARA2) | |
3635 | #define SS_HTSTATE_MASK 0x0425 | |
3636 | #elif defined(ROCK) | |
3637 | #define SS_HTSTATE_MASK 0xf025 | |
3638 | #else | |
3639 | #error "No processor defined" | |
3640 | #endif | |
3641 | N_HTSTATE(v9p, v9p->tl) = val & SS_HTSTATE_MASK; | |
3642 | break; | |
3643 | case 0x3: | |
3644 | if (val & 1) { | |
3645 | v9p->hstick_cmpr.pending = 1; | |
3646 | ss_check_interrupts(sp); | |
3647 | } else | |
3648 | v9p->hstick_cmpr.pending = 0; | |
3649 | DBGSOFTINT( lprintf(sp->gid, | |
3650 | "hintp write: pc=0x%llx, o7=0x%llx, hintp=%u\n", | |
3651 | sp->pc, sp->intreg[Reg_sparcv9_o7], | |
3652 | v9p->hstick_cmpr.pending); ); | |
3653 | break; | |
3654 | case 0x5: /* htba */ | |
3655 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3656 | val = VA48(val); | |
3657 | #endif /* } */ | |
3658 | v9p->htba = val & V9_HTBA_MASK; | |
3659 | break; | |
3660 | case 0x6: /* ver */ | |
3661 | /* read only register ! */ | |
3662 | goto illegal_instruction; | |
3663 | ||
3664 | #ifdef NIAGARA2 /* { */ | |
3665 | case 0x1e: /* halt */ | |
3666 | IMPL_WARNING(("ss_write_hyp_priv_reg: (wrhpr <reg>, %%halt @ pc=0x%llx) halt not implemented", sp->pc)); | |
3667 | break; | |
3668 | #endif /* } */ | |
3669 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
3670 | case 0x1f: /* hstick_cmpr */ | |
3671 | ss_tick_cmpr_write(sp, &v9p->hstick_cmpr, val); | |
3672 | DBGTICK( lprintf(sp->gid, | |
3673 | "hstick_cmpr write: pc=0x%llx, o7=0x%llx, val=0x%llx inten=%u val-stick=0x%llx\n", | |
3674 | sp->pc, sp->intreg[Reg_sparcv9_o7], v9p->hstick_cmpr.compare, v9p->hstick_cmpr.interrupt_enabled, | |
3675 | v9p->hstick_cmpr.compare - ss_read_stick(sp)); ); | |
3676 | ss_recomp_cycle_target(sp); | |
3677 | break; | |
3678 | #endif /* } */ | |
3679 | default: | |
3680 | illegal_instruction: | |
3681 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3682 | return; | |
3683 | } | |
3684 | ||
3685 | NEXT_INSTN(sp); | |
3686 | } | |
3687 | ||
3688 | ||
3689 | ||
3690 | /*************************************************************/ | |
3691 | ||
3692 | /* | |
3693 | * Done / Retry handler | |
3694 | */ | |
3695 | ||
3696 | void ss_done_retry(simcpu_t * sp, bool_t is_done) | |
3697 | { | |
3698 | sparcv9_cpu_t * v9p; | |
3699 | uint64_t val; | |
3700 | uint_t win, gl; | |
3701 | tvaddr_t new_pc, new_npc; | |
3702 | ||
3703 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3704 | ||
3705 | if (v9p->state == V9_User) { | |
3706 | v9p->post_precise_trap(sp, Sparcv9_trap_privileged_opcode); | |
3707 | return; | |
3708 | } | |
3709 | ||
3710 | if (v9p->tl == 0) { | |
3711 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3712 | return; | |
3713 | } | |
3714 | ||
3715 | if (!is_done) { | |
3716 | new_pc = N_TPC(v9p, v9p->tl); /* retry */ | |
3717 | new_npc = N_TNPC(v9p, v9p->tl); | |
3718 | } else { | |
3719 | new_pc = N_TNPC(v9p, v9p->tl); /* done */ | |
3720 | new_npc = new_pc + 4; | |
3721 | } | |
3722 | ||
3723 | val = N_TSTATE(v9p, v9p->tl); | |
3724 | ||
3725 | DBGE( lprintf(sp->gid, "%s: tl=%u : gl=%u : pc=0x%llx : new_pc=0x%llx " | |
3726 | "new_npc=0x%llx tstate=0x%llx\n", | |
3727 | (is_done ? "done" : "retry"), | |
3728 | v9p->tl, v9p->gl, sp->pc, new_pc, new_npc, val); ); | |
3729 | ||
3730 | /* Check for return to 32bit mode */ | |
3731 | if (((val >> V9_TSTATE_PSTATE_SHIFT) >> V9_PSTATE_AM_BIT)&1) { | |
3732 | /* simple sanity checks ... */ | |
3733 | if (new_pc & MASK64(63,32)) EXEC_WARNING(("done/retry to non-32bit PC with pstate.am =1")); | |
3734 | if (new_npc & MASK64(63,32)) EXEC_WARNING(("done/retry to non-32bit NPC with pstate.am =1")); | |
3735 | ||
3736 | new_pc &= MASK64(31,0); | |
3737 | new_npc &= MASK64(31,0); | |
3738 | } | |
3739 | ||
3740 | /* setup the new pc and npc */ | |
3741 | sp->pc = new_pc; | |
3742 | sp->npc = new_npc; | |
3743 | ||
3744 | /* restore everything from TSTATE register */ | |
3745 | ||
3746 | sp->v9_ccr = (val >> V9_TSTATE_CCR_SHIFT) & V9_TSTATE_CCR_MASK; | |
3747 | sp->v9_asi = (val >> V9_TSTATE_ASI_SHIFT) & V9_TSTATE_ASI_MASK; | |
3748 | ||
3749 | win = (val >> V9_TSTATE_CWP_SHIFT) & V9_TSTATE_CWP_MASK; | |
3750 | if (win >= v9p->nwins) { | |
3751 | warning("restoring cwp with value too large from TSTATE (%u) ; saturating",win); | |
3752 | win = v9p->nwins-1; | |
3753 | } | |
3754 | if (v9p->cwp != win) { | |
3755 | v9p->cwp = win; | |
3756 | sparcv9_active_window(sp, v9p->cwp); | |
3757 | } | |
3758 | ||
3759 | gl = (val >> V9_TSTATE_GL_SHIFT) & V9_TSTATE_GL_MASK; | |
3760 | switch(v9p->state) { | |
3761 | default: | |
3762 | case V9_User: abort(); /* shouldn't get here */ | |
3763 | case V9_Priv: | |
3764 | if (gl > Q_MAXPGL) { | |
3765 | EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl, Q_MAXPGL)); | |
3766 | gl = Q_MAXPGL; | |
3767 | } | |
3768 | break; | |
3769 | case V9_HyperPriv: | |
3770 | case V9_RED: | |
3771 | if (gl >= v9p->nglobals) { | |
3772 | EXEC_WARNING(("Restoring gl with value too large from TSTATE (%u) ; saturating to %u", gl, v9p->nglobals-1)); | |
3773 | gl = v9p->nglobals-1; | |
3774 | } | |
3775 | break; | |
3776 | } | |
3777 | if (v9p->gl != gl) { | |
3778 | v9p->gl = gl; | |
3779 | sparcv9_active_globals(sp, v9p->gl); | |
3780 | } | |
3781 | ||
3782 | ss_write_pstate( sp, (val >> V9_TSTATE_PSTATE_SHIFT) & V9_TSTATE_PSTATE_MASK ); | |
3783 | ||
3784 | #ifdef NIAGARA1 | |
3785 | /* FIXME: care not to overwrite the ENB bit in HPSTATE if we ever implement it */ | |
3786 | #endif /* NIAGARA1 */ | |
3787 | ss_write_hpstate( sp, true, N_HTSTATE(v9p, v9p->tl) ); | |
3788 | ||
3789 | v9p->tl --; | |
3790 | xcache_set_tagstate(sp); | |
3791 | } | |
3792 | ||
3793 | ||
3794 | /*************************************************************/ | |
3795 | ||
3796 | /* | |
3797 | * jpriv handler | |
3798 | */ | |
3799 | ||
3800 | void ss_jpriv(simcpu_t * sp, tvaddr_t addr) | |
3801 | { | |
3802 | sparcv9_cpu_t * v9p; | |
3803 | ss_strand_t * nsp; | |
3804 | ||
3805 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3806 | nsp = v9p->impl_specificp; | |
3807 | ||
3808 | if (v9p->state != V9_HyperPriv) { | |
3809 | v9p->post_precise_trap(sp, Sparcv9_trap_illegal_instruction); | |
3810 | return; | |
3811 | } | |
3812 | ||
3813 | if ((addr & 3) != 0) { | |
3814 | v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned); | |
3815 | return; | |
3816 | } | |
3817 | ||
3818 | /* setup the new pc and npc */ | |
3819 | sp->pc = addr; | |
3820 | sp->npc = addr + 4; | |
3821 | ||
3822 | /* clear HPSTATE.hpriv */ | |
3823 | v9p->hpstate.hpriv = false; | |
3824 | ||
3825 | /* set PSTATE.priv */ | |
3826 | v9p->pstate.priv = true; | |
3827 | ||
3828 | V9_STATE_CHANGE(sp, v9p, V9_Priv); | |
3829 | sp->xicache_trans_flush_pending = true; | |
3830 | sp->xdcache_trans_flush_pending = true; | |
3831 | ss_check_interrupts(sp); /* because of state change */ | |
3832 | ||
3833 | /* ROCK PRM 1.2 (wip) does not specify PSTATE.AM behavior for jpriv */ | |
3834 | } | |
3835 | ||
3836 | ||
3837 | /*************************************************************/ | |
3838 | ||
3839 | ||
3840 | /* | |
3841 | * Exception handling - very processor specific ... | |
3842 | */ | |
3843 | ||
3844 | ||
3845 | ||
3846 | ||
3847 | /* | |
3848 | * This function supports all synchronous and asynchronous traps | |
3849 | * The result is to leave the cpu in the correct state ready | |
3850 | * for trap execution. | |
3851 | * This model supports only the new v9 trap gl globals | |
3852 | * and of course the privileged, hyperpriv trap modes. | |
3853 | */ | |
3854 | ||
3855 | ||
3856 | ||
3857 | void ss_take_exception(simcpu_t * sp) | |
3858 | { | |
3859 | sparcv9_cpu_t * v9p; | |
3860 | sparcv9_state_t new_state; | |
3861 | ss_strand_t * nsp; | |
3862 | ss_proc_t * npp; | |
3863 | ss_trap_type_t RED_tt = 0; | |
3864 | bool_t prev_state; | |
3865 | uint_t tl, oldtl; | |
3866 | ss_trap_type_t tt; /* the trap we deliver */ | |
3867 | ss_trap_type_t actual_tt; /* the trap that occurred */ | |
3868 | tvaddr_t new_trap_pc; | |
3869 | ||
3870 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
3871 | nsp = v9p->impl_specificp; | |
3872 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
3873 | ||
3874 | ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]); | |
3875 | ||
3876 | /* | |
3877 | * OK, let's figure out which trap we need to take - | |
3878 | * based on the list that is pending, and the | |
3879 | * outstanding disrupting trap events. | |
3880 | */ | |
3881 | ||
3882 | actual_tt = nsp->pending_precise_tt; | |
3883 | ||
3884 | /* | |
3885 | * anything precise will get generated by re-execution of the | |
3886 | * same code sequence - so clear immediately | |
3887 | */ | |
3888 | nsp->pending_precise_tt = SS_trap_NONE; | |
3889 | ||
3890 | if (SS_trap_NONE != nsp->pending_async_tt) { | |
3891 | ||
3892 | tt = nsp->pending_async_tt; | |
3893 | ||
3894 | /* NB larger priority number = lower priority */ | |
3895 | if ( SS_trap_NONE == actual_tt || ss_trap_list[actual_tt].priority > ss_trap_list[tt].priority) { | |
3896 | actual_tt = tt; | |
3897 | ||
3898 | /* Only clear if we already detected it was not clear | |
3899 | * and we took the trap | |
3900 | */ | |
3901 | nsp->pending_async_tt = SS_trap_NONE; | |
3902 | } | |
3903 | } | |
3904 | ||
3905 | /* More tests here - FIXME */ | |
3906 | ||
3907 | /* | |
3908 | * Clear the global pending notice .. if nothing left pending. | |
3909 | * | |
3910 | * Note this could be simpler since above pending_precise_tt | |
3911 | * is always set to SS_trap_NONE and pending_async_tt is | |
3912 | * aleady being tested for SS_trap_NONE. | |
3913 | */ | |
3914 | ||
3915 | if (SS_trap_NONE == nsp->pending_async_tt && | |
3916 | SS_trap_NONE == nsp->pending_precise_tt) { | |
3917 | ||
3918 | sp->exception_pending = false; | |
3919 | } | |
3920 | ||
3921 | /* Bail if we found nothing to do ! error ? - FIXME */ | |
3922 | if (actual_tt == SS_trap_NONE) { | |
3923 | DBGE( lprintf(sp->gid, "No exception?\n"); ); | |
3924 | return; | |
3925 | } | |
3926 | ||
3927 | ||
3928 | DBGE( if (v9p->tl != v9p->gl) { | |
3929 | lprintf(sp->gid, "tl != gl: exception 0x%02x : %s : tl=%u : gl=%u : pc=0x%llx\n", | |
3930 | actual_tt, ss_trap_list[actual_tt].trap_namep, | |
3931 | v9p->tl, v9p->gl, sp->pc); | |
3932 | } ); | |
3933 | ||
3934 | #if RELINQUISH_QUANTUM_HACKS /* { */ | |
3935 | if (actual_tt == SS_trap_htrap_instruction && | |
3936 | sp->etp->nsimcpus > 1 && | |
3937 | sp->intreg[Reg_sparcv9_o5] == 0x12) { | |
3938 | /* CPU yield hcall - yield this CPU's quantum */ | |
3939 | set_sync_pending(sp); | |
3940 | } | |
3941 | #endif /* } */ | |
3942 | ||
3943 | DBGE( lprintf(sp->gid, "Taking exception 0x%02x : %s : tl=%u : gl=%u : pc=0x%llx, npc=0x%llx\n", | |
3944 | actual_tt, ss_trap_list[actual_tt].trap_namep, | |
3945 | v9p->tl, v9p->gl, sp->pc, sp->npc); ); | |
3946 | DBGHC( if (actual_tt >= SS_trap_htrap_instruction && v9p->state == V9_Priv) | |
3947 | if (actual_tt == SS_trap_htrap_instruction) | |
3948 | lprintf(sp->gid, "hcall fast:" | |
3949 | " (%%o5=0x%llx)(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx)" | |
3950 | " [pc=0x%llx %%o6=0x%llx %%o7=0x%llx]\n", | |
3951 | sp->intreg[Reg_sparcv9_o5], | |
3952 | sp->intreg[Reg_sparcv9_o0], | |
3953 | sp->intreg[Reg_sparcv9_o1], | |
3954 | sp->intreg[Reg_sparcv9_o2], | |
3955 | sp->intreg[Reg_sparcv9_o3], | |
3956 | sp->intreg[Reg_sparcv9_o4], | |
3957 | sp->pc, | |
3958 | sp->intreg[Reg_sparcv9_o6], | |
3959 | sp->intreg[Reg_sparcv9_o7]); | |
3960 | else | |
3961 | lprintf(sp->gid, "hcall hyper-fast:" | |
3962 | " (TT=0x%03x)" | |
3963 | "(0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx, 0x%llx)" | |
3964 | " [pc=0x%llx %%o6=0x%llx %%o7=0x%llx]\n", | |
3965 | actual_tt, | |
3966 | sp->intreg[Reg_sparcv9_o0], | |
3967 | sp->intreg[Reg_sparcv9_o1], | |
3968 | sp->intreg[Reg_sparcv9_o2], | |
3969 | sp->intreg[Reg_sparcv9_o3], | |
3970 | sp->intreg[Reg_sparcv9_o4], | |
3971 | sp->intreg[Reg_sparcv9_o5], | |
3972 | sp->pc, | |
3973 | sp->intreg[Reg_sparcv9_o6], | |
3974 | sp->intreg[Reg_sparcv9_o7]); ); | |
3975 | ||
3976 | ||
3977 | switch (actual_tt) { | |
3978 | ||
3979 | /* | |
3980 | * We special case the early reset traps that get special | |
3981 | * behaviour and need to use the reset trap vector. | |
3982 | */ | |
3983 | case SS_trap_power_on_reset: | |
3984 | case SS_trap_watchdog_reset: | |
3985 | case SS_trap_externally_initiated_reset: | |
3986 | case SS_trap_software_initiated_reset: | |
3987 | #if defined(NIAGARA1) || defined(NIAGARA2) | |
3988 | case SS_trap_RED_state_exception: | |
3989 | #endif | |
3990 | ss_reset_trap(sp, actual_tt); | |
3991 | return; | |
3992 | ||
3993 | #ifdef NIAGARA2 | |
3994 | case N2_trap_control_word_queue_interrupt: | |
3995 | case N2_trap_modular_arithmetic_interrupt: | |
3996 | break; | |
3997 | ||
3998 | /* | |
3999 | * FIXME: for now assert on traps that are not implemented yet | |
4000 | */ | |
4001 | case SS_trap_control_transfer_instruction: | |
4002 | case SS_trap_instruction_VA_watchpoint: | |
4003 | case SS_trap_instruction_breakpoint: | |
4004 | EXEC_WARNING(("Legion should not generate an unimplemented trap (TT=0x%x)",actual_tt)); | |
4005 | ASSERT(0); | |
4006 | #endif | |
4007 | ||
4008 | #ifdef ROCK | |
4009 | case RK_trap_hyperprivileged_queue_0: | |
4010 | case RK_trap_hyperprivileged_queue_1: | |
4011 | #endif | |
4012 | case SS_trap_cpu_mondo_trap: | |
4013 | case SS_trap_dev_mondo_trap: | |
4014 | case SS_trap_resumable_error: | |
4015 | DBGMONDO( lprintf(sp->gid, "Mondo trap 0x%02x : %s : " | |
4016 | "pc=0x%llx, npc=0x%llx\n", | |
4017 | actual_tt, ss_trap_list[actual_tt].trap_namep, | |
4018 | sp->pc, sp->npc); ); | |
4019 | break; | |
4020 | ||
4021 | default: | |
4022 | break; | |
4023 | } | |
4024 | ||
4025 | ||
4026 | ||
4027 | /* save the state of the | |
4028 | * strand as the trap occurred. | |
4029 | */ | |
4030 | ||
4031 | oldtl = v9p->tl; | |
4032 | ASSERT(oldtl <= v9p->maxtl); | |
4033 | ||
4034 | if (oldtl == v9p->maxtl) { | |
4035 | RED_tt = SS_trap_watchdog_reset; | |
4036 | } else { | |
4037 | v9p->tl++; | |
4038 | if (v9p->tl == v9p->maxtl) | |
4039 | RED_tt = SS_trap_RED_state_exception; | |
4040 | } | |
4041 | tl = v9p->tl; | |
4042 | ||
4043 | ASSERT(tl!=0); | |
4044 | ||
4045 | if (v9p->pstate.addr_mask) { | |
4046 | N_TPC( v9p, tl ) = sp->pc & MASK64(31,0); | |
4047 | N_TNPC( v9p, tl ) = sp->npc & MASK64(31,0); | |
4048 | } else { | |
4049 | N_TPC( v9p, tl ) = sp->pc; | |
4050 | N_TNPC( v9p, tl ) = sp->npc; | |
4051 | } | |
4052 | ||
4053 | ASSERT(actual_tt<SS_trap_illegal_value); | |
4054 | N_TT( v9p, tl ) = actual_tt; | |
4055 | ||
4056 | /* assemble tstate */ | |
4057 | N_TSTATE( v9p, tl ) = | |
4058 | ((v9p->gl & V9_TSTATE_GL_MASK)<<V9_TSTATE_GL_SHIFT) | | |
4059 | ((sp->v9_ccr & V9_TSTATE_CCR_MASK)<<V9_TSTATE_CCR_SHIFT) | | |
4060 | ((sp->v9_asi & V9_TSTATE_ASI_MASK)<<V9_TSTATE_ASI_SHIFT) | | |
4061 | (ss_read_pstate(v9p)<<V9_TSTATE_PSTATE_SHIFT) | | |
4062 | ((v9p->cwp & V9_TSTATE_CWP_MASK)<<V9_TSTATE_CWP_SHIFT); | |
4063 | ||
4064 | N_HTSTATE( v9p, tl ) = ss_read_hpstate(v9p); | |
4065 | ||
4066 | DBGTSTACK( | |
4067 | log_lock(); | |
4068 | log_printf(sp->gid, "Precise trap\n"); | |
4069 | sparcv9_dump_state(sp); | |
4070 | log_unlock(); ); | |
4071 | ||
4072 | /* Now for window traps there are some window reg adjustments | |
4073 | * to be made here ... | |
4074 | */ | |
4075 | ||
4076 | /* Modify cwp for window traps. */ | |
4077 | /* Even for hypervisor versions ? */ | |
4078 | ||
4079 | if (actual_tt == SS_trap_clean_window) { | |
4080 | /* Increment cwp */ | |
4081 | v9p->cwp = INC_MOD(v9p->cwp, v9p->nwins); | |
4082 | sparcv9_active_window(sp, v9p->cwp); | |
4083 | } else | |
4084 | if ( actual_tt>= SS_trap_spill_0_normal && actual_tt< (SS_trap_spill_7_other+3)) { | |
4085 | /* Increment cwp by 2+CANSAVE */ | |
4086 | v9p->cwp = (v9p->cwp + 2 + v9p->cansave) % v9p->nwins; | |
4087 | sparcv9_active_window(sp, v9p->cwp); | |
4088 | } else | |
4089 | if ( actual_tt>= SS_trap_fill_0_normal && actual_tt<=(SS_trap_fill_7_other+3)) { | |
4090 | /* Decrement cwp */ | |
4091 | v9p->cwp = (v9p->cwp + v9p->nwins-1) % v9p->nwins; | |
4092 | sparcv9_active_window(sp, v9p->cwp); | |
4093 | } | |
4094 | ||
4095 | tt = actual_tt; /* common case */ | |
4096 | ||
4097 | /* | |
4098 | * First step is to handle all those operation common to trap setup | |
4099 | */ | |
4100 | ||
4101 | prev_state = v9p->fpu_on; | |
4102 | v9p->pstate.fpu_enabled = v9p->has_fpu; | |
4103 | v9p->fpu_on = (v9p->fprs.fef && v9p->has_fpu); | |
4104 | #ifdef FP_DECODE_DISABLED | |
4105 | /* flush decoded instns if behaviour of FP instns is to change */ | |
4106 | if (v9p->fpu_on != prev_state) { | |
4107 | sp->xicache_instn_flush_pending = true; | |
4108 | set_sync_pending(sp); | |
4109 | } | |
4110 | #endif /* FP_DECODE_DISABLED */ | |
4111 | ||
4112 | v9p->pstate.addr_mask = false; | |
4113 | V9_PSTATE_AM_CHANGED(v9p); | |
4114 | ||
4115 | ||
4116 | ||
4117 | /* OK, based on the trap type, and a few other | |
4118 | * pieces of info ... like what state we're in | |
4119 | * - which state are we headed to ... | |
4120 | * .. RED_tt is set if we saturated TL | |
4121 | */ | |
4122 | ||
4123 | switch( v9p->state ) { | |
4124 | default: | |
4125 | break; | |
4126 | case V9_User: | |
4127 | switch ( ss_trap_list[actual_tt].from_user) { | |
4128 | case TFlag_Not_Poss: | |
4129 | fatal("Hardware should not be able to generate TT=0x%x from user mode", actual_tt); | |
4130 | case TFlag_Priv: | |
4131 | if (tl<=Q_MAXPTL) goto priv_trap_setup; | |
4132 | goto hyperpriv_trap_setup; | |
4133 | case TFlag_HypPriv: | |
4134 | goto hyperpriv_trap_setup; | |
4135 | default: abort(); | |
4136 | } | |
4137 | case V9_Priv: | |
4138 | switch ( ss_trap_list[actual_tt].from_priv) { | |
4139 | case TFlag_Not_Poss: | |
4140 | fatal("Hardware should not be able to generate TT=0x%x from privileged mode", actual_tt); | |
4141 | case TFlag_Priv: | |
4142 | if (tl<=Q_MAXPTL) goto priv_trap_setup; | |
4143 | goto hyperpriv_trap_setup; | |
4144 | case TFlag_HypPriv: | |
4145 | goto hyperpriv_trap_setup; | |
4146 | default: abort(); | |
4147 | } | |
4148 | case V9_HyperPriv: | |
4149 | switch ( ss_trap_list[actual_tt].from_hyperpriv) { | |
4150 | case TFlag_Not_Poss: | |
4151 | fatal("Hardware should not be able to generate TT=0x%x from hyper-privileged mode", actual_tt); | |
4152 | case TFlag_Priv: | |
4153 | fatal("Hardware cant generate a trap 0x%x to priv mode from hyper-priv mode", actual_tt); | |
4154 | case TFlag_HypPriv: | |
4155 | if (RED_tt != 0) goto REDstate_trap_setup; | |
4156 | goto hyperpriv_trap_setup; | |
4157 | default: abort(); | |
4158 | } | |
4159 | case V9_RED: | |
4160 | if (RED_tt == 0) | |
4161 | RED_tt = SS_trap_RED_state_exception; | |
4162 | if (v9p->had_RED_trap) { | |
4163 | fatal("Caught trap type 0x%x (%s) at pc=0x%llx while " | |
4164 | "in RED state - aborting simulation", | |
4165 | actual_tt, ss_trap_list[actual_tt].trap_namep, | |
4166 | sp->pc); | |
4167 | } | |
4168 | v9p->had_RED_trap = true; | |
4169 | goto REDstate_trap_setup; | |
4170 | } | |
4171 | abort(); /* internal error if we get here ! */ | |
4172 | ||
4173 | ||
4174 | ||
4175 | /* | |
4176 | * OK setup for trap into privileged mode | |
4177 | */ | |
4178 | priv_trap_setup:; | |
4179 | ||
4180 | new_trap_pc = v9p->tba | (oldtl != 0 ? (1<<14) : 0) | (tt << 5); | |
4181 | ||
4182 | v9p->pstate.priv = true; | |
4183 | v9p->pstate.int_enabled = false; /* pstate.ie = 0 */ | |
4184 | prev_state = v9p->pstate.cle; | |
4185 | v9p->pstate.cle = v9p->pstate.tle; | |
4186 | if (v9p->pstate.cle != prev_state) | |
4187 | sp->xdcache_trans_flush_pending = true; | |
4188 | ||
4189 | /* hpstate unchanged */ | |
4190 | ||
4191 | /* adjust and saturate GL appropriately */ | |
4192 | ASSERT( v9p->gl <= Q_MAXPGL ); /* internal error if otherwise */ | |
4193 | if (v9p->gl < Q_MAXPGL) { | |
4194 | v9p->gl++; | |
4195 | } else { | |
4196 | EXEC_WARNING(("Taking privileged trap with gl value too large (%u) ; saturating to %u (tl=%u tpc=0x%llx, tt=0x%x, actual_tt=0x%x)", v9p->gl, Q_MAXPGL, v9p->tl, N_TPC(v9p, tl), tt, actual_tt)); | |
4197 | } | |
4198 | sparcv9_active_globals(sp, v9p->gl); | |
4199 | ||
4200 | new_state = V9_Priv; | |
4201 | goto take_trap; | |
4202 | ||
4203 | /* | |
4204 | * OK setup for trap into hyper-privileged mode | |
4205 | */ | |
4206 | hyperpriv_trap_setup:; | |
4207 | ||
4208 | new_trap_pc = v9p->htba | (tt << 5); | |
4209 | ||
4210 | /* v9p->pstate.priv unchanged */ | |
4211 | /* v9p->pstate.mm unchanged */ | |
4212 | v9p->pstate.int_enabled = false; /* pstate.ie = 0 */ | |
4213 | prev_state = v9p->pstate.cle; | |
4214 | v9p->pstate.cle = false; /* always big endian */ | |
4215 | if (v9p->pstate.cle != prev_state) | |
4216 | sp->xdcache_trans_flush_pending = true; | |
4217 | ||
4218 | v9p->hpstate.red = false; | |
4219 | #ifndef NIAGARA2 | |
4220 | v9p->hpstate.tlz = false; | |
4221 | #endif | |
4222 | v9p->hpstate.hpriv = true; | |
4223 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
4224 | v9p->hpstate.ibe = false; /* not supported ? FIXME */ | |
4225 | #endif /* } */ | |
4226 | ||
4227 | ASSERT( v9p->gl <= (v9p->nglobals-1) ); | |
4228 | if (v9p->gl < (v9p->nglobals-1)) { | |
4229 | v9p->gl ++; | |
4230 | } else { | |
4231 | EXEC_WARNING(("Taking hyper-priv trap with gl value too large (%u) ; saturating to %u", v9p->gl, v9p->nglobals-1)); | |
4232 | } | |
4233 | sparcv9_active_globals(sp, v9p->gl); | |
4234 | ||
4235 | new_state = V9_HyperPriv; | |
4236 | goto take_trap; | |
4237 | ||
4238 | ||
4239 | REDstate_trap_setup:; | |
4240 | EXEC_WARNING(("Entering RED at pc=0x%llx", sp->pc)); | |
4241 | ||
4242 | new_trap_pc = npp->rstv_addr | (RED_tt << 5); | |
4243 | ||
4244 | /* v9p->pstate.priv unchanged */ | |
4245 | v9p->pstate.int_enabled = false; /* pstate.ie = 0 */ | |
4246 | prev_state = v9p->pstate.cle; | |
4247 | v9p->pstate.cle = false; /* always big endian */ | |
4248 | if (v9p->pstate.cle != prev_state) | |
4249 | sp->xdcache_trans_flush_pending = true; | |
4250 | v9p->pstate.mm = v9_mm_tso; /* as per UltraSPARC 2006 sec 12.6.2 */ | |
4251 | ||
4252 | v9p->hpstate.red = true; | |
4253 | v9p->hpstate.tlz = false; | |
4254 | v9p->hpstate.hpriv = true; | |
4255 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
4256 | v9p->hpstate.ibe = false; /* not supported ? FIXME */ | |
4257 | #endif /* } */ | |
4258 | ||
4259 | ASSERT( v9p->gl <= (v9p->nglobals-1) ); | |
4260 | if (v9p->gl < (v9p->nglobals-1)) { | |
4261 | v9p->gl ++; | |
4262 | } else { | |
4263 | EXEC_WARNING(("entering RED state with gl value too large (%u) ; saturating to %u", v9p->gl, v9p->nglobals-1)); | |
4264 | } | |
4265 | sparcv9_active_globals(sp, v9p->gl); | |
4266 | ||
4267 | new_state = V9_RED; | |
4268 | goto take_trap; | |
4269 | ||
4270 | ||
4271 | ||
4272 | take_trap:; | |
4273 | DBGTSTACK(lprintf(sp->gid, "Trap new pc=0x%llx\n", new_trap_pc);); | |
4274 | /* V9_PSTATE_CHANGED(v9p); V9_HPSTATE_CHANGED(sp, v9p); */ | |
4275 | if (v9p->state != new_state) { | |
4276 | V9_STATE_CHANGE(sp, v9p, new_state); | |
4277 | /* V9_STATE_CHANGE includes xcache_set_tagstate() */ | |
4278 | } else { | |
4279 | xcache_set_tagstate(sp); | |
4280 | } | |
4281 | ||
4282 | if (actual_tt == SS_trap_legion_save_state) { | |
4283 | new_trap_pc = options.save_restore.trap_pc; | |
4284 | log_printf(sp->gid, | |
4285 | "ss_take_exception : SS_trap_legion_save_state : divert to %%pc 0x%llx\n", | |
4286 | new_trap_pc); | |
4287 | } | |
4288 | sp->pc = new_trap_pc; | |
4289 | sp->npc = new_trap_pc+4; | |
4290 | ||
4291 | #if ERROR_TRAP_GEN /* { */ | |
4292 | ss_error_taking_trap(sp, (sparcv9_trap_type_t)tt); | |
4293 | #endif /* } ERROR_TRAP_GEN */ | |
4294 | ||
4295 | ss_check_interrupts(sp); /* because of state change */ | |
4296 | } | |
4297 | ||
4298 | ||
4299 | ||
4300 | ||
4301 | /* | |
4302 | * These reset traps always go to the hypervisor | |
4303 | * and always use the reset trap vector | |
4304 | */ | |
4305 | ||
4306 | ||
4307 | void ss_reset_trap(simcpu_t * sp, ss_trap_type_t tt) | |
4308 | { | |
4309 | sparcv9_cpu_t * v9p; | |
4310 | ss_strand_t * nsp; | |
4311 | ss_proc_t * npp; | |
4312 | uint_t tl; | |
4313 | bool_t prev_state; | |
4314 | ||
4315 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
4316 | nsp = v9p->impl_specificp; | |
4317 | ||
4318 | npp = (ss_proc_t *)(sp->config_procp->procp); | |
4319 | ||
4320 | switch (tt) { | |
4321 | case SS_trap_power_on_reset: | |
4322 | v9p->tl = v9p->maxtl; | |
4323 | v9p->tt[v9p->tl] = SS_trap_power_on_reset; | |
4324 | ||
4325 | v9p->gl = v9p->nglobals - 1; | |
4326 | ||
4327 | v9p->pstate.mm = v9_mm_tso; /* TSO */ | |
4328 | v9p->pstate.fpu_enabled = v9p->has_fpu; | |
4329 | v9p->pstate.addr_mask = false; | |
4330 | V9_PSTATE_AM_CHANGED(v9p); | |
4331 | ||
4332 | v9p->pstate.priv= true; | |
4333 | v9p->pstate.int_enabled = false; | |
4334 | v9p->pstate.cle = false; | |
4335 | v9p->pstate.tle = false; | |
4336 | ||
4337 | v9p->hpstate.red= true; | |
4338 | v9p->hpstate.tlz= false; | |
4339 | v9p->hpstate.hpriv= true; | |
4340 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
4341 | v9p->hpstate.ibe= false; | |
4342 | #endif /* } */ | |
4343 | ||
4344 | v9p->softint = 0; | |
4345 | ||
4346 | V9_STATE_CHANGE(sp, v9p, V9_RED); | |
4347 | ||
4348 | /* stuff for tick interrupts */ | |
4349 | v9p->_tick.non_priv_trap = true; /* tick.npt = 1; */ | |
4350 | v9p->tick->offset = 0LL; | |
4351 | ||
4352 | v9p->_stick.non_priv_trap = true; /* stick.npt = 1; */ | |
4353 | v9p->stick->offset = 0LL; | |
4354 | ||
4355 | ||
4356 | #define SS_TICK_CMPR_RESET (1ULL << 63) | |
4357 | ss_tick_cmpr_write(sp, &v9p->tick_cmpr, SS_TICK_CMPR_RESET); | |
4358 | ss_tick_cmpr_write(sp, &v9p->stick_cmpr, SS_TICK_CMPR_RESET); | |
4359 | ss_tick_cmpr_write(sp, &v9p->hstick_cmpr, SS_TICK_CMPR_RESET); | |
4360 | ss_recomp_cycle_target(sp); | |
4361 | ||
4362 | break; | |
4363 | ||
4364 | case SS_trap_externally_initiated_reset: | |
4365 | tl = v9p->tl+1; | |
4366 | if (tl>v9p->maxtl) tl = v9p->maxtl; | |
4367 | v9p->tl = tl; | |
4368 | ||
4369 | N_TPC( v9p, tl ) = sp->pc; | |
4370 | N_TNPC( v9p, tl ) = sp->npc; | |
4371 | ||
4372 | N_TT( v9p, tl ) = tt; | |
4373 | ||
4374 | /* assemble tstate */ | |
4375 | N_TSTATE( v9p, tl ) = | |
4376 | ((v9p->gl & V9_TSTATE_GL_MASK)<<V9_TSTATE_GL_SHIFT) | | |
4377 | ((sp->v9_ccr & V9_TSTATE_CCR_MASK)<<V9_TSTATE_CCR_SHIFT) | | |
4378 | ((sp->v9_asi & V9_TSTATE_ASI_MASK)<<V9_TSTATE_ASI_SHIFT) | | |
4379 | (ss_read_pstate(v9p)<<V9_TSTATE_PSTATE_SHIFT) | | |
4380 | ((v9p->cwp & V9_TSTATE_CWP_MASK)<<V9_TSTATE_CWP_SHIFT); | |
4381 | ||
4382 | N_HTSTATE( v9p, tl ) = ss_read_hpstate(v9p); | |
4383 | ||
4384 | /* Entering RED state gl can saturate at nglobals-1 */ | |
4385 | if (v9p->gl < (v9p->nglobals-1)) v9p->gl ++; | |
4386 | /* sparcv9_active_globals selected at function exit */ | |
4387 | ||
4388 | v9p->pstate.mm = v9_mm_tso; /* TSO */ | |
4389 | v9p->pstate.fpu_enabled = v9p->has_fpu; | |
4390 | v9p->pstate.addr_mask = false; | |
4391 | V9_PSTATE_AM_CHANGED(v9p); | |
4392 | ||
4393 | v9p->pstate.priv= true; | |
4394 | v9p->pstate.int_enabled = false; | |
4395 | prev_state = v9p->pstate.cle; | |
4396 | v9p->pstate.cle = v9p->pstate.tle; | |
4397 | if (v9p->pstate.cle != prev_state) | |
4398 | sp->xdcache_trans_flush_pending = true; | |
4399 | ||
4400 | v9p->hpstate.red= true; | |
4401 | v9p->hpstate.tlz= false; | |
4402 | v9p->hpstate.hpriv= true; | |
4403 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
4404 | v9p->hpstate.ibe= false; | |
4405 | #endif /* } */ | |
4406 | ||
4407 | /* v9p->softint = 0; FIXME: this what happens on Niagara ? */ | |
4408 | ||
4409 | V9_STATE_CHANGE(sp, v9p, V9_RED); | |
4410 | ||
4411 | break; | |
4412 | ||
4413 | ||
4414 | #if 0 /* { */ | |
4415 | -- case T_WATCHDOG_RESET: | |
4416 | -- if (++sp->trap_level > sp->max_trap_level) { | |
4417 | -- sp->trap_level = sp->max_trap_level; | |
4418 | -- } | |
4419 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4420 | -- | |
4421 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4422 | -- | |
4423 | -- /* | |
4424 | -- * After saving state increment CWP .This is implementation | |
4425 | -- * specific to spitfire. Bug:1194954 | |
4426 | -- */ | |
4427 | -- | |
4428 | -- if(from_error_mode){ | |
4429 | -- /* | |
4430 | -- * Modify cwp for window traps. | |
4431 | -- * on entering error mode. | |
4432 | -- */ | |
4433 | -- if (error_tt == T_CLEAN_WINDOW) { | |
4434 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4435 | -- if (P_CWP != sp->current_window) | |
4436 | -- update_cur_win_regs(sp); | |
4437 | -- } else if (is_spill_trap(error_tt)) { | |
4438 | -- /* Increment CWP by 2 + CANSAVE. */ | |
4439 | -- int count = 2 + sp->cansave; | |
4440 | -- while (count--) { | |
4441 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4442 | -- } | |
4443 | -- if (P_CWP != sp->current_window) | |
4444 | -- update_cur_win_regs(sp); | |
4445 | -- } else if (is_fill_trap(error_tt)) { | |
4446 | -- /* Decrement CWP. */ | |
4447 | -- P_CWP= DEC_MOD_NWIN(P_CWP); | |
4448 | -- /* Update cur_win_regs[]. */ | |
4449 | -- if (P_CWP != sp->current_window) | |
4450 | -- update_cur_win_regs(sp); | |
4451 | -- } | |
4452 | -- } | |
4453 | -- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno; | |
4454 | -- | |
4455 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4456 | -- Old_val = sp->pstate.s.red; | |
4457 | -- sp->pstate.s.red = 1; | |
4458 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4459 | -- sp->pstate.s.pef = 1; | |
4460 | -- sp->pstate.s.am = 0; | |
4461 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4462 | -- sp->pstate.s.priv = 1; | |
4463 | -- sp->pstate.s.ie = 0; | |
4464 | -- sp->pstate.s.ag = 1; | |
4465 | -- sp->pstate.s.vg = 0; | |
4466 | -- sp->pstate.s.mg = 0; | |
4467 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4468 | -- | |
4469 | -- set_pc_for_red_mode_trap(sp, T_WATCHDOG_RESET); | |
4470 | -- break; | |
4471 | -- | |
4472 | -- | |
4473 | -- case T_SOFTWARE_RESET: | |
4474 | -- if (++sp->trap_level > sp->max_trap_level) { | |
4475 | -- sp->trap_level = sp->max_trap_level; | |
4476 | -- } | |
4477 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4478 | -- | |
4479 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4480 | -- | |
4481 | -- sp->trap_type[sp->trap_level] = T_SOFTWARE_RESET; | |
4482 | -- | |
4483 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4484 | -- Old_val = sp->pstate.s.red; | |
4485 | -- sp->pstate.s.red = 1; | |
4486 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4487 | -- sp->pstate.s.pef = 1; | |
4488 | -- sp->pstate.s.am = 0; | |
4489 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4490 | -- sp->pstate.s.priv = 1; | |
4491 | -- sp->pstate.s.ie = 0; | |
4492 | -- sp->pstate.s.ag = 1; | |
4493 | -- sp->pstate.s.vg = 0; | |
4494 | -- sp->pstate.s.mg = 0; | |
4495 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4496 | -- | |
4497 | -- set_pc_for_red_mode_trap(sp, T_SOFTWARE_RESET); | |
4498 | -- break; | |
4499 | #endif /* } */ | |
4500 | ||
4501 | default: | |
4502 | fatal("cpu: unknown reset trap 0x%x.\n", tt); | |
4503 | break; | |
4504 | } | |
4505 | ||
4506 | /* setup the mmu_bypass to reflect hpstate */ | |
4507 | /* V9_PSTATE_CHANGED(v9p); V9_HPSTATE_CHANGED(sp, v9p); */ | |
4508 | ||
4509 | sp->pc = npp->rstv_addr | (tt<<5); | |
4510 | sp->npc = sp->pc + 4; | |
4511 | ||
4512 | sparcv9_active_window(sp, v9p->cwp); | |
4513 | sparcv9_active_globals(sp, v9p->gl); | |
4514 | ||
4515 | v9p->fpu_on = (v9p->pstate.fpu_enabled && v9p->fprs.fef && v9p->has_fpu); | |
4516 | #ifdef FP_DECODE_DISABLED | |
4517 | sp->xicache_instn_flush_pending = true; /* for such things as fpu_on changed */ | |
4518 | set_sync_pending(sp); | |
4519 | #endif /* FP_DECODE_DISABLED */ | |
4520 | xcache_set_tagstate(sp); | |
4521 | ||
4522 | ss_check_interrupts(sp); /* because of state change */ | |
4523 | } | |
4524 | ||
4525 | static void ss_post_precise_trap(simcpu_t * sp, sparcv9_trap_type_t code) | |
4526 | { | |
4527 | sparcv9_cpu_t * v9p; | |
4528 | ss_strand_t * nsp; | |
4529 | ss_trap_type_t tt; /* the trap we deliver */ | |
4530 | ss_trap_type_t pendtt; /* the trap we deliver */ | |
4531 | ||
4532 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
4533 | nsp = v9p->impl_specificp; | |
4534 | ||
4535 | #ifdef ROCK | |
4536 | if (TM_ACTIVE(nsp)) { | |
4537 | switch((uint_t)code) { | |
4538 | /* Set of all disruptive traps specified in PRM (for rock) */ | |
4539 | case SS_trap_sw_recoverable_error: | |
4540 | case SS_trap_interrupt_level_1: | |
4541 | case SS_trap_interrupt_level_2: | |
4542 | case SS_trap_interrupt_level_3: | |
4543 | case SS_trap_interrupt_level_4: | |
4544 | case SS_trap_interrupt_level_5: | |
4545 | case SS_trap_interrupt_level_6: | |
4546 | case SS_trap_interrupt_level_7: | |
4547 | case SS_trap_interrupt_level_8: | |
4548 | case SS_trap_interrupt_level_9: | |
4549 | case SS_trap_interrupt_level_a: | |
4550 | case SS_trap_interrupt_level_b: | |
4551 | case SS_trap_interrupt_level_c: | |
4552 | case SS_trap_interrupt_level_d: | |
4553 | case SS_trap_interrupt_level_e: | |
4554 | case SS_trap_interrupt_level_f: | |
4555 | case SS_trap_hstick_match: | |
4556 | case SS_trap_trap_level_zero: | |
4557 | case SS_trap_hw_corrected_error: | |
4558 | case RK_trap_store_data_value: | |
4559 | case RK_trap_no_retire: | |
4560 | case RK_trap_SIU_inbound_exception: | |
4561 | case RK_trap_data_access_SIU_error: | |
4562 | case RK_trap_hyperprivileged_queue_0: | |
4563 | case RK_trap_hyperprivileged_queue_1: | |
4564 | case SS_trap_cpu_mondo_trap: | |
4565 | case SS_trap_dev_mondo_trap: | |
4566 | case SS_trap_resumable_error: | |
4567 | nsp->tm_fail(sp, V9_CPS_ASYNC); | |
4568 | break; /* Handle the trap since these are disruptive type */ | |
4569 | default: | |
4570 | nsp->tm_fail(sp, V9_CPS_PRECISE); | |
4571 | return; /* Return without handling the trap */ | |
4572 | ||
4573 | } | |
4574 | } | |
4575 | #endif | |
4576 | ||
4577 | tt = (ss_trap_type_t)code; | |
4578 | ||
4579 | /* | |
4580 | * post this trap if it has higher priority than the | |
4581 | * trap already in place | |
4582 | */ | |
4583 | ||
4584 | pendtt = nsp->pending_precise_tt; | |
4585 | ASSERT( ss_trap_list[tt].trap_namep != (char*)0 ); /* legit HW trap ? */ | |
4586 | ||
4587 | /* NB larger priority number = lower priority */ | |
4588 | if (pendtt==SS_trap_NONE || | |
4589 | ss_trap_list[pendtt].priority > ss_trap_list[tt].priority) { | |
4590 | ||
4591 | nsp->pending_precise_tt = tt; | |
4592 | sp->exception_pending = true; | |
4593 | } | |
4594 | } | |
4595 | ||
4596 | ||
4597 | #if 0 /* { */ | |
4598 | -- | |
4599 | -- /* Called to execute all SunSPARC synchronous and asynchronous traps. */ | |
4600 | -- void ss_execute_trap(FcpuT* sp, Word trno, const char* trap_str, | |
4601 | -- LWord trap_pc, LWord trap_npc, enum Trap_globals p_globals, bool_t sync_trap) | |
4602 | -- { | |
4603 | -- Word error_tt; | |
4604 | -- Bool from_error_mode = FALSE; | |
4605 | -- u_tba_addr tba_addr; | |
4606 | -- Byte Old_val; | |
4607 | -- | |
4608 | -- if (trno > MAX_TRAP_NUM) { | |
4609 | -- fatal("cpu: called with illegal trap number of 0x%x.\n", trno); | |
4610 | -- } | |
4611 | -- | |
4612 | -- | |
4613 | -- sp->trap_linkage_pending = TRUE; | |
4614 | -- | |
4615 | -- if (sp->trap_level == sp->max_trap_level) { | |
4616 | -- | |
4617 | -- /* ERROR mode. */ | |
4618 | -- /* If stop_on_reset flag is set, stop the simulation. */ | |
4619 | -- if (sp->stop_on_reset) { | |
4620 | -- /* | |
4621 | -- * Modify cwp for window traps. | |
4622 | -- * on entering error mode. | |
4623 | -- */ | |
4624 | -- if (trno == T_CLEAN_WINDOW) { | |
4625 | -- /* Increment CWP. */ | |
4626 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4627 | -- if (P_CWP != sp->current_window) | |
4628 | -- update_cur_win_regs(sp); | |
4629 | -- | |
4630 | -- } else if (is_spill_trap(trno)) { | |
4631 | -- /* Increment CWP by 2 + CANSAVE. */ | |
4632 | -- int count = 2 + sp->cansave; | |
4633 | -- while (count--) { | |
4634 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4635 | -- } | |
4636 | -- if (P_CWP != sp->current_window) | |
4637 | -- update_cur_win_regs(sp); | |
4638 | -- | |
4639 | -- } else if (is_fill_trap(trno)) { | |
4640 | -- /* Decrement CWP. */ | |
4641 | -- P_CWP= DEC_MOD_NWIN(P_CWP); | |
4642 | -- /* Update cur_win_regs[]. */ | |
4643 | -- if (P_CWP != sp->current_window) | |
4644 | -- update_cur_win_regs(sp); | |
4645 | -- } | |
4646 | -- | |
4647 | -- printf("cpu : error mode caused by trap 0x%x (%s)\n", trno, trap_str); | |
4648 | -- printf("\tAt time of trap: pc 0x%llx, npc 0x%llx\n", | |
4649 | -- (uint64_t)trap_pc, (uint64_t)trap_npc); | |
4650 | -- stop_simulation(sp->cmd_ptr); | |
4651 | -- return; | |
4652 | -- } | |
4653 | -- | |
4654 | -- /* | |
4655 | -- * Make the processor look like it halted and then was | |
4656 | -- * brought back to life by a watchdog reset. | |
4657 | -- */ | |
4658 | -- error_tt = trno; /* save trno for below */ | |
4659 | -- from_error_mode = TRUE; | |
4660 | -- | |
4661 | -- /* make processor think it received | |
4662 | -- * a watchdog reset | |
4663 | -- */ | |
4664 | -- trno = T_WATCHDOG_RESET; | |
4665 | -- } | |
4666 | -- | |
4667 | -- if (is_reset_trap(trno)) { | |
4668 | -- execute_reset_trap(sp, trno, from_error_mode, error_tt, trap_pc, trap_npc); | |
4669 | -- return; | |
4670 | -- } | |
4671 | -- | |
4672 | -- if (sp->trap_level == (sp->max_trap_level - 1) || sp->pstate.s.red) { | |
4673 | -- execute_red_mode_trap(sp, trno, trap_pc, trap_npc, p_globals); | |
4674 | -- return; | |
4675 | -- } | |
4676 | -- | |
4677 | -- /* | |
4678 | -- * "Normal" trap processing. | |
4679 | -- */ | |
4680 | -- | |
4681 | -- tba_addr.l = sp->tba.l; | |
4682 | -- tba_addr.s.in_trap = (sp->trap_level > 0); | |
4683 | -- sp->trap_level++; | |
4684 | -- | |
4685 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4686 | -- | |
4687 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4688 | -- | |
4689 | -- /* Modify cwp for window traps. */ | |
4690 | -- if (trno == T_CLEAN_WINDOW) { | |
4691 | -- | |
4692 | -- /* Increment CWP. */ | |
4693 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4694 | -- if (P_CWP != sp->current_window) | |
4695 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
4696 | -- | |
4697 | -- } else if (is_spill_trap(trno)) { | |
4698 | -- | |
4699 | -- /* Increment CWP by 2 + CANSAVE. */ | |
4700 | -- | |
4701 | -- uint32_t cwp = P_CWP; | |
4702 | -- | |
4703 | -- if (sp->cansave == 0) { | |
4704 | -- cwp = INC_MOD_NWIN(cwp); | |
4705 | -- P_CWP = INC_MOD_NWIN(cwp); | |
4706 | -- } | |
4707 | -- else { | |
4708 | -- int count = 2 + sp->cansave; | |
4709 | -- while (count--) { | |
4710 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4711 | -- } | |
4712 | -- } | |
4713 | -- | |
4714 | -- | |
4715 | -- if (P_CWP != sp->current_window) { | |
4716 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
4717 | -- } | |
4718 | -- | |
4719 | -- } else if (is_fill_trap(trno)) { | |
4720 | -- /* Decrement CWP. */ | |
4721 | -- P_CWP= DEC_MOD_NWIN(P_CWP); | |
4722 | -- | |
4723 | -- if (P_CWP != sp->current_window) { | |
4724 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
4725 | -- } | |
4726 | -- } | |
4727 | -- | |
4728 | -- Old_val = sp->pstate.s.red; | |
4729 | -- sp->pstate.s.red = 0; | |
4730 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4731 | -- sp->pstate.s.pef = 1; | |
4732 | -- sp->pstate.s.am = 0; | |
4733 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4734 | -- sp->pstate.s.priv = 1; | |
4735 | -- sp->pstate.s.ie = 0; | |
4736 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4737 | -- | |
4738 | -- /* Activate the appropriate globals. */ | |
4739 | -- switch (p_globals) { | |
4740 | -- case use_alternate_globals: | |
4741 | -- sp->pstate.s.ag = 1; | |
4742 | -- sp->pstate.s.vg = 0; | |
4743 | -- sp->pstate.s.mg = 0; | |
4744 | -- | |
4745 | -- update_global_regs_il (sp, use_alternate_globals + 1); | |
4746 | -- | |
4747 | -- break; | |
4748 | -- case use_mmu_globals: | |
4749 | -- sp->pstate.s.ag = 0; | |
4750 | -- sp->pstate.s.vg = 0; | |
4751 | -- sp->pstate.s.mg = 1; | |
4752 | -- | |
4753 | -- update_global_regs_il (sp, use_mmu_globals + 1); | |
4754 | -- | |
4755 | -- break; | |
4756 | -- case use_vector_globals: | |
4757 | -- sp->pstate.s.ag = 0; | |
4758 | -- sp->pstate.s.vg = 1; | |
4759 | -- sp->pstate.s.mg = 0; | |
4760 | -- | |
4761 | -- update_global_regs_il (sp, use_vector_globals + 1); | |
4762 | -- | |
4763 | -- break; | |
4764 | -- } | |
4765 | -- | |
4766 | -- /* SL - 05/09/2001 */ | |
4767 | -- /* repalced the call below by inline function to shorten trap handling */ | |
4768 | -- | |
4769 | -- | |
4770 | -- /* update_global_regs(sp, sp->pstate); */ | |
4771 | -- | |
4772 | -- | |
4773 | -- update_async_trap_pending(sp); | |
4774 | -- | |
4775 | -- #ifdef ECC | |
4776 | -- update_error_trap_pending(sp); | |
4777 | -- #endif | |
4778 | -- | |
4779 | -- FASTMEM_TL_PSTATE_CHANGE(sp); | |
4780 | -- | |
4781 | -- sp->trap_type[sp->trap_level] = trno; | |
4782 | -- | |
4783 | -- tba_addr.s.tt = sp->trap_type[sp->trap_level]; | |
4784 | -- | |
4785 | -- sp->pc = tba_addr.l; | |
4786 | -- sp->npc = tba_addr.l + 4; | |
4787 | -- sp->cti_executed = TRUE; | |
4788 | -- sp->cti_indx = TCC_T; | |
4789 | -- | |
4790 | -- #if 0 | |
4791 | -- { | |
4792 | -- Word tctxt; | |
4793 | -- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt); | |
4794 | -- sp->fm.current_ctxt = tctxt; | |
4795 | -- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) ); | |
4796 | -- } | |
4797 | -- #endif | |
4798 | -- | |
4799 | -- } | |
4800 | #endif /* } */ | |
4801 | ||
4802 | #if 0 /* { */ | |
4803 | -- | |
4804 | -- /**************************************************************/ | |
4805 | -- | |
4806 | -- | |
4807 | -- void | |
4808 | -- execute_reset_trap(FcpuT* sp, Word trno, Bool from_error_mode, | |
4809 | -- Word error_tt, LWord trap_pc, LWord trap_npc) | |
4810 | -- { | |
4811 | -- Byte Old_val; | |
4812 | -- | |
4813 | -- switch (trno) { | |
4814 | -- | |
4815 | -- case SS_trap_power_on_reset: | |
4816 | -- sp->trap_level = sp->max_trap_level; | |
4817 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4818 | -- sp->trap_type[sp->trap_level] = T_POWER_ON_RESET; | |
4819 | -- | |
4820 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4821 | -- Old_val = sp->pstate.s.red; | |
4822 | -- sp->pstate.s.red = 1; | |
4823 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4824 | -- sp->pstate.s.pef = 1; | |
4825 | -- sp->pstate.s.am = 0; | |
4826 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4827 | -- sp->pstate.s.priv = 1; | |
4828 | -- sp->pstate.s.ie = 0; | |
4829 | -- sp->pstate.s.ag = 1; | |
4830 | -- sp->pstate.s.vg = 0; | |
4831 | -- sp->pstate.s.mg = 0; | |
4832 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4833 | -- | |
4834 | -- set_pc_for_red_mode_trap(sp, T_POWER_ON_RESET); | |
4835 | -- | |
4836 | -- /* stuff for tick interrupts */ | |
4837 | -- sp->tick.nonpriv_trap = 1; /* tick.npt = 1; */ | |
4838 | -- sp->stick.nonpriv_trap = 1; /* stick.npt = 1; */ | |
4839 | -- sp->tick.interrupt_enabled = 0; /* tick_cmpr.int_dis = 1; */ | |
4840 | -- sp->stick.interrupt_enabled = 0; /* stick_cmpr.int_dis = 1; */ | |
4841 | -- | |
4842 | -- /* tick & stick scales created when cpu created ? */ | |
4843 | -- assert(sp->tick.scale != 0.0); | |
4844 | -- | |
4845 | -- ss_recompute_count_target(sp); /* changed tick & stick */ | |
4846 | -- | |
4847 | -- break; | |
4848 | -- | |
4849 | -- case T_WATCHDOG_RESET: | |
4850 | -- if (++sp->trap_level > sp->max_trap_level) { | |
4851 | -- sp->trap_level = sp->max_trap_level; | |
4852 | -- } | |
4853 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4854 | -- | |
4855 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4856 | -- | |
4857 | -- /* | |
4858 | -- * After saving state increment CWP .This is implementation | |
4859 | -- * specific to spitfire. Bug:1194954 | |
4860 | -- */ | |
4861 | -- | |
4862 | -- if(from_error_mode){ | |
4863 | -- /* | |
4864 | -- * Modify cwp for window traps. | |
4865 | -- * on entering error mode. | |
4866 | -- */ | |
4867 | -- if (error_tt == T_CLEAN_WINDOW) { | |
4868 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4869 | -- if (P_CWP != sp->current_window) | |
4870 | -- update_cur_win_regs(sp); | |
4871 | -- } else if (is_spill_trap(error_tt)) { | |
4872 | -- /* Increment CWP by 2 + CANSAVE. */ | |
4873 | -- int count = 2 + sp->cansave; | |
4874 | -- while (count--) { | |
4875 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
4876 | -- } | |
4877 | -- if (P_CWP != sp->current_window) | |
4878 | -- update_cur_win_regs(sp); | |
4879 | -- } else if (is_fill_trap(error_tt)) { | |
4880 | -- /* Decrement CWP. */ | |
4881 | -- P_CWP= DEC_MOD_NWIN(P_CWP); | |
4882 | -- /* Update cur_win_regs[]. */ | |
4883 | -- if (P_CWP != sp->current_window) | |
4884 | -- update_cur_win_regs(sp); | |
4885 | -- } | |
4886 | -- } | |
4887 | -- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno; | |
4888 | -- | |
4889 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4890 | -- Old_val = sp->pstate.s.red; | |
4891 | -- sp->pstate.s.red = 1; | |
4892 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4893 | -- sp->pstate.s.pef = 1; | |
4894 | -- sp->pstate.s.am = 0; | |
4895 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4896 | -- sp->pstate.s.priv = 1; | |
4897 | -- sp->pstate.s.ie = 0; | |
4898 | -- sp->pstate.s.ag = 1; | |
4899 | -- sp->pstate.s.vg = 0; | |
4900 | -- sp->pstate.s.mg = 0; | |
4901 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4902 | -- | |
4903 | -- set_pc_for_red_mode_trap(sp, T_WATCHDOG_RESET); | |
4904 | -- break; | |
4905 | -- | |
4906 | -- case T_EXTERNAL_RESET: | |
4907 | -- if (++sp->trap_level > sp->max_trap_level) { | |
4908 | -- sp->trap_level = sp->max_trap_level; | |
4909 | -- } | |
4910 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4911 | -- | |
4912 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4913 | -- | |
4914 | -- sp->trap_type[sp->trap_level] = from_error_mode ? error_tt: trno; | |
4915 | -- | |
4916 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4917 | -- Old_val = sp->pstate.s.red; | |
4918 | -- sp->pstate.s.red = 1; | |
4919 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4920 | -- sp->pstate.s.pef = 1; | |
4921 | -- sp->pstate.s.am = 0; | |
4922 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4923 | -- sp->pstate.s.priv = 1; | |
4924 | -- sp->pstate.s.ie = 0; | |
4925 | -- sp->pstate.s.ag = 1; | |
4926 | -- sp->pstate.s.vg = 0; | |
4927 | -- sp->pstate.s.mg = 0; | |
4928 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4929 | -- | |
4930 | -- set_pc_for_red_mode_trap(sp, T_EXTERNAL_RESET); | |
4931 | -- break; | |
4932 | -- | |
4933 | -- case T_SOFTWARE_RESET: | |
4934 | -- if (++sp->trap_level > sp->max_trap_level) { | |
4935 | -- sp->trap_level = sp->max_trap_level; | |
4936 | -- } | |
4937 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
4938 | -- | |
4939 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
4940 | -- | |
4941 | -- sp->trap_type[sp->trap_level] = T_SOFTWARE_RESET; | |
4942 | -- | |
4943 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
4944 | -- Old_val = sp->pstate.s.red; | |
4945 | -- sp->pstate.s.red = 1; | |
4946 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
4947 | -- sp->pstate.s.pef = 1; | |
4948 | -- sp->pstate.s.am = 0; | |
4949 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
4950 | -- sp->pstate.s.priv = 1; | |
4951 | -- sp->pstate.s.ie = 0; | |
4952 | -- sp->pstate.s.ag = 1; | |
4953 | -- sp->pstate.s.vg = 0; | |
4954 | -- sp->pstate.s.mg = 0; | |
4955 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
4956 | -- | |
4957 | -- set_pc_for_red_mode_trap(sp, T_SOFTWARE_RESET); | |
4958 | -- break; | |
4959 | -- | |
4960 | -- default: | |
4961 | -- fatal("cpu: unknown reset trap 0x%x.\n", trno); | |
4962 | -- break; | |
4963 | -- } | |
4964 | -- | |
4965 | -- update_global_regs(sp, sp->pstate); | |
4966 | -- update_async_trap_pending(sp); | |
4967 | -- | |
4968 | -- #ifdef ECC | |
4969 | -- update_error_trap_pending(sp); | |
4970 | -- #endif | |
4971 | -- | |
4972 | -- FASTMEM_TL_PSTATE_CHANGE(sp); | |
4973 | -- | |
4974 | -- sp->cti_executed = TRUE; | |
4975 | -- sp->cti_indx = TCC_T; | |
4976 | -- | |
4977 | -- /* setup for the fast memory stuff ... -ash */ | |
4978 | -- | |
4979 | -- | |
4980 | -- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */ | |
4981 | -- | |
4982 | -- | |
4983 | -- #if 0 | |
4984 | -- { Word tctxt; | |
4985 | -- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt); | |
4986 | -- sp->fm.current_ctxt = tctxt; | |
4987 | -- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) ); | |
4988 | -- } | |
4989 | -- #endif | |
4990 | -- | |
4991 | -- } | |
4992 | -- | |
4993 | -- /* | |
4994 | -- * Called when a trap occurs in RED mode or a trap puts the processor | |
4995 | -- * into RED mode (when trap_level is max_trap_level less 1). | |
4996 | -- */ | |
4997 | -- void | |
4998 | -- execute_red_mode_trap(FcpuT* sp, Word trno, LWord trap_pc, | |
4999 | -- LWord trap_npc, enum Trap_globals p_globals) | |
5000 | -- { | |
5001 | -- Byte Old_val; | |
5002 | -- | |
5003 | -- sp->trap_level ++; | |
5004 | -- TRACEMOD_TTEXT( ("TRAP_LEVEL %d",(int)sp->trap_level) ); | |
5005 | -- | |
5006 | -- save_trap_state(sp, sp->trap_level, trap_pc, trap_npc); | |
5007 | -- | |
5008 | -- sp->trap_type[sp->trap_level] = trno; | |
5009 | -- | |
5010 | -- /* Modify cwp for window traps. */ | |
5011 | -- if (trno == T_CLEAN_WINDOW) { | |
5012 | -- /* Increment CWP. */ | |
5013 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
5014 | -- if (P_CWP != sp->current_window) | |
5015 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
5016 | -- } else if (is_spill_trap(trno)) { | |
5017 | -- /* Increment CWP by 2 + CANSAVE. */ | |
5018 | -- int count = 2 + sp->cansave; | |
5019 | -- | |
5020 | -- while (count--) { | |
5021 | -- P_CWP = INC_MOD_NWIN(P_CWP); | |
5022 | -- } | |
5023 | -- if (P_CWP != sp->current_window) | |
5024 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
5025 | -- } else if (is_fill_trap(trno)) { | |
5026 | -- /* Decrement CWP. */ | |
5027 | -- P_CWP= DEC_MOD_NWIN(P_CWP); | |
5028 | -- if (P_CWP != sp->current_window) | |
5029 | -- update_cur_win_regs(sp); /* Update cur_win_regs[]. */ | |
5030 | -- } | |
5031 | -- sp->pstate.s.mm = 0x0; /* TSO */ | |
5032 | -- Old_val = sp->pstate.s.red; | |
5033 | -- sp->pstate.s.red = 1; | |
5034 | -- PSTATE_RED_CHANGED(sp->pstate.s.red); | |
5035 | -- sp->pstate.s.pef = 1; | |
5036 | -- sp->pstate.s.am = 0; | |
5037 | -- PSTATE_AM_CHANGED(sp->pstate.s.am); | |
5038 | -- sp->pstate.s.priv = 1; | |
5039 | -- sp->pstate.s.ie = 0; | |
5040 | -- sp->pstate.s.cle = sp->pstate.s.tle; | |
5041 | -- | |
5042 | -- /* Activate the appropriate globals. */ | |
5043 | -- switch (p_globals) { | |
5044 | -- case use_alternate_globals: | |
5045 | -- sp->pstate.s.ag = 1; | |
5046 | -- sp->pstate.s.vg = 0; | |
5047 | -- sp->pstate.s.mg = 0; | |
5048 | -- break; | |
5049 | -- case use_mmu_globals: | |
5050 | -- sp->pstate.s.ag = 0; | |
5051 | -- sp->pstate.s.vg = 0; | |
5052 | -- sp->pstate.s.mg = 1; | |
5053 | -- break; | |
5054 | -- case use_vector_globals: | |
5055 | -- sp->pstate.s.ag = 0; | |
5056 | -- sp->pstate.s.vg = 1; | |
5057 | -- sp->pstate.s.mg = 0; | |
5058 | -- break; | |
5059 | -- } | |
5060 | -- | |
5061 | -- update_global_regs(sp, sp->pstate); | |
5062 | -- update_async_trap_pending(sp); | |
5063 | -- | |
5064 | -- #ifdef ECC | |
5065 | -- update_error_trap_pending(sp); | |
5066 | -- #endif | |
5067 | -- | |
5068 | -- FASTMEM_TL_PSTATE_CHANGE(sp); | |
5069 | -- | |
5070 | -- set_pc_for_red_mode_trap(sp, T_RED_MODE_EXCEPTION); | |
5071 | -- sp->cti_executed = TRUE; | |
5072 | -- sp->cti_indx = TCC_T; | |
5073 | -- | |
5074 | -- /* setup for the fast memory stuff ... -ash */ | |
5075 | -- | |
5076 | -- | |
5077 | -- /* fastmem_flush(&(sp->fm), 0, 0); */ /* flush all pages */ | |
5078 | -- | |
5079 | -- #if 0 | |
5080 | -- { | |
5081 | -- Word tctxt; | |
5082 | -- (sp->dmmu->context_select)(sp->dmmu, DEFAULT_DATA_ASI, &tctxt); | |
5083 | -- sp->fm.current_ctxt = tctxt; | |
5084 | -- TRACEMOD_TTEXT( ("CONTEXT %d",(int)tctxt) ); | |
5085 | -- } | |
5086 | -- #endif | |
5087 | -- | |
5088 | -- } | |
5089 | -- | |
5090 | -- | |
5091 | -- | |
5092 | -- | |
5093 | -- | |
5094 | -- | |
5095 | -- void | |
5096 | -- save_trap_state(FcpuT* sp, Byte level, LWord trap_pc, LWord trap_npc) | |
5097 | -- { | |
5098 | -- | |
5099 | -- | |
5100 | -- sp->tstate[level].s.ccr = sp->ccr.b; | |
5101 | -- sp->tstate[level].s.asi = sp->asi_reg; | |
5102 | -- sp->tstate[level].s.cwp = P_CWP; | |
5103 | -- sp->tstate[level].s.pstate = sp->pstate.w; | |
5104 | -- | |
5105 | -- #if 0 | |
5106 | -- LO_W(trap_pc) &= ~0x3; /* Blow away lower two bits */ | |
5107 | -- LO_W(trap_npc) &= ~0x3; | |
5108 | -- #endif | |
5109 | -- | |
5110 | -- sp->was_tpc_in_hole[level] = FALSE; | |
5111 | -- sp->was_tnpc_in_hole[level] = FALSE; | |
5112 | -- sp->orig_tnpc[level] = trap_npc; | |
5113 | -- | |
5114 | -- if (sp->annul) { | |
5115 | -- sp->tpc[level] = trap_npc; | |
5116 | -- sp->tnpc[level] = trap_npc + 4; | |
5117 | -- sp->annul = FALSE; | |
5118 | -- } else { | |
5119 | -- sp->tpc[level] = trap_pc; | |
5120 | -- sp->tnpc[level] = trap_npc; | |
5121 | -- } | |
5122 | -- | |
5123 | -- } | |
5124 | -- | |
5125 | #endif /* } */ | |
5126 | ||
5127 | ||
5128 | ||
5129 | ||
5130 | ||
5131 | ||
5132 | ||
5133 | /************************************************************* | |
5134 | * | |
5135 | * ASI access support functions (and instruction impls) | |
5136 | * | |
5137 | *************************************************************/ | |
5138 | ||
5139 | ||
5140 | static void ss_xdc_miss(simcpu_t * sp, uint64_t * regp, tvaddr_t addr, maccess_t op) | |
5141 | { | |
5142 | sparcv9_cpu_t * v9p; | |
5143 | ss_strand_t * nsp; | |
5144 | mem_flags_t mflags; | |
5145 | uint_t context_type; | |
5146 | ||
5147 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
5148 | nsp = v9p->impl_specificp; | |
5149 | ||
5150 | context_type = (v9p->tl>0) ? ss_ctx_nucleus : ss_ctx_primary; | |
5151 | ||
5152 | mflags = MF_Normal; | |
5153 | ||
5154 | if (V9_User != v9p->state) mflags |= MF_Has_Priv; | |
5155 | if (nsp->mmu_bypass) mflags |= MF_MMU_Bypass; | |
5156 | /* V9_ASI_IMPLICIT takes little endian from CLE */ | |
5157 | if (v9p->pstate.cle) mflags |= MF_Little_Endian; | |
5158 | ||
5159 | ASSERT(!IS_V9_MA_ATOMIC(op & MA_Op_Mask)); | |
5160 | ||
5161 | ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]); | |
5162 | ||
5163 | ss_memory_asi_access(sp, op, regp, mflags, SS_ASI_PRIMARY, context_type, | |
5164 | (1<<(op & MA_Size_Mask))-1, addr, 0); | |
5165 | ||
5166 | ASSERT(0LL==sp->intreg[Reg_sparcv9_g0]); | |
5167 | } | |
5168 | ||
5169 | ||
5170 | /**************************************************************** | |
5171 | * | |
5172 | * TLB code | |
5173 | * | |
5174 | ****************************************************************/ | |
5175 | ||
5176 | ||
5177 | ||
5178 | ||
5179 | void ss_tlb_init(ss_tlb_t * tlbp, uint_t nentries) | |
5180 | { | |
5181 | int i; | |
5182 | ||
5183 | tlbp->freep = (tlb_entry_t *)0; | |
5184 | tlbp->nentries = nentries; | |
5185 | tlbp->tlb_entryp = Xcalloc(nentries, tlb_entry_t); | |
5186 | ||
5187 | /* clear each entry and stack on the free list */ | |
5188 | for (i=nentries-1; i>=0; i--) { | |
5189 | /* FIXME: give the tlb entry fields some default power on value ? */ | |
5190 | ss_free_tlb_entry(tlbp, &(tlbp->tlb_entryp[i])); | |
5191 | } | |
5192 | ||
5193 | /* Now init and clear the hash */ | |
5194 | for (i=0; i<SS_TLB_HASH_ENTRIES; i++) { | |
5195 | tlb_hash_bucket_t * hbp; | |
5196 | ||
5197 | hbp = &(tlbp->hash[i]); | |
5198 | ||
5199 | hbp->ptr = NULL; | |
5200 | } | |
5201 | ||
5202 | tlbp->shares = 0; | |
5203 | ||
5204 | #if SS_TLB_REPLACE_RROBIN /* { */ | |
5205 | tlbp->last_replaced = 0; | |
5206 | #endif /* } */ | |
5207 | RW_lock_init(&tlbp->rwlock, NULL); | |
5208 | } | |
5209 | ||
5210 | void ss_tlb_flush_shares(simcpu_t * sp, ss_tlb_t * tlbp, bool_t is_immu) | |
5211 | { | |
5212 | simcpu_t * xsp; | |
5213 | sparcv9_cpu_t * v9p; | |
5214 | ss_strand_t * nsp; | |
5215 | int i; | |
5216 | uint_t found = 0; | |
5217 | ||
5218 | for (i = 0; i < simcpu_list.count; i++) { | |
5219 | xsp = LIST_ENTRY(simcpu_list, i); | |
5220 | v9p = (sparcv9_cpu_t *)(xsp->specificp); | |
5221 | nsp = v9p->impl_specificp; | |
5222 | if (nsp->itlbp == tlbp || nsp->dtlbp == tlbp) { | |
5223 | found++; | |
5224 | if (xsp != sp) { | |
5225 | if (is_immu) | |
5226 | xsp->xicache_trans_flush_pending = true; | |
5227 | else | |
5228 | xsp->xdcache_trans_flush_pending = true; | |
5229 | } | |
5230 | } | |
5231 | } | |
5232 | ASSERT(found == tlbp->shares); | |
5233 | } | |
5234 | ||
5235 | void ss_tlb_scrub(simcpu_t * sp, ss_tlb_t * tlbp, bool_t is_immu) | |
5236 | { | |
5237 | tlb_entry_t * tep; | |
5238 | int i; | |
5239 | bool_t need_flush = false; | |
5240 | ||
5241 | RW_wrlock(&tlbp->rwlock); | |
5242 | ||
5243 | tep = &(tlbp->tlb_entryp[0]); | |
5244 | for (i=tlbp->nentries-1; i>=0; i--, tep++) { | |
5245 | if (tep->hashidx != -1) { | |
5246 | need_flush = true; | |
5247 | ss_tlb_unhash(tlbp, tep); | |
5248 | ss_free_tlb_entry(tlbp, tep); | |
5249 | } | |
5250 | } | |
5251 | ||
5252 | #if SS_TLB_REPLACE_RROBIN /* { */ | |
5253 | tlbp->last_replaced = 0; | |
5254 | #endif /* } */ | |
5255 | ||
5256 | RW_unlock(&tlbp->rwlock); | |
5257 | ||
5258 | if (need_flush) { | |
5259 | if (is_immu) | |
5260 | sp->xicache_trans_flush_pending = true; | |
5261 | else | |
5262 | sp->xdcache_trans_flush_pending = true; | |
5263 | if (tlbp->shares > 1) { | |
5264 | ss_tlb_flush_shares(sp, tlbp, is_immu); | |
5265 | } | |
5266 | } | |
5267 | } | |
5268 | ||
5269 | ||
5270 | ||
5271 | ||
5272 | /* | |
5273 | * assumes you hold any appropriate locks when calling | |
5274 | */ | |
5275 | ||
5276 | void ss_free_tlb_entry(ss_tlb_t * tlbp, tlb_entry_t * tep) | |
5277 | { | |
5278 | /* only write to fields that can't be read by software */ | |
5279 | /* and ensure this entry is effectively marked "invalid" */ | |
5280 | tep->match_shift = 0; | |
5281 | tep->flags = 0; | |
5282 | tep->match_context = SS_TLB_INVALID_CONTEXT; | |
5283 | tep->hashidx = -1; | |
5284 | #if defined(NIAGARA1) | |
5285 | tep->data &= ~(1ull << SUN4U_TTED_V_BIT); | |
5286 | #elif defined(ROCK) | |
5287 | tep->data &= ~((1ull << SUN4V_TTED_V_BIT) | | |
5288 | (1ull << ROCK_DATA_ACCESS_V1_BIT)); | |
5289 | #else | |
5290 | tep->data &= ~(1ull << SUN4V_TTED_V_BIT); | |
5291 | #endif | |
5292 | ||
5293 | /* assign to free list */ | |
5294 | tep->nextp = tlbp->freep; | |
5295 | tlbp->freep = tep; | |
5296 | } | |
5297 | ||
5298 | ||
5299 | void ss_tlb_unhash(ss_tlb_t * tlbp, tlb_entry_t * tep) | |
5300 | { | |
5301 | tlb_entry_t ** bp, *p; | |
5302 | ||
5303 | ASSERT( tep->hashidx != -1 ); | |
5304 | ||
5305 | /* We'll barf any panic if we can't find the entry on the list */ | |
5306 | /* but this cant ever happen right ! :-) */ | |
5307 | for (bp = &(tlbp->hash[tep->hashidx].ptr); (p=*bp)!=tep; bp=&(p->nextp)); | |
5308 | ||
5309 | *bp = p->nextp; | |
5310 | ||
5311 | tep->hashidx = -1; | |
5312 | } | |
5313 | ||
5314 | void ss_tlb_unfree(ss_tlb_t * tlbp, tlb_entry_t * tep) | |
5315 | { | |
5316 | tlb_entry_t ** bp, *p; | |
5317 | ||
5318 | ASSERT( tep->hashidx == -1 ); | |
5319 | ||
5320 | /* We'll barf any panic if we can't find the entry on the list */ | |
5321 | /* but this cant ever happen right ! :-) */ | |
5322 | for (bp = &(tlbp->freep); (p=*bp)!=tep; bp=&(p->nextp)); | |
5323 | ||
5324 | *bp = p->nextp; | |
5325 | } | |
5326 | ||
5327 | ||
5328 | ||
5329 | /* | |
5330 | * Niagara 1: | |
5331 | * - demap all demaps all entries that DO NOT have their locked bit set | |
5332 | * - demap page & context demaps matching entries even if their locked bits are set. | |
5333 | * | |
5334 | * Niagara 2: | |
5335 | * - removes the lock bit support | |
5336 | * - adds a demap all page operation, which demaps all pages with R=1 from the TLB. | |
5337 | */ | |
5338 | bool_t ss_demap(simcpu_t * sp, ss_demap_t op, ss_mmu_t * mmup, ss_tlb_t * tlbp, uint_t partid, bool_t is_real, uint_t context, tvaddr_t addr) | |
5339 | { | |
5340 | matchcontext_t match_context; | |
5341 | tlb_entry_t * tep; | |
5342 | uint_t i; | |
5343 | bool_t need_flush = false; | |
5344 | ||
5345 | match_context = is_real ? SS_TLB_REAL_CONTEXT : context; | |
5346 | ||
5347 | switch (op) { | |
5348 | case NA_demap_page: | |
5349 | DBGMMU( lprintf(sp->gid, "demap_page: part=%x ctx=%x va=0x%llx r:%u\n", partid, context, addr, is_real); ); | |
5350 | break; | |
5351 | case NA_demap_all: | |
5352 | DBGMMU( lprintf(sp->gid, "demap_all: part=%x r:%u\n", partid, is_real); ); | |
5353 | break; | |
5354 | #ifdef NIAGARA2 | |
5355 | case NA_demap_all_page: | |
5356 | DBGMMU( lprintf(sp->gid, "demap_all_page: r:%u\n", is_real); ); | |
5357 | break; | |
5358 | #endif | |
5359 | #ifdef NIAGARA1 | |
5360 | case NA_demap_init: | |
5361 | DBGMMU( lprintf(sp->gid, "demap_init\n"); ); | |
5362 | break; | |
5363 | #endif | |
5364 | case NA_demap_context: | |
5365 | /* | |
5366 | * remove TLB entries that match the specified context, partid and R=0 | |
5367 | * (true for both N1 and N2, see to N1 PRM Rev 1.5 and N2 PM Rev 0.8) | |
5368 | */ | |
5369 | DBGMMU( lprintf(sp->gid, "demap_ctx: part=%x ctx=%x r:%u\n", partid, context, is_real); ); | |
5370 | if (is_real) return false; | |
5371 | break; | |
5372 | #ifdef ROCK | |
5373 | case NA_demap_real: | |
5374 | DBGMMU( lprintf(sp->gid, "demap_real: part=%x r:%u\n", partid, is_real); ); | |
5375 | /* | |
5376 | * This operation is supposed to remove all real TLB entries that match | |
5377 | * the partid, so we force match_context to SS_TLB_REAL_CONTEXT and | |
5378 | * treat this like a NA_demap_page and remove any entry which matches | |
5379 | * context and partid. | |
5380 | */ | |
5381 | match_context = SS_TLB_REAL_CONTEXT; | |
5382 | break; | |
5383 | #endif | |
5384 | default: | |
5385 | return false; | |
5386 | } | |
5387 | ||
5388 | RW_wrlock(&tlbp->rwlock); | |
5389 | ||
5390 | /* | |
5391 | * First lets look for potentially matching pages we may have to | |
5392 | * de-map first. We demap the old entry if it incorporates our new | |
5393 | * page, or vice-versa. | |
5394 | */ | |
5395 | ||
5396 | tep = &(tlbp->tlb_entryp[0]); | |
5397 | for (i=tlbp->nentries; i>0; i--, tep++) { | |
5398 | ||
5399 | /* ignore this entry if not in use - i.e. not on hash list */ | |
5400 | if (tep->hashidx == -1) continue; | |
5401 | ||
5402 | switch (op) { | |
5403 | #ifdef NIAGARA2 | |
5404 | case NA_demap_all_page: | |
5405 | if (tep->is_real != is_real) break; | |
5406 | goto matching_pid; | |
5407 | #endif | |
5408 | case NA_demap_all: | |
5409 | #ifdef NIAGARA1 | |
5410 | if (tep->flags & SS_TLB_FLAG_LOCKED) break; | |
5411 | #endif | |
5412 | goto matching_pid; | |
5413 | case NA_demap_page: | |
5414 | if (((tep->tag_pfn ^ addr)>>tep->match_shift)!=0LL) break; | |
5415 | goto matching_ctx; | |
5416 | #ifdef ROCK | |
5417 | case NA_demap_real: | |
5418 | /* fall through */ | |
5419 | #endif | |
5420 | case NA_demap_context: | |
5421 | matching_ctx: | |
5422 | if (tep->match_context != match_context) break; | |
5423 | matching_pid: | |
5424 | if (tep->partid != partid) break; | |
5425 | /* fall thru */ | |
5426 | #ifdef NIAGARA1 | |
5427 | case NA_demap_init: | |
5428 | #endif | |
5429 | /* matching entry - put back on the free list */ | |
5430 | need_flush = true; | |
5431 | ss_tlb_unhash(tlbp, tep); | |
5432 | ss_free_tlb_entry( tlbp, tep ); | |
5433 | ||
5434 | #if ERROR_INJECTION | |
5435 | DBGERR( lprintf(sp->gid, "ss_demap(): errorp->itep=%x" | |
5436 | " errorp->dtep=%x tep=%x\n", | |
5437 | sp->errorp->itep, sp->errorp->dtep, tep); ); | |
5438 | tlb_entry_error_match(sp, mmup, tep); | |
5439 | #endif | |
5440 | ||
5441 | break; | |
5442 | default: | |
5443 | fatal("Internal error: illegal demap op (0x%x)", op); | |
5444 | } | |
5445 | } | |
5446 | RW_unlock(&tlbp->rwlock); | |
5447 | ||
5448 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
5449 | if (need_flush) { | |
5450 | if (mmup->is_immu) | |
5451 | sp->xicache_trans_flush_pending = true; | |
5452 | else | |
5453 | sp->xdcache_trans_flush_pending = true; | |
5454 | if (tlbp->shares > 1) { | |
5455 | ss_tlb_flush_shares(sp, tlbp, mmup->is_immu); | |
5456 | } | |
5457 | } | |
5458 | #endif /* } */ | |
5459 | ||
5460 | return true; | |
5461 | } | |
5462 | ||
5463 | ||
5464 | #ifdef HW_TABLEWALK | |
5465 | ||
5466 | /**************************************************************** | |
5467 | * | |
5468 | * HW tablewalk code | |
5469 | * | |
5470 | ****************************************************************/ | |
5471 | ||
5472 | /* | |
5473 | * This function implements the hardware tablewalk for Niagara 2 and Rock | |
5474 | * to service the reload request from the TLBs. The sequence of | |
5475 | * matching the TTEs and reloading the new TLB entry is as follows: | |
5476 | * | |
5477 | * - access all configured TSBs to find an TTE that matches the | |
5478 | * vpn (passed in as 'va') and the context of the request | |
5479 | * | |
5480 | * - translate the RPN of the matching TTE into a PPN | |
5481 | * | |
5482 | * - call ss_tlb_insert to load the matching TTE into TLB | |
5483 | * | |
5484 | * - return a specific trap code to indicate the completion status: | |
5485 | * | |
5486 | * a) trap_NONE: | |
5487 | * | |
5488 | * a match is found, and TLB reload is successful | |
5489 | * | |
5490 | * b) {instruction,data}_invalid_TSB_entry/IAE_unauth_access: | |
5491 | * | |
5492 | * a match is found, but an error occurred during | |
5493 | * the RPN-> PPN translation | |
5494 | * | |
5495 | * c) {instruction,data}_access_MMU_miss: | |
5496 | * | |
5497 | * tablewalk is enabled, but a match is not found | |
5498 | * or the TLB reload is not successful | |
5499 | * | |
5500 | * d) fast_{instruction,data}_access_MMU_miss: | |
5501 | * | |
5502 | * tablewalk on all TSBs are disabled | |
5503 | * | |
5504 | */ | |
5505 | ss_trap_type_t ss_hardware_tablewalk(simcpu_t *sp, ss_mmu_t * mmup, ss_tlb_t *tlbp, tvaddr_t va, uint_t context_type, uint_t *flags, uint64_t *pa_offset) | |
5506 | { | |
5507 | sparcv9_cpu_t *v9p = (sparcv9_cpu_t *)(sp->specificp); | |
5508 | ss_strand_t *nsp = v9p->impl_specificp; | |
5509 | ||
5510 | ss_trap_type_t miss_trap_type = SS_trap_NONE; | |
5511 | ss_tsb_info_t *tsb_config_regp; | |
5512 | tvaddr_t tsb_base; | |
5513 | bool_t hwtw_enabled = false; | |
5514 | uint_t i, ctrl, context, context0, context1; | |
5515 | tvaddr_t va_22; | |
5516 | ||
5517 | /* | |
5518 | * Figure out the context value. Note that the shared context | |
5519 | * value is not really used in HW TW other than for determining | |
5520 | * which context to insert the entry with (based on the | |
5521 | * use_context0/1 bits) | |
5522 | */ | |
5523 | switch (context_type) { | |
5524 | case ss_ctx_primary: | |
5525 | context0 = nsp->pri_context; | |
5526 | context1 = nsp->pri_context1; | |
5527 | break; | |
5528 | case ss_ctx_secondary: | |
5529 | context0 = nsp->sec_context; | |
5530 | context1 = nsp->sec_context1; | |
5531 | break; | |
5532 | case ss_ctx_nucleus: | |
5533 | context0 = SS_NUCLEUS_CONTEXT; | |
5534 | context1 = context0; | |
5535 | break; | |
5536 | default: | |
5537 | fatal("ss_hardware_tablewalk: Internal Error. Not expecting " | |
5538 | "context type 0x%x\n", context_type); | |
5539 | } | |
5540 | ||
5541 | if (context0 == 0) { | |
5542 | tsb_config_regp = &nsp->mmu_zero_ctxt_tsb_config[0]; | |
5543 | } else { | |
5544 | tsb_config_regp = &nsp->mmu_nonzero_ctxt_tsb_config[0]; | |
5545 | } | |
5546 | ||
5547 | va_22 = va >> 22; | |
5548 | ||
5549 | /* | |
5550 | * walk through all four TSBs to find an tte entry that matches | |
5551 | * the va and the context of the request | |
5552 | */ | |
5553 | for (i=0; i<4; i++, tsb_config_regp++) { | |
5554 | uint_t tte_context, tte_ps, tte_idx, tte_ep; | |
5555 | uint64_t tte_entry[2]; | |
5556 | tvaddr_t tte_va, tte_ra, tte_pa; | |
5557 | uint8_t *tte_addr; | |
5558 | ||
5559 | /* | |
5560 | * first check if the tablewalk on this TSB is enabled | |
5561 | * (controled by the enable bit of tsb_config_reg) | |
5562 | */ | |
5563 | if (!tsb_config_regp->enable) continue; | |
5564 | ||
5565 | hwtw_enabled = true; | |
5566 | ||
5567 | /* | |
5568 | * calculate the TSB pointer based on the given VA and | |
5569 | * TSB configuration register. | |
5570 | */ | |
5571 | tte_addr = ss_make_tsb_pointer_int(va, tsb_config_regp); | |
5572 | ||
5573 | /* | |
5574 | * fetch the tte entry from the memory (each taken two 8 bytes) | |
5575 | */ | |
5576 | if (!ss_hwtw_get_tte_entry(sp, tte_addr, &tte_entry[0])) { | |
5577 | /* Shouldn't happen under normal operation */ | |
5578 | EXEC_WARNING(("ss_hardware_tablewalk: ss_hwtw_get_tte_entry() failed.\n")); | |
5579 | continue; | |
5580 | } | |
5581 | ||
5582 | DBGMMU( lprintf(sp->gid, "ss_hardware_tablewalk check tsb%u pa=0x%llx: va>>22=0x%llx ctx=0x%x/0x%x tag=0x%llx data=0x%llx\n", i, tte_addr, va_22, context0, context1, tte_entry[0], tte_entry[1]);); | |
5583 | ||
5584 | /* | |
5585 | * validate the V bit, continue searching other TSBs if invalid | |
5586 | */ | |
5587 | if (!SUN4V_TTED_V(tte_entry[1])) continue; | |
5588 | ||
5589 | /* | |
5590 | * sun4v Solaris uses the reserved bits to flag SW bugs, | |
5591 | * ignore this tte entry if any of these reserved bits are set | |
5592 | */ | |
5593 | if (SUN4V_TTET_RSVD(tte_entry[0])) continue; | |
5594 | ||
5595 | /* | |
5596 | * check the fetched tte for a match | |
5597 | */ | |
5598 | tte_context = SUN4V_TTET_CTXT(tte_entry[0]); | |
5599 | tte_va = SUN4V_TTET_VA(tte_entry[0]); | |
5600 | tte_ep = SUN4V_TTED_EP(tte_entry[1]); | |
5601 | tte_ra = SUN4V_TTED_RA(tte_entry[1]); | |
5602 | tte_ps = SUN4V_TTED_PS(tte_entry[1]); | |
5603 | ||
5604 | /* | |
5605 | * determine the context to check in terms of bit 62 and 61 of the tsb_config_reg: | |
5606 | * (see 13.11.11 of N2 PRM Rev 1.1 and section 18.5.4 of SPA 2.0 Draft 0.7.1) | |
5607 | * | |
5608 | * - requesting context is zero (nucleus), both use_context_0 and use_context_1 are | |
5609 | * ignored, but hwtw behaves as if these two bits are zero (so there is a context | |
5610 | * comparison, i.e. to check if tte_context == 0) | |
5611 | * | |
5612 | * - context ID-match mode (use_context_0==0 && use_context_1==0): | |
5613 | * context field of TTE is matched against the context of the request | |
5614 | * | |
5615 | * - context ID-ignore mode (use_context_0==1 || use_context_1==1): | |
5616 | * context field of TTE is ignored, load value of Context Register 0 or 1 to TLB | |
5617 | */ | |
5618 | ||
5619 | if (context0 == SS_NUCLEUS_CONTEXT) { | |
5620 | if (tte_context != SS_NUCLEUS_CONTEXT) continue; | |
5621 | context = SS_NUCLEUS_CONTEXT; | |
5622 | } else { | |
5623 | if (tsb_config_regp->use_context_0) { | |
5624 | context = context0; | |
5625 | } else if (tsb_config_regp->use_context_1) { | |
5626 | context = context1; | |
5627 | } else /* use_context_0 == use_context_1 == 0 */ { | |
5628 | if (tte_context != context0 && | |
5629 | tte_context != context1) | |
5630 | continue; | |
5631 | context = tte_context; | |
5632 | } | |
5633 | } | |
5634 | ||
5635 | /* | |
5636 | * In both Niagara2 and Rock, it is okay to match if the | |
5637 | * page size of the tsb_config_reg does not match the page | |
5638 | * size of the tte. | |
5639 | * More specifically: | |
5640 | * if page size of TTE >= page size of TSB, then match OK | |
5641 | * if page size of TTE < page size of TSB, then no match | |
5642 | * | |
5643 | * The VA mask bits used in a comparison for a match | |
5644 | * are determined by the TSB page size. | |
5645 | * | |
5646 | */ | |
5647 | if (((tte_va ^ va_22) >> tsb_config_regp->tag_match_shift)==0 && | |
5648 | tte_ps>=tsb_config_regp->page_size) { | |
5649 | /* | |
5650 | * a matching tte is found, check its EP bit for ITLB miss | |
5651 | */ | |
5652 | if (mmup->is_immu && (!tte_ep)) { | |
5653 | DBGMMU(lprintf(sp->gid,"ss_hardware_tablewalk: false hit on ITLB miss (EP bit unset): va=0x%llx\n", va); ); | |
5654 | return (SS_trap_IAE_unauth_access); | |
5655 | } | |
5656 | ||
5657 | /* | |
5658 | * the ra_not_pa bit determines if this TSB contains RAs or PAs: | |
5659 | * (if ra_not_pa is set, do RPN -> PPN translation, otherwise | |
5660 | * the RA is treated as PA) | |
5661 | */ | |
5662 | if (!tsb_config_regp->ra_not_pa) { | |
5663 | tte_pa = tte_ra; | |
5664 | } else { | |
5665 | /* | |
5666 | * setup trap type for error occurred in RPN->PPN translation | |
5667 | */ | |
5668 | if (mmup->is_immu) | |
5669 | miss_trap_type = SS_trap_instruction_invalid_TSB_entry; | |
5670 | else | |
5671 | miss_trap_type = SS_trap_data_invalid_TSB_entry; | |
5672 | ||
5673 | /* | |
5674 | * See if we can find a conversion for this RA | |
5675 | */ | |
5676 | if (ss_hwtw_convert_ra_to_pa(tte_ra, &tte_pa, nsp, tte_ps)) { | |
5677 | /* | |
5678 | * RA -> PA conversion found. | |
5679 | * Reassemble the matching TTE data entry with the PPN translation | |
5680 | */ | |
5681 | tte_entry[1] &= ~SUN4V_PN_MASK; | |
5682 | tte_entry[1] |= tte_pa & SUN4V_PN_MASK; | |
5683 | ||
5684 | /* | |
5685 | * set the trap flag to direct the call to tlb_reload | |
5686 | */ | |
5687 | miss_trap_type = SS_trap_NONE; | |
5688 | } | |
5689 | } | |
5690 | ||
5691 | if (miss_trap_type != SS_trap_NONE) { | |
5692 | DBGMMU(lprintf(sp->gid, "ss_hardware_tablewalk: false hit (invalid tte): va=0x%llx tte_ra=0x%llx\n", va, tte_ra); ); | |
5693 | return (miss_trap_type); | |
5694 | } | |
5695 | ||
5696 | /* | |
5697 | * update tag access register (we use macros because this is somewhat | |
5698 | * chip-specific). | |
5699 | */ | |
5700 | UPDATE_MMU_TAG_ACCESS(mmup, va, context); | |
5701 | ||
5702 | DBGMMU( lprintf(sp->gid, "ss_hardware_tablewalk hit: va=0x%llx data=0x%llx ctx=0x%x\n", va, tte_entry[1], context);); | |
5703 | ||
5704 | /* | |
5705 | * Insert this TTE into the TLB. | |
5706 | */ | |
5707 | miss_trap_type = ss_tlb_insert(sp, mmup, tlbp, nsp->partid, false, tte_entry[1], flags, pa_offset); | |
5708 | ||
5709 | return (miss_trap_type); | |
5710 | } | |
5711 | } | |
5712 | ||
5713 | #ifdef ROCK | |
5714 | /* | |
5715 | * Unlike N2, if we are in this routine, it means HWTW was enabled and so | |
5716 | * we will issue a "slow" MMU trap regardless of whether all four TSB | |
5717 | * config registers where valid or not. | |
5718 | */ | |
5719 | hwtw_enabled = true; | |
5720 | #endif | |
5721 | /* | |
5722 | * if we are here, either a match was not found or the tablewalk is disabled | |
5723 | */ | |
5724 | if (mmup->is_immu) { | |
5725 | if (hwtw_enabled) | |
5726 | miss_trap_type = SS_trap_instruction_access_MMU_miss; | |
5727 | else | |
5728 | miss_trap_type = SS_trap_fast_instruction_access_MMU_miss; | |
5729 | } else { | |
5730 | if (hwtw_enabled) | |
5731 | miss_trap_type = SS_trap_data_access_MMU_miss; | |
5732 | else | |
5733 | miss_trap_type = SS_trap_fast_data_access_MMU_miss; | |
5734 | } | |
5735 | ||
5736 | return (miss_trap_type); | |
5737 | } | |
5738 | ||
5739 | ||
5740 | uint8_t *ss_hwtw_find_base(simcpu_t *sp, ss_tsb_info_t *tsb_config_reg) | |
5741 | { | |
5742 | domain_t *domainp; | |
5743 | config_proc_t *config_procp; | |
5744 | config_addr_t *capsrc; | |
5745 | uint8_t *srcbufp; | |
5746 | tpaddr_t extent; | |
5747 | ||
5748 | config_procp = sp->config_procp; | |
5749 | domainp = config_procp->domainp; | |
5750 | ||
5751 | capsrc = find_domain_address(domainp, tsb_config_reg->tsb_base); | |
5752 | if (capsrc == NULL) { | |
5753 | EXEC_WARNING(("@ pc=0x%llx : ss_hwtw_find_base: src address not valid",sp->pc)); | |
5754 | return NULL; | |
5755 | } | |
5756 | /* | |
5757 | * try and get the buffer pointer | |
5758 | */ | |
5759 | extent = capsrc->config_devp->dev_typep->dev_cacheable(capsrc, DA_Load, | |
5760 | tsb_config_reg->tsb_base-capsrc->baseaddr, &srcbufp); | |
5761 | if (extent < (1ull << (13+tsb_config_reg->tsb_size))) { | |
5762 | EXEC_WARNING(("@ pc=0x%llx : ss_hwtw_find_base: src not large enough",sp->pc)); | |
5763 | return NULL; | |
5764 | } | |
5765 | ||
5766 | return srcbufp; | |
5767 | } | |
5768 | ||
5769 | ||
5770 | /* | |
5771 | * Get a tte entry from memory via a 128 bit attomic load | |
5772 | */ | |
5773 | static bool_t ss_hwtw_get_tte_entry(simcpu_t *sp, uint8_t * tte_addr, uint64_t *ttep) | |
5774 | { | |
5775 | /* | |
5776 | * do an atomic load of the TTE entry | |
5777 | */ | |
5778 | host_atomic_get128be((uint64_t *)tte_addr, ttep, &ttep[1]); | |
5779 | #if HOST_CPU_LITTLE_ENDIAN | |
5780 | *ttep = BSWAP_64(*ttep); | |
5781 | *(ttep + 1) = BSWAP_64(*(ttep + 1)); | |
5782 | #endif | |
5783 | ||
5784 | return true; | |
5785 | } | |
5786 | ||
5787 | #endif /* HW_TABLEWALK */ | |
5788 | ||
5789 | /* | |
5790 | * XOR all bits to get a single bit result. | |
5791 | * Used for parity bit generation. | |
5792 | */ | |
5793 | uint64_t xor_bits(uint64_t n) | |
5794 | { | |
5795 | n = (n ^ (n >> 32)) & 0xffffffff; | |
5796 | n = (n ^ (n >> 16)) & 0xffff; | |
5797 | n = (n ^ (n >> 8)) & 0xff; | |
5798 | n = (n ^ (n >> 4)) & 0xf; | |
5799 | n = (n ^ (n >> 2)) & 0x3; | |
5800 | n = (n ^ (n >> 1)) & 0x1; | |
5801 | return n; | |
5802 | } | |
5803 | ||
5804 | /**************************************************************** | |
5805 | * | |
5806 | * L1/L2 cache code | |
5807 | * | |
5808 | ****************************************************************/ | |
5809 | ||
5810 | ||
5811 | void ss_l1_cache_init(ss_l1_cache_t * cachep, uint32_t cachesize, bool_t is_icache) | |
5812 | { | |
5813 | uint_t tagsize, datasize; | |
5814 | int i; | |
5815 | ||
5816 | /* | |
5817 | * Allocate icache/dcache memory for data and tags | |
5818 | * Icache: | |
5819 | * 64bit diag tag per 32byte instn block | |
5820 | * 64bit diag data per 32bits instn data (instn + switch/parity bits) | |
5821 | * Dcache: | |
5822 | * 64bit tag per 16byte data block | |
5823 | * 64bit diag data mirrors cache data | |
5824 | */ | |
5825 | ||
5826 | tagsize = is_icache ? cachesize/4 : cachesize/2; | |
5827 | datasize = is_icache ? cachesize*2 : cachesize; | |
5828 | ||
5829 | cachep->tagp = Xmalloc(tagsize); | |
5830 | cachep->datap = Xmalloc(datasize); | |
5831 | ||
5832 | /* fill with bad data for debug */ | |
5833 | for (i=(tagsize/8)-1; i>=0; i--) { | |
5834 | cachep->tagp[i] = 0xbaadfeed; | |
5835 | } | |
5836 | for (i=(datasize/8)-1; i>=0; i--) { | |
5837 | cachep->datap[i] = 0xbaadfeed; | |
5838 | } | |
5839 | ||
5840 | cachep->bist_ctl = 0; | |
5841 | cachep->assocdis = 0; | |
5842 | cachep->inst_mask = 0; | |
5843 | RW_lock_init(&cachep->rwlock, NULL); | |
5844 | } | |
5845 | ||
5846 | ||
5847 | ||
5848 | /**************************************************************** | |
5849 | * | |
5850 | * Misc debugging code | |
5851 | * | |
5852 | ****************************************************************/ | |
5853 | ||
5854 | ||
5855 | #if !defined(NDEBUG) /* { */ | |
5856 | ||
5857 | ||
5858 | #endif /* } */ | |
5859 | ||
5860 | #if defined(NIAGARA1) || defined(NIAGARA2) | |
5861 | /* | |
5862 | * called in response to ~i or ~d | |
5863 | */ | |
5864 | void _ss_dump_tlbs(config_proc_t * config_procp, bool_t is_dtlb, bool_t lockit) | |
5865 | { | |
5866 | #if !defined(NDEBUG) /* { */ | |
5867 | ss_proc_t * np; | |
5868 | uint_t i, pnum; | |
5869 | FILE * fop = stdout; | |
5870 | ||
5871 | np = (ss_proc_t*)(config_procp->procp); | |
5872 | pnum = config_procp->proc_id; | |
5873 | ||
5874 | if (is_dtlb) { | |
5875 | for (i=0; i<np->ndtlb; i++) { | |
5876 | fprintf(fop, "Processor %u Core %u D-TLB:\n",pnum,i); | |
5877 | ss_tlb_contents(fop, &(np->dtlbp[i]), lockit); | |
5878 | } | |
5879 | } else { | |
5880 | for (i=0; i<np->nitlb; i++) { | |
5881 | fprintf(fop, "Processor %u Core %u I-TLB:\n",pnum,i); | |
5882 | ss_tlb_contents(fop, &(np->itlbp[i]), lockit); | |
5883 | } | |
5884 | } | |
5885 | #endif /* } */ | |
5886 | } | |
5887 | ||
5888 | void ss_dump_tlbs(config_proc_t * config_procp, bool_t is_dtlb) | |
5889 | { | |
5890 | _ss_dump_tlbs(config_procp, is_dtlb, true); | |
5891 | } | |
5892 | ||
5893 | void ss_dump_tlbs_nolock(config_proc_t * config_procp, bool_t is_dtlb) | |
5894 | { | |
5895 | _ss_dump_tlbs(config_procp, is_dtlb, false); | |
5896 | } | |
5897 | #endif | |
5898 | ||
5899 | ||
5900 | ||
5901 | #if !defined(NDEBUG) /* { */ | |
5902 | ||
5903 | ||
5904 | ||
5905 | ||
5906 | ||
5907 | /* HACK !! */ | |
5908 | ||
5909 | #include <stdarg.h> | |
5910 | ||
5911 | typedef enum { | |
5912 | PS_Left, PS_Center, PS_Right | |
5913 | } just_t; | |
5914 | ||
5915 | void pspace(FILE * fop, uint_t space, just_t just, char * fmt, ...) | |
5916 | { | |
5917 | va_list args; | |
5918 | char buf[2048]; | |
5919 | size_t len; | |
5920 | int i, before, after; | |
5921 | ||
5922 | va_start(args, fmt); | |
5923 | ||
5924 | vsprintf(buf, fmt, args); | |
5925 | ||
5926 | va_end(args); | |
5927 | ||
5928 | len = strlen(buf); | |
5929 | switch(just) { | |
5930 | case PS_Left: | |
5931 | before = 0; | |
5932 | after = space - len; | |
5933 | if (after<0) after = 0; | |
5934 | break; | |
5935 | case PS_Right: | |
5936 | after = 0; | |
5937 | before = space - len; | |
5938 | if (before<0) before = 0; | |
5939 | break; | |
5940 | case PS_Center: | |
5941 | before = space - len; | |
5942 | if (before<0) before = 0; | |
5943 | before >>=1; | |
5944 | after = space - (len + before); | |
5945 | if (after<0) after = 0; | |
5946 | break; | |
5947 | } | |
5948 | ||
5949 | for (i=0; i<before; i++) fprintf(fop, " "); | |
5950 | fprintf(fop, "%s", buf); | |
5951 | for (i=0; i<after; i++) fprintf(fop, " "); | |
5952 | } | |
5953 | ||
5954 | ||
5955 | ||
5956 | ||
5957 | ||
5958 | ||
5959 | ||
5960 | ||
5961 | ||
5962 | void ss_tlb_contents(FILE *fop, ss_tlb_t * tlbp, bool_t lockit) | |
5963 | { | |
5964 | uint_t i; | |
5965 | tlb_entry_t * tep; | |
5966 | ||
5967 | pspace(fop, 6, PS_Left, "Entry"); | |
5968 | pspace(fop, 8, PS_Right, "Part:"); | |
5969 | pspace(fop, 8, PS_Left, "ctxt"); | |
5970 | pspace(fop, 7, PS_Center, "shift"); | |
5971 | pspace(fop, 20, PS_Center, "Tag"); | |
5972 | pspace(fop, 20, PS_Center, "PA"); | |
5973 | pspace(fop, 7, PS_Center, "Flags"); | |
5974 | pspace(fop, 20, PS_Center, "RAW"); | |
5975 | fprintf(fop, "\n"); | |
5976 | ||
5977 | /* grab lock as writer to ensure nothing changes */ | |
5978 | if (lockit) | |
5979 | RW_wrlock(&tlbp->rwlock); | |
5980 | ||
5981 | tep = &(tlbp->tlb_entryp[0]); | |
5982 | for (i=0; i<tlbp->nentries; i++) { | |
5983 | char * contextp; | |
5984 | uint_t sh; | |
5985 | tpaddr_t pa; | |
5986 | ||
5987 | switch (tep->match_context) { | |
5988 | case SS_TLB_INVALID_CONTEXT: | |
5989 | goto no_output; | |
5990 | case SS_TLB_REAL_CONTEXT: | |
5991 | pspace(fop, 6, PS_Right, "%u : ", i); | |
5992 | pspace(fop, 8, PS_Right, "0x%x:", tep->partid); | |
5993 | pspace(fop, 8, PS_Left, "real"); | |
5994 | break; | |
5995 | default: | |
5996 | pspace(fop, 6, PS_Right, "%u : ", i); | |
5997 | pspace(fop, 8, PS_Right, "0x%x:", tep->partid); | |
5998 | pspace(fop, 8, PS_Left, "0x%x", tep->tag_context); | |
5999 | break; | |
6000 | } | |
6001 | ||
6002 | pspace(fop, 7, PS_Center, "%u", tep->match_shift); | |
6003 | ||
6004 | sh = tep->match_shift; | |
6005 | pa = tep->tag_pfn + tep->pa_offset; | |
6006 | ||
6007 | pspace(fop, 20, PS_Right, "0x%llx", tep->tag_pfn); | |
6008 | pspace(fop, 20, PS_Right, "0x%llx", pa); | |
6009 | ||
6010 | #ifdef NIAGARA1 | |
6011 | pspace(fop, 7, PS_Center, "%c%c%c%c%c", | |
6012 | (tep->flags & SS_TLB_FLAG_READ) ? 'R' : '.', | |
6013 | (tep->flags & SS_TLB_FLAG_WRITE) ? 'W' : '.', | |
6014 | (tep->flags & SS_TLB_FLAG_EXEC) ? 'X' : '.', | |
6015 | (tep->flags & SS_TLB_FLAG_PRIV) ? 'P' : '.', | |
6016 | (tep->flags & SS_TLB_FLAG_LOCKED) ? 'L' : '.' ); | |
6017 | #else | |
6018 | pspace(fop, 7, PS_Center, "%c%c%c%c", | |
6019 | (tep->flags & SS_TLB_FLAG_READ) ? 'R' : '.', | |
6020 | (tep->flags & SS_TLB_FLAG_WRITE) ? 'W' : '.', | |
6021 | (tep->flags & SS_TLB_FLAG_EXEC) ? 'X' : '.', | |
6022 | (tep->flags & SS_TLB_FLAG_PRIV) ? 'P' : '.'); | |
6023 | #endif | |
6024 | pspace(fop, 20, PS_Right, "0x%llx", tep->data); | |
6025 | ||
6026 | next:; | |
6027 | fprintf(fop, "\n"); | |
6028 | no_output:; | |
6029 | ||
6030 | tep++; | |
6031 | } | |
6032 | if (lockit) | |
6033 | RW_unlock(&tlbp->rwlock); | |
6034 | } | |
6035 | ||
6036 | #endif /* } */ | |
6037 | ||
6038 | ||
6039 | /**************************************************************** | |
6040 | * | |
6041 | * OLD STUFF | |
6042 | * | |
6043 | ****************************************************************/ | |
6044 | ||
6045 | ||
6046 | ||
6047 | ||
6048 | ||
6049 | #if 0 /* { */ | |
6050 | -- | |
6051 | --void ss_xdc_load_miss(simcpu_t * sp, uint64_t * regp, xdcache_line_t * xclp, tvaddr_t va, bool_t issigned, int bytes) | |
6052 | --{ | |
6053 | -- tpaddr_t pa, pa_tag; | |
6054 | -- uint8_t * bufp, * ptr; | |
6055 | -- tvaddr_t tag; | |
6056 | -- config_dev_t * cdp; | |
6057 | -- tpaddr_t extent; | |
6058 | -- uint64_t val; | |
6059 | -- sparcv9_cpu_t * v9p; | |
6060 | -- ss_strand_t * nsp; | |
6061 | -- int flags; | |
6062 | -- | |
6063 | -- ASSERT( (bytes & (bytes-1)) == 0 ); /* must be power of 2 number of bytes */ | |
6064 | -- | |
6065 | -- v9p = (sparcv9_cpu_t *)(sp->specificp); | |
6066 | -- nsp = v9p->impl_specificp; | |
6067 | -- | |
6068 | -- /* quick check of alignment */ | |
6069 | -- if ((va & (bytes-1)) != 0) { | |
6070 | -- /* alignment error force a trap */ | |
6071 | -- SET_DTLB_LOAD_FAULT( nsp, va ); | |
6072 | -- MEMORY_ACCESS_TRAP(); | |
6073 | -- v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned); | |
6074 | -- return; | |
6075 | -- } | |
6076 | -- | |
6077 | -- /* Find the pa corresponding to the line we need */ | |
6078 | -- tag = va & XDCACHE_TAG_MASK; | |
6079 | -- | |
6080 | -- /* We assume that for Niagara, the TLB is off in Hyper priv mode */ | |
6081 | -- /* FIXME: we should probably do this by swizzling a function pointer */ | |
6082 | -- /* for this when we change mode, rather that having an if here ... fix later */ | |
6083 | -- | |
6084 | -- pa = va; | |
6085 | -- pa_tag = tag; | |
6086 | -- if (!nsp->mmu_bypass) { | |
6087 | -- int idx, context, partid; | |
6088 | -- ss_tlb_t * tlbp; | |
6089 | -- tlb_entry_t * tep; | |
6090 | -- int flags; | |
6091 | -- | |
6092 | -- tlbp = nsp->dtlbp; | |
6093 | -- RW_rdlock(&tlbp->rwlock); | |
6094 | -- | |
6095 | -- /* FIXME: need a current context variable, not a test here */ | |
6096 | -- context = (v9p->tl>0) ? 0 : nsp->pri_context; | |
6097 | -- partid = nsp->partid; | |
6098 | -- | |
6099 | -- /* FIXME: Need a better hash than this ! */ | |
6100 | -- idx = va >> SS_MAX_PAGE_SIZE_BITS; | |
6101 | -- idx += context + partid; | |
6102 | -- | |
6103 | -- idx &= SS_TLB_HASH_MASK; | |
6104 | -- | |
6105 | -- /* | |
6106 | -- * So we search for a matching page using the info we have in the | |
6107 | -- * hash - while another thread might possibly be removing or | |
6108 | -- * inserting an entry into the same table. | |
6109 | -- */ | |
6110 | -- | |
6111 | -- | |
6112 | -- for ( tep = tlbp->hash[idx].ptr; tep!=(tlb_entry_t*)0; tep = tep->nextp ) { | |
6113 | -- /* try and match the entry as appropriate */ | |
6114 | -- if (((tep->tag_pfn ^ va)>>tep->match_shift)==0 && tep->match_context==context && tep->partid == partid) goto tlb_match; | |
6115 | -- } | |
6116 | -- RW_unlock(&tlbp->rwlock); | |
6117 | -- SET_DTLB_LOAD_FAULT( nsp, va ); | |
6118 | -- MEMORY_ACCESS_TRAP(); | |
6119 | -- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_MMU_miss); | |
6120 | -- return; | |
6121 | -- | |
6122 | --tlb_match:; | |
6123 | -- /* we have a matching entry ... now all we have to worry about are the permissions */ | |
6124 | -- flags = tep->flags; | |
6125 | -- pa += tep->pa_offset; | |
6126 | -- pa_tag += tep->pa_offset; | |
6127 | -- | |
6128 | -- RW_unlock(&tlbp->rwlock); | |
6129 | -- | |
6130 | -- if ((flags & SS_TLB_FLAG_PRIV) && v9p->state == V9_User) { | |
6131 | -- SET_DTLB_LOAD_FAULT( nsp, va ); | |
6132 | -- MEMORY_ACCESS_TRAP(); | |
6133 | -- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_data_access_exception); | |
6134 | -- return; | |
6135 | -- } | |
6136 | -- } | |
6137 | -- | |
6138 | -- /* | |
6139 | -- * OK - now go get the pointer to the line data | |
6140 | -- * ... start by finding the device that has the | |
6141 | -- * memory we need. | |
6142 | -- * optimise: by guessing at the last device found. | |
6143 | -- */ | |
6144 | -- | |
6145 | -- /* now find the device - looking in the cache first */ | |
6146 | -- | |
6147 | -- cdp = sp->xdc.miss_devp; | |
6148 | -- if (!(cdp && cdp->baseaddr<=pa && pa<cdp->topaddr)) { | |
6149 | -- domain_t * domainp; | |
6150 | -- config_proc_t * config_procp; | |
6151 | -- | |
6152 | -- config_procp = sp->config_procp; | |
6153 | -- domainp = config_procp->domainp; | |
6154 | -- | |
6155 | -- cdp = find_domain_device(domainp, pa); | |
6156 | -- if (cdp == (config_dev_t*)0) { | |
6157 | -- /* OK it's a bus error there was no backing store */ | |
6158 | -- | |
6159 | -- fatal("bus error - (@pc=0x%llx) load from va=0x%llx (cacheline va=0x%llx -> physical 0x%llx)", sp->pc, va, tag, pa_tag); /* FIXME */ | |
6160 | -- } | |
6161 | -- } | |
6162 | -- | |
6163 | -- /* try and get the buffer pointer */ | |
6164 | -- | |
6165 | -- extent = cdp->dev_typep->dev_cacheable(cdp, DA_Load, pa_tag - cdp->baseaddr, &bufp); | |
6166 | -- | |
6167 | -- if (extent < XDCACHE_LINE_SIZE) { | |
6168 | -- /* Let device handle memory access operation */ | |
6169 | -- /* bus error again ? or fill from multiple devices ? */ | |
6170 | -- /* need to check validty for device here ... FIXME */ | |
6171 | -- if (cdp->dev_typep->dev_cpu_load(cdp, pa - cdp->baseaddr, issigned, bytes, regp)) goto load_done; | |
6172 | -- | |
6173 | -- MEMORY_ACCESS_TRAP(); | |
6174 | -- v9p->post_precise_trap(sp, Sparcv9_trap_data_access_error); /* FIXME: right trap ? */ | |
6175 | -- return; | |
6176 | -- } | |
6177 | -- | |
6178 | -- /* | |
6179 | -- * For now only handle cacheable device memory | |
6180 | -- */ | |
6181 | -- /* only cache if memory is cacheable */ | |
6182 | -- sp->xdc.miss_devp = cdp; /* cache for next time */ | |
6183 | -- | |
6184 | -- /* fill in the line */ | |
6185 | -- xclp->tag = tag; | |
6186 | -- xclp->offset = ((uint64_t)bufp) - tag; | |
6187 | -- | |
6188 | -- /* | |
6189 | -- * Sigh now complete the load on behalf of the original | |
6190 | -- * load instruction | |
6191 | -- */ | |
6192 | -- | |
6193 | -- ptr = (uint8_t*)(xclp->offset + va); | |
6194 | -- | |
6195 | -- if (issigned) { | |
6196 | -- switch(bytes) { | |
6197 | -- case 1: val = *(sint8_t*)ptr; break; | |
6198 | -- case 2: val = *(sint16_t*)ptr; break; | |
6199 | -- case 4: val = *(sint32_t*)ptr; break; | |
6200 | -- default: abort(); | |
6201 | -- } | |
6202 | -- } else { | |
6203 | -- switch(bytes) { | |
6204 | -- case 1: val = *(uint8_t*)ptr; break; | |
6205 | -- case 2: val = *(uint16_t*)ptr; break; | |
6206 | -- case 4: val = *(uint32_t*)ptr; break; | |
6207 | -- case 8: val = *(uint64_t*)ptr; break; | |
6208 | -- default: abort(); | |
6209 | -- } | |
6210 | -- } | |
6211 | -- | |
6212 | -- *regp = val; | |
6213 | -- | |
6214 | -- /* | |
6215 | -- * Finally go get the next instruction | |
6216 | -- */ | |
6217 | --load_done:; | |
6218 | -- | |
6219 | -- NEXT_INSTN(sp); | |
6220 | --} | |
6221 | -- | |
6222 | -- | |
6223 | -- | |
6224 | --void ss_xdc_store_miss(simcpu_t * sp, uint64_t val, xdcache_line_t * xclp, tvaddr_t va, int bytes) | |
6225 | --{ | |
6226 | -- tpaddr_t pa, pa_tag; | |
6227 | -- uint8_t * bufp, * ptr; | |
6228 | -- tvaddr_t tag; | |
6229 | -- config_dev_t * cdp; | |
6230 | -- tpaddr_t extent; | |
6231 | -- sparcv9_cpu_t * v9p; | |
6232 | -- ss_strand_t * nsp; | |
6233 | -- | |
6234 | -- ASSERT( (bytes & (bytes-1)) == 0 ); /* must be power of 2 number of bytes */ | |
6235 | -- | |
6236 | -- v9p = (sparcv9_cpu_t *)(sp->specificp); | |
6237 | -- nsp = v9p->impl_specificp; | |
6238 | -- | |
6239 | -- /* quick check of alignment */ | |
6240 | -- if ((va & (bytes-1)) != 0) { | |
6241 | -- /* alignment error force a trap */ | |
6242 | -- SET_DTLB_STORE_FAULT( nsp, va ); | |
6243 | -- v9p->post_precise_trap(sp, Sparcv9_trap_mem_address_not_aligned); | |
6244 | -- return; | |
6245 | -- } | |
6246 | -- | |
6247 | -- /* Find the pa corresponding to the line we need */ | |
6248 | -- tag = va & XDCACHE_TAG_MASK; | |
6249 | -- | |
6250 | -- /* We assume that for Niagara, the TLB is off in Hyper priv mode */ | |
6251 | -- /* FIXME: we should probably do this by swizzling a function pointer */ | |
6252 | -- /* for this when we change mode, rather that having an if here ... fix later */ | |
6253 | -- | |
6254 | -- pa = va; | |
6255 | -- pa_tag = tag; | |
6256 | -- if (!nsp->mmu_bypass) { | |
6257 | -- int idx, context, partid; | |
6258 | -- ss_tlb_t * tlbp; | |
6259 | -- tlb_entry_t * tep; | |
6260 | -- int flags; | |
6261 | -- | |
6262 | -- tlbp = nsp->dtlbp; | |
6263 | -- RW_rdlock(&tlbp->rwlock); | |
6264 | -- | |
6265 | -- /* FIXME: need a current context variable, not a test here */ | |
6266 | -- context = (v9p->tl>0) ? 0 : nsp->pri_context; | |
6267 | -- partid = nsp->partid; | |
6268 | -- | |
6269 | -- /* FIXME: Need a better hash than this ! */ | |
6270 | -- idx = va >> SS_MAX_PAGE_SIZE_BITS; | |
6271 | -- idx += context + partid; | |
6272 | -- | |
6273 | -- idx &= SS_TLB_HASH_MASK; | |
6274 | -- | |
6275 | -- /* | |
6276 | -- * So we search for a matching page using the info we have in the | |
6277 | -- * hash - while another thread might possibly be removing or | |
6278 | -- * inserting an entry into the same table. | |
6279 | -- */ | |
6280 | -- | |
6281 | -- | |
6282 | -- for ( tep = tlbp->hash[idx].ptr; tep!=(tlb_entry_t*)0; tep = tep->nextp ) { | |
6283 | -- /* try and match the entry as appropriate */ | |
6284 | -- if (((tep->tag_pfn ^ va)>>tep->match_shift)==0 && tep->match_context==context && tep->partid == partid) goto tlb_match; | |
6285 | -- } | |
6286 | -- RW_unlock(&tlbp->rwlock); | |
6287 | -- SET_DTLB_STORE_FAULT( nsp, va ); | |
6288 | -- MEMORY_ACCESS_TRAP(); | |
6289 | -- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_MMU_miss); | |
6290 | -- return; | |
6291 | -- | |
6292 | --tlb_match:; | |
6293 | -- /* we have a matching entry ... now all we have to worry about are the permissions */ | |
6294 | -- flags = tep->flags; | |
6295 | -- pa += tep->pa_offset; | |
6296 | -- pa_tag += tep->pa_offset; | |
6297 | -- | |
6298 | -- RW_unlock(&tlbp->rwlock); | |
6299 | -- | |
6300 | -- /* privilege test apparently takes priority ... p.51 US-I PRM table 6-4 */ | |
6301 | -- if ((flags & SS_TLB_FLAG_PRIV) && v9p->state == V9_User) { | |
6302 | -- SET_DTLB_STORE_FAULT( nsp, va ); | |
6303 | -- MEMORY_ACCESS_TRAP(); | |
6304 | -- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_data_access_exception); | |
6305 | -- return; | |
6306 | -- } | |
6307 | -- | |
6308 | -- if (!(flags & SS_TLB_FLAG_WRITE)) { | |
6309 | -- SET_DTLB_STORE_FAULT( nsp, va ); | |
6310 | -- MEMORY_ACCESS_TRAP(); | |
6311 | -- v9p->post_precise_trap(sp, (sparcv9_trap_type_t)SS_trap_fast_data_access_protection); | |
6312 | -- return; | |
6313 | -- } | |
6314 | -- } | |
6315 | -- | |
6316 | -- | |
6317 | -- /* | |
6318 | -- * OK - now go get the pointer to the line data | |
6319 | -- * ... start by finding the device that has the | |
6320 | -- * memory we need. | |
6321 | -- * optimise: by guessing at the last device found. | |
6322 | -- */ | |
6323 | -- | |
6324 | -- /* now find the device - looking in the cache first */ | |
6325 | -- | |
6326 | -- cdp = sp->xdc.miss_devp; | |
6327 | -- if (!(cdp && cdp->baseaddr<=pa && pa<cdp->topaddr)) { | |
6328 | -- domain_t * domainp; | |
6329 | -- config_proc_t * config_procp; | |
6330 | -- | |
6331 | -- config_procp = sp->config_procp; | |
6332 | -- domainp = config_procp->domainp; | |
6333 | -- | |
6334 | -- cdp = find_domain_device(domainp, pa); | |
6335 | -- if (cdp == (config_dev_t*)0) { | |
6336 | -- /* OK it's a bus error there was no backing store */ | |
6337 | -- | |
6338 | -- fatal("bus error - (@pc=0x%llx) store to va=0x%llx (cacheline va=0x%llx -> physical 0x%llx)", sp->pc, va, tag, pa); /* FIXME */ | |
6339 | -- } | |
6340 | -- } | |
6341 | -- | |
6342 | -- /* try and get the buffer pointer */ | |
6343 | -- | |
6344 | -- extent = cdp->dev_typep->dev_cacheable(cdp, DA_Store, pa_tag - cdp->baseaddr, &bufp); | |
6345 | -- | |
6346 | -- if (extent < XDCACHE_LINE_SIZE) { | |
6347 | -- /* Let device handle memory access operation */ | |
6348 | -- /* bus error again ? or fill from multiple devices ? */ | |
6349 | -- /* need to check validty for device here ... FIXME */ | |
6350 | -- if (cdp->dev_typep->dev_cpu_store(cdp, pa - cdp->baseaddr, bytes, val)) goto store_done; | |
6351 | -- | |
6352 | -- MEMORY_ACCESS_TRAP(); | |
6353 | -- v9p->post_precise_trap(sp, Sparcv9_trap_data_access_error); /* FIXME: right trap ? */ | |
6354 | -- return; | |
6355 | -- } | |
6356 | -- | |
6357 | -- /* | |
6358 | -- * For now only handle cacheable device memory | |
6359 | -- */ | |
6360 | -- /* only cache if memory is cacheable */ | |
6361 | -- sp->xdc.miss_devp = cdp; /* cache for next time */ | |
6362 | -- | |
6363 | -- /* fill in the line */ | |
6364 | -- xclp->tag = tag; | |
6365 | -- xclp->offset = ((uint64_t)bufp) - tag; | |
6366 | -- | |
6367 | -- /* | |
6368 | -- * Sigh now complete the store on behalf of the original | |
6369 | -- * store instruction | |
6370 | -- */ | |
6371 | -- | |
6372 | -- ptr = (uint8_t*)(xclp->offset + va); | |
6373 | -- | |
6374 | -- switch(bytes) { | |
6375 | -- case 1: *(uint8_t*)ptr = val; break; | |
6376 | -- case 2: *(uint16_t*)ptr = val; break; | |
6377 | -- case 4: *(uint32_t*)ptr = val; break; | |
6378 | -- case 8: *(uint64_t*)ptr = val; break; | |
6379 | -- default: abort(); | |
6380 | -- } | |
6381 | -- | |
6382 | -- | |
6383 | -- /* | |
6384 | -- * Finally go get the next instruction | |
6385 | -- */ | |
6386 | --store_done:; | |
6387 | -- | |
6388 | -- NEXT_INSTN(sp); | |
6389 | --} | |
6390 | -- | |
6391 | -- | |
6392 | #endif /* } */ | |
6393 | ||
6394 | ||
6395 | /* | |
6396 | * called in response to ~n | |
6397 | */ | |
6398 | void ss_dump_instruction_counts(config_proc_t * cp) | |
6399 | { | |
6400 | #if !defined(NDEBUG) /* { */ | |
6401 | ss_proc_t * np; | |
6402 | sparcv9_cpu_t * sv9p; | |
6403 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
6404 | ss_strand_t * nsp; | |
6405 | #endif /* } */ | |
6406 | simcpu_t * sp; | |
6407 | uint64_t hi_cycle = 0; | |
6408 | uint64_t lo_cycle = 0xFFFFFFFFFFFFFFFF; | |
6409 | uint64_t now; | |
6410 | uint_t j; | |
6411 | uint_t ncpus; | |
6412 | ||
6413 | np = (ss_proc_t*)(cp->procp); | |
6414 | ||
6415 | ncpus = np->nstrands; | |
6416 | if (ncpus > 1) | |
6417 | lprintf(-1, "==== strand counts proc %d ====\n", cp->proc_id); | |
6418 | for (j=0; j<ncpus; j++) { | |
6419 | sv9p = np->strand[j]; | |
6420 | sp = sv9p->simp; | |
6421 | now = sp->cycle; | |
6422 | ||
6423 | if (now > hi_cycle) | |
6424 | hi_cycle = now; | |
6425 | if (now < lo_cycle) | |
6426 | lo_cycle = now; | |
6427 | lprintf(sp->gid, "@ pc=0x%llx tl=%d: cycle[%llu] instr[%llu] " | |
6428 | "cycle_target[%lld] \n", | |
6429 | sp->pc, sv9p->tl, sp->cycle, ICOUNT(sp), | |
6430 | sp->cycle_target); | |
6431 | ||
6432 | #if defined(NIAGARA1) || defined(NIAGARA2) /* { */ | |
6433 | nsp = sv9p->impl_specificp; | |
6434 | if (nsp->pcr != 0 || nsp->pic0 != 0 || nsp->pic1 != 0) { | |
6435 | lprintf(sp->gid, "%%pcr=0x%llx %%pic0=0x%x " | |
6436 | "%%pic1=0x%x\n", | |
6437 | nsp->pcr, nsp->pic0, nsp->pic1); | |
6438 | } | |
6439 | #endif /* } */ | |
6440 | ||
6441 | } | |
6442 | if (ncpus > 1) | |
6443 | lprintf(sp->gid, "hi_cycle - lo_cycle=%llu\n", hi_cycle - lo_cycle); | |
6444 | ||
6445 | #endif /* } */ | |
6446 | } | |
6447 | ||
6448 | /* | |
6449 | * This function is called upon each update to the running status register. | |
6450 | * It performs a bit test (each bit corresponding to a virtual core or strand) | |
6451 | * and park or unpark the corresponding strand depending on the bit value. | |
6452 | */ | |
6453 | void | |
6454 | ss_change_exec_state(ss_proc_t *npp, uint64_t running_status) | |
6455 | { | |
6456 | uint_t idx, ncpu, vcore_id; | |
6457 | ||
6458 | ncpu = npp->nstrands; | |
6459 | ||
6460 | for (idx = 0; idx < ncpu; idx++) { | |
6461 | sparcv9_cpu_t *tv9p; | |
6462 | simcpu_t *tsp; | |
6463 | ss_strand_t *tnsp; | |
6464 | uint_t unparked; | |
6465 | ||
6466 | tnsp = &(npp->ss_strandp[idx]); | |
6467 | tv9p = npp->strand[idx]; | |
6468 | tsp = tv9p->simp; | |
6469 | ||
6470 | vcore_id = tnsp->vcore_id; | |
6471 | unparked = ((running_status >> vcore_id) & 0x1); | |
6472 | ||
6473 | if (unparked) { | |
6474 | #if defined(NIAGARA1) /* { */ | |
6475 | SET_THREAD_STS_SFSM(npp, tnsp, THREAD_STS_TSTATE_RUN); | |
6476 | #endif /* } NIAGARA1 */ | |
6477 | if (PARKED(tsp)) | |
6478 | simcore_cpu_state_unpark(tsp); | |
6479 | } else { | |
6480 | #if defined(NIAGARA1) /* { */ | |
6481 | SET_THREAD_STS_SFSM(npp, tnsp, THREAD_STS_TSTATE_IDLE); | |
6482 | #endif /* } NIAGARA1 */ | |
6483 | if (!PARKED(tsp)) | |
6484 | simcore_cpu_state_park(tsp); | |
6485 | } | |
6486 | } | |
6487 | } | |
6488 | ||
6489 | /* | |
6490 | * Given a PA from hypervisor, lookup where in simualted | |
6491 | * memory that PA resides so we can read an address | |
6492 | * local to the simulator. | |
6493 | */ | |
6494 | uint64_t* lookup_sim_addr(simcpu_t *sp, uint64_t *sim_addr) | |
6495 | { | |
6496 | uint64_t extent; | |
6497 | config_addr_t *cap; | |
6498 | domain_t *domainp; | |
6499 | config_proc_t *config_procp; | |
6500 | uint64_t *bufp; | |
6501 | ||
6502 | config_procp = sp->config_procp; | |
6503 | domainp = config_procp->domainp; | |
6504 | ||
6505 | /* | |
6506 | * Lookup which memory device owns this address | |
6507 | * XXX FIXME: We know it's in memory, so we could cache it here | |
6508 | */ | |
6509 | cap = find_domain_address(domainp, (tpaddr_t)sim_addr); | |
6510 | if (cap == NULL) { | |
6511 | /* it's a bus error there was no backing store */ | |
6512 | EXEC_WARNING(("bus error - (@pc=0x%llx, icount=%llu) access to addr=0x%llx ", | |
6513 | sp->pc, ICOUNT(sp), sim_addr)); | |
6514 | abort(); | |
6515 | } | |
6516 | ||
6517 | /* | |
6518 | * Use the device function to read the memory address | |
6519 | */ | |
6520 | extent = cap->config_devp->dev_typep->dev_cacheable(cap, DA_Load, | |
6521 | (uint64_t)sim_addr - cap->baseaddr, (uint8_t **)&bufp); | |
6522 | ASSERT(extent != 0); | |
6523 | ||
6524 | return(bufp); | |
6525 | } | |
6526 | ||
6527 | ||
6528 | /* | |
6529 | * Get the virtual core Id (or CPU Id) of the calling strand. | |
6530 | */ | |
6531 | uint_t ss_get_cpuid(simcpu_t *sp) | |
6532 | { | |
6533 | sparcv9_cpu_t *v9p; | |
6534 | ss_strand_t *nsp; | |
6535 | ||
6536 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
6537 | nsp = v9p->impl_specificp; | |
6538 | ||
6539 | return (nsp->vcore_id); | |
6540 | } | |
6541 | ||
6542 | /* | |
6543 | * Being used when switching to SST mode to simulate CPU's behaviour | |
6544 | * of trashing all registers. | |
6545 | */ | |
6546 | void ss_trash_regs(sparcv9_cpu_t * v9p, uint64_t val) | |
6547 | { | |
6548 | simcpu_t * sp; | |
6549 | uint_t idx; | |
6550 | ||
6551 | sp = v9p->simp; | |
6552 | ||
6553 | /* trash globals */ | |
6554 | for (idx = 0; idx < v9p->nglobals * 8; idx++){ | |
6555 | /* need to leave %g0 alone */ | |
6556 | if (idx % 8) | |
6557 | v9p->globalsp[idx] = val; | |
6558 | } | |
6559 | ||
6560 | /* trash ins and locals */ | |
6561 | for (idx = 0; idx < (v9p->nwins * 2 * V9_REG_GROUP); idx++) | |
6562 | v9p->winsp[idx] = val; | |
6563 | ||
6564 | /* trash floating point regs */ | |
6565 | for (idx = 0; idx < NFP_64; idx++) | |
6566 | sp->fpreg.s64[idx] = val; | |
6567 | ||
6568 | /* trash what's in the working register file */ | |
6569 | for (idx = 1; idx < NINT; idx++) | |
6570 | sp->intreg[idx] = val; | |
6571 | } | |
6572 | ||
6573 | /* | |
6574 | * Macro to create symbols for the debug_hook functions | |
6575 | */ | |
6576 | #define ADD_DBG_DEFS( func ) extern void dbg_##func##(simcpu_t *sp, uint32_t rawi);\ | |
6577 | extern void dbg_##func##_parse(void); \ | |
6578 | extern void dbg_##func##_dump(void) | |
6579 | ADD_DBG_DEFS(trace); | |
6580 | ADD_DBG_DEFS(coverage); | |
6581 | ADD_DBG_DEFS(debug_log); | |
6582 | ADD_DBG_DEFS(lockstep); | |
6583 | ||
6584 | /* | |
6585 | * Parse the debug_hook directive. | |
6586 | * The debug_hook directive in the conf file should look like this: | |
6587 | * | |
6588 | * directive_name debug_hook ..... | |
6589 | * -------------- ---------- | |
6590 | * eg: | |
6591 | * debug_hook trace ..... ; | |
6592 | * \________________________/ \____________/ | |
6593 | * we parse this much here debug_hook_parse() | |
6594 | * parses the rest if any | |
6595 | * | |
6596 | * We parse the 'debug_hook' as the name of the function the user | |
6597 | * is requesting. We then use this name to locate the following functions: | |
6598 | * debug_hook - execloop calls this for each instn | |
6599 | * debug_hook_parse - we call this to parse the rest of the directive | |
6600 | * debug_hook_dump - dumbserial calls this when ~l is input on | |
6601 | * the console | |
6602 | * | |
6603 | */ | |
6604 | ||
6605 | static void parse_debug_hook(ss_proc_t *procp) | |
6606 | { | |
6607 | lexer_tok_t tok; | |
6608 | char hookname[BUFSIZ]; | |
6609 | char hookname_parse[BUFSIZ]; | |
6610 | char hookname_dump[BUFSIZ]; | |
6611 | void (*hookp)(uint_t); | |
6612 | void (*hook_parsep)(void); | |
6613 | void (*hook_dumpp)(uint_t); | |
6614 | ||
6615 | tok = lex_get_token(); /* parse debug_hook function name */ | |
6616 | sprintf(hookname, "%s", lex.strp); | |
6617 | sprintf(hookname_parse, "%s_parse", hookname); /* debug_hook_parse() */ | |
6618 | sprintf(hookname_dump, "%s_dump", hookname); /* debug_hook_dump() */ | |
6619 | ||
6620 | if (streq(hookname, "trace")) { | |
6621 | hookp = (void(*)(uint_t))dbg_trace; | |
6622 | hook_parsep = (void(*)(void))dbg_trace_parse; | |
6623 | hook_dumpp = (void(*)(uint_t))dbg_trace_dump; | |
6624 | } else if (streq(hookname, "coverage")) { | |
6625 | hookp = (void(*)(uint_t))dbg_coverage; | |
6626 | hook_parsep = (void(*)(void))dbg_coverage_parse; | |
6627 | hook_dumpp = (void(*)(uint_t))dbg_coverage_dump; | |
6628 | } else if (streq(hookname, "debug_log")) { | |
6629 | hookp = (void(*)(uint_t))dbg_debug_log; | |
6630 | hook_parsep = (void(*)(void))dbg_debug_log_parse; | |
6631 | hook_dumpp = (void(*)(uint_t))dbg_debug_log_dump; | |
6632 | } else if (streq(hookname, "lockstep")) { | |
6633 | hookp = (void(*)(uint_t))dbg_lockstep; | |
6634 | hook_parsep = (void(*)(void))dbg_lockstep_parse; | |
6635 | hook_dumpp = (void(*)(uint_t))dbg_lockstep_dump; | |
6636 | } else { | |
6637 | lex_fatal("Unsupported debug_hook %s", hookname); | |
6638 | } | |
6639 | ||
6640 | procp->config_procp->proc_typep->debug_hookp = (void*)hookp; | |
6641 | procp->config_procp->proc_typep->debug_hook_dumpp = (void*)hook_dumpp; | |
6642 | ||
6643 | /* | |
6644 | * Now call the parse function to continue parsing the | |
6645 | * debug_hook directive. | |
6646 | */ | |
6647 | hook_parsep(); | |
6648 | ||
6649 | lex_get(T_S_Colon); | |
6650 | ||
6651 | return; | |
6652 | } | |
6653 | ||
6654 | #define PRI_CTX_IS_NUCLEUS(_nsp) ((_nsp)->pri_context == SS_NUCLEUS_CONTEXT) | |
6655 | ||
6656 | void | |
6657 | xcache_set_tagstate(simcpu_t * sp) | |
6658 | { | |
6659 | ss_strand_t * nsp; | |
6660 | sparcv9_cpu_t * v9p; | |
6661 | uint32_t newstate; | |
6662 | ||
6663 | v9p = (sparcv9_cpu_t *)(sp->specificp); | |
6664 | nsp = v9p->impl_specificp; | |
6665 | ||
6666 | if (nsp->mmu_bypass) | |
6667 | newstate = XCACHE_TAGSTATE_PHYS; | |
6668 | else | |
6669 | if (v9p->state == V9_User) { | |
6670 | newstate = (v9p->tl == 0 && !PRI_CTX_IS_NUCLEUS(nsp)) ? | |
6671 | XCACHE_TAGSTATE_TL0_U : XCACHE_TAGSTATE_TLN_U; | |
6672 | } | |
6673 | else { | |
6674 | newstate = (v9p->tl == 0 && !PRI_CTX_IS_NUCLEUS(nsp)) ? | |
6675 | XCACHE_TAGSTATE_TL0 : XCACHE_TAGSTATE_TLN; | |
6676 | } | |
6677 | DBGXCACHE( lprintf(sp->gid, "xcache_set_tagstate: %u -> %u " | |
6678 | "pc=0x%llx [cycle=0x%llx]\n", | |
6679 | sp->tagstate >> _XCACHE_TAGSTATE_SHIFT, | |
6680 | newstate >> _XCACHE_TAGSTATE_SHIFT, sp->pc, sp->cycle); ); | |
6681 | ||
6682 | if (sp->tagstate != newstate) | |
6683 | sp->exec_loop_reset = true; | |
6684 | ||
6685 | sp->tagstate = newstate; | |
6686 | } | |
6687 | ||
6688 | void | |
6689 | ss_rdlock(RW_lock_t *lp) | |
6690 | { | |
6691 | RW_lock_t l, n, v; | |
6692 | ||
6693 | again: | |
6694 | while ((l = *lp) < 0) | |
6695 | ; | |
6696 | n = l + 1; | |
6697 | v = host_cas64((uint64_t *)lp, l, n); | |
6698 | if (v != l) | |
6699 | goto again; | |
6700 | } | |
6701 | ||
6702 | ||
6703 | void | |
6704 | ss_wrlock(RW_lock_t *lp) | |
6705 | { | |
6706 | RW_lock_t l, n, v; | |
6707 | ||
6708 | n = -1; | |
6709 | again: | |
6710 | while ((l = *lp) != 0) | |
6711 | ; | |
6712 | v = host_cas64((uint64_t *)lp, l, n); | |
6713 | if (v != l) | |
6714 | goto again; | |
6715 | } | |
6716 | ||
6717 | ||
6718 | void | |
6719 | ss_unlock(RW_lock_t *lp) | |
6720 | { | |
6721 | RW_lock_t l, n, v; | |
6722 | ||
6723 | again: | |
6724 | l = *lp; | |
6725 | if (l > 0) { | |
6726 | n = l - 1; | |
6727 | } else | |
6728 | if (l < 0) { | |
6729 | n = 0; | |
6730 | } else | |
6731 | fatal("lock error"); | |
6732 | v = host_cas64((uint64_t *)lp, l, n); | |
6733 | if (v != l) | |
6734 | goto again; | |
6735 | } | |
6736 | ||
6737 | /* | |
6738 | * Memory images are written out in 2 formats. Binary format and | |
6739 | * text format. With the binary format, a partial legion config | |
6740 | * file is also dumped which contains the memory directives for | |
6741 | * loading in the various memory binary images. | |
6742 | * | |
6743 | * Memory images are stored in a text file with the following format: | |
6744 | * | |
6745 | * @addr | |
6746 | * 8d41400010800036 0100000000000000 0000000000000000 0000000000000000 | |
6747 | * 8d41400010800036 0100000000000000 0000000000000000 0000000000000000 | |
6748 | * 8d4140008c21a020 1080002601000000 108004e601000000 0000000000000000 | |
6749 | * 1080052901000000 0000000000000000 0000000000000000 0000000000000000 | |
6750 | * 1080056b01000000 0000000000000000 0000000000000000 0000000000000000 | |
6751 | * 108005aa01000000 0000000000000000 0000000000000000 0000000000000000 | |
6752 | * 108005e901000000 0000000000000000 0000000000000000 0000000000000000 | |
6753 | * 03004000c221a000 c221a004c221a008 c221a00c10800240 0100000082102018 | |
6754 | * @addr//+size | |
6755 | * | |
6756 | * where : | |
6757 | * @addr is the starting address of a chunk of memory followed by | |
6758 | * a mimimum of 256 bytes of non-zero data. | |
6759 | * | |
6760 | * If a range of memory is all zeroes, it is recorded as: | |
6761 | * @addr//+size | |
6762 | */ | |
6763 | void | |
6764 | save_memory_segment_axis(config_dev_t *cdp, FILE *filep) | |
6765 | { | |
6766 | uint64_t *legion_mem_basep; | |
6767 | uint64_t data_chunk[32]; | |
6768 | uint64_t off = 0; | |
6769 | uint64_t foo_addr = 0; | |
6770 | uint64_t skip_cnt = 0; | |
6771 | int i; | |
6772 | ||
6773 | #define MEM_IMAGE_DATA_SIZE 32 | |
6774 | ||
6775 | /* Get the base address of the Guest Mem segment */ | |
6776 | if (cdp->dev_typep->dev_cacheable(cdp->addrp, DA_Load, 0, (uint8_t**)&legion_mem_basep) == 0) | |
6777 | fatal("Failed to locate base address for memory segment @ 0x%llx\n", cdp); | |
6778 | ||
6779 | fprintf(filep, "\n//Memory from %llx to %llx range %llx - %p", | |
6780 | cdp->addrp->baseaddr, | |
6781 | cdp->addrp->topaddr, | |
6782 | cdp->addrp->range, | |
6783 | cdp); | |
6784 | ||
6785 | ||
6786 | /* | |
6787 | * process memory in 256 byte chunks | |
6788 | */ | |
6789 | while (off <= (cdp->addrp->range - 256)) { | |
6790 | bool_t has_data = false; | |
6791 | uint64_t start_addr; | |
6792 | uint64_t legion_addr; | |
6793 | ||
6794 | start_addr = cdp->addrp->baseaddr + off; | |
6795 | legion_addr = start_addr; | |
6796 | ||
6797 | /* | |
6798 | * Scan a 256 Byte chunk of memory at a time | |
6799 | * and mark it if it has any non-zero data. | |
6800 | */ | |
6801 | for (i = 0; i < MEM_IMAGE_DATA_SIZE; i++) { | |
6802 | data_chunk[i] = *legion_mem_basep++; | |
6803 | ||
6804 | /* | |
6805 | * apply any memory patch here | |
6806 | */ | |
6807 | if (options.save_restore.mem_patch.addr != 0x0) { | |
6808 | if (legion_addr == options.save_restore.mem_patch.addr) { | |
6809 | printf("Patching addr (0x%llx) from 0x%llx to 0x%llx\n", | |
6810 | legion_addr, data_chunk[i], | |
6811 | options.save_restore.mem_patch.val); | |
6812 | data_chunk[i] = options.save_restore.mem_patch.val; | |
6813 | } | |
6814 | } | |
6815 | off += 8; | |
6816 | legion_addr += 8; | |
6817 | ||
6818 | if (data_chunk[i] != 0ULL) { | |
6819 | /* | |
6820 | * There's non-zero data in this chunk | |
6821 | * so we flag this so we can print the | |
6822 | * contents of the 256byte chunk. | |
6823 | */ | |
6824 | has_data = true; | |
6825 | } | |
6826 | } | |
6827 | ||
6828 | /* | |
6829 | * If memory chunk is non-zero, print it | |
6830 | * otherwise, keep a track of zero chunks | |
6831 | * so we can print a summary before the next | |
6832 | * non-zero chunk. | |
6833 | */ | |
6834 | if (has_data) { | |
6835 | /* print addr for first chunk of memory */ | |
6836 | if (start_addr == cdp->addrp->baseaddr) { | |
6837 | fprintf(filep, "\n@%llx\n", start_addr); | |
6838 | } | |
6839 | ||
6840 | /* | |
6841 | * We've got data to print, lets check if we have | |
6842 | * skipped over any zero chunks of memory and print a | |
6843 | * summary of these first. | |
6844 | */ | |
6845 | if (skip_cnt != 0) { | |
6846 | fprintf(filep, "\n@%llx//+%llx\n", | |
6847 | foo_addr, skip_cnt * 256); | |
6848 | skip_cnt = 0; | |
6849 | foo_addr = 0x0ULL; | |
6850 | ||
6851 | /* | |
6852 | * Now, print the starting @addr of the new | |
6853 | * non-zero chunk | |
6854 | */ | |
6855 | fprintf(filep, "\n@%llx\n", start_addr); | |
6856 | } | |
6857 | ||
6858 | /* Print the 256 bytes of data */ | |
6859 | for (i=0; i< MEM_IMAGE_DATA_SIZE; i++) { | |
6860 | fprintf(filep, "%016llx ", data_chunk[i]); | |
6861 | if ((i & 0x3) == 0x3) | |
6862 | fprintf(filep, "\n"); | |
6863 | } | |
6864 | ||
6865 | } else { | |
6866 | /* chunk was all zeros so, just keep a count */ | |
6867 | if (skip_cnt == 0) { | |
6868 | /* save start address of zero chunk */ | |
6869 | foo_addr = start_addr; | |
6870 | } | |
6871 | skip_cnt++; | |
6872 | } | |
6873 | } | |
6874 | ||
6875 | /* Before we finish, check if we have any zero chunks to print */ | |
6876 | if (skip_cnt != 0) { | |
6877 | fprintf(filep, "\n@%llx//+%llx\n", | |
6878 | foo_addr, skip_cnt * 256); | |
6879 | } | |
6880 | } | |
6881 | ||
6882 | void | |
6883 | save_memory_segment_binary(config_dev_t *cdp, FILE *filep) | |
6884 | { | |
6885 | uint64_t *legion_mem_basep; | |
6886 | uint64_t *datap; | |
6887 | uint64_t size = cdp->addrp->range; | |
6888 | uint64_t start_addr = cdp->addrp->baseaddr; | |
6889 | uint64_t end_addr = cdp->addrp->baseaddr + size; | |
6890 | char filename[MAXPATHLEN]; | |
6891 | int fd = 0; | |
6892 | static int file_cnt = 0; | |
6893 | mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO; | |
6894 | ||
6895 | /* | |
6896 | * Open new file | |
6897 | * mmap in new file | |
6898 | * bcopy from legion memory to new file | |
6899 | * unmap file | |
6900 | */ | |
6901 | /* Get the base address of the Guest Mem segment */ | |
6902 | if (cdp->dev_typep->dev_cacheable(cdp->addrp, DA_Load, 0, (uint8_t**)&legion_mem_basep) == 0) | |
6903 | fatal("Failed to locate base address for memory segment @ 0x%llx\n", cdp); | |
6904 | ||
6905 | sprintf(filename, "mem.image.%d", file_cnt++); | |
6906 | lprintf(-1, "Writing %s basep=0x%llx, range=0x%llx (0x%llx)\n", | |
6907 | filename, start_addr, size, legion_mem_basep); | |
6908 | ||
6909 | /* if file already exists, delete it */ | |
6910 | if (file_exists(filename)) | |
6911 | unlink(filename); | |
6912 | ||
6913 | /* Open new output file and set size */ | |
6914 | if ((fd = open(filename, O_RDWR | O_CREAT, mode)) == -1) | |
6915 | fatal("Error opening %s for writing\n", filename); | |
6916 | ||
6917 | /* Set the size of the file by seeking to the end and write NULL */ | |
6918 | if (lseek(fd, size-1, SEEK_SET) < 0) | |
6919 | fatal("Could not create file %s of size [0x%llx]", | |
6920 | filename, size); | |
6921 | ||
6922 | if (write(fd, "", 1) != 1) | |
6923 | fatal("Could not write to file %s of size [0x%llx]", | |
6924 | filename, size); | |
6925 | ||
6926 | /* mmap file file */ | |
6927 | datap = (uint64_t*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | |
6928 | if (datap == MAP_FAILED) | |
6929 | fatal("Failed to mmap %s\n", filename); | |
6930 | ||
6931 | /* bcopy from legion memory segment to newly mmaped file */ | |
6932 | bcopy(legion_mem_basep, datap, size); | |
6933 | ||
6934 | /* | |
6935 | * apply any memory patch to the newly created binary | |
6936 | */ | |
6937 | if (options.save_restore.mem_patch.addr != 0x0) { | |
6938 | ||
6939 | uint64_t *patch_addr; | |
6940 | uint64_t offset; | |
6941 | ||
6942 | if ((start_addr <= options.save_restore.mem_patch.addr) && | |
6943 | (end_addr >= options.save_restore.mem_patch.addr)) { | |
6944 | ||
6945 | offset = options.save_restore.mem_patch.addr - start_addr; | |
6946 | patch_addr = (uint64_t *)((uint64_t)datap + (uint64_t)offset); | |
6947 | ||
6948 | printf("Patching addr (0x%llx) from 0x%llx to 0x%llx\n", | |
6949 | options.save_restore.mem_patch.addr, *patch_addr, | |
6950 | options.save_restore.mem_patch.val); | |
6951 | ||
6952 | /* apply patch */ | |
6953 | *patch_addr = options.save_restore.mem_patch.val; | |
6954 | } | |
6955 | } | |
6956 | ||
6957 | if (munmap((void*)datap, size) != 0) | |
6958 | fatal("Error munmapping file %s\n", filename); | |
6959 | ||
6960 | /* write out legion memory directive to load this back in */ | |
6961 | fprintf(filep, "\ndevice \"memory\" 0x%llx +0x%llx {\n" \ | |
6962 | " load +0x0 \"%s\"; \n" \ | |
6963 | "}\n", | |
6964 | start_addr, size, filename); | |
6965 | ||
6966 | close(fd); | |
6967 | } | |
6968 | ||
6969 | ||
6970 | bool_t | |
6971 | ss_save_state(simcpu_t *sp) | |
6972 | { | |
6973 | domain_t *domainp = sp->config_procp->domainp; | |
6974 | config_dev_t *cdp = NULL; | |
6975 | FILE *mem_image_filep = 0; | |
6976 | FILE *config_filep = 0; | |
6977 | ||
6978 | if (options.save_restore.legion_format) { | |
6979 | ||
6980 | /* | |
6981 | * For legion, we dump out memory images in binary | |
6982 | * format and then print a partial legion config file | |
6983 | * called restore.conf . | |
6984 | * This file contains the memory directives for loading | |
6985 | * these binary memory images back into legion. | |
6986 | */ | |
6987 | lprintf(sp->gid, "ss_save_state about to save system memory " \ | |
6988 | "in binary format\nWait for Legion to save each memory " \ | |
6989 | "image and print a Completed message.\n"); | |
6990 | ||
6991 | if ((config_filep = fopen("restore.conf", "w")) == NULL) | |
6992 | fatal("Error opening %s for writing\n", "restore.conf"); | |
6993 | ||
6994 | /* Locate the all memory segments, save them out in binary format */ | |
6995 | for (cdp = domainp->device.listp; cdp!=(config_dev_t*)0; cdp=cdp->nextp) { | |
6996 | if (streq(cdp->dev_typep->dev_type_namep, "memory")) { | |
6997 | save_memory_segment_binary(cdp, config_filep); | |
6998 | } | |
6999 | } | |
7000 | fclose(config_filep); | |
7001 | ||
7002 | } else { | |
7003 | /* | |
7004 | * For axis, we dump out memory images in txt format | |
7005 | */ | |
7006 | lprintf(sp->gid, "ss_save_state about to save system " \ | |
7007 | "memory to %s in Axis format\n", | |
7008 | options.save_restore.filenamep); | |
7009 | ||
7010 | if ((mem_image_filep = | |
7011 | fopen(options.save_restore.filenamep, "w")) == NULL) { | |
7012 | fatal("Error opening %s for writing\n", | |
7013 | options.save_restore.filenamep); | |
7014 | } | |
7015 | ||
7016 | /* Locate the all memory segments, save them to text format */ | |
7017 | for (cdp = domainp->device.listp; cdp!=(config_dev_t*)0; cdp=cdp->nextp) { | |
7018 | if (streq(cdp->dev_typep->dev_type_namep, "memory")) { | |
7019 | save_memory_segment_axis(cdp, mem_image_filep); | |
7020 | } | |
7021 | } | |
7022 | fclose(mem_image_filep); | |
7023 | } | |
7024 | ||
7025 | return (true); | |
7026 | ||
7027 | } |