xref: /openbmc/qemu/target/cris/op_helper.c (revision d9cb4336)
1 /*
2  *  CRIS helper routines
3  *
4  *  Copyright (c) 2007 AXIS Communications
5  *  Written by Edgar E. Iglesias
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "mmu.h"
24 #include "exec/helper-proto.h"
25 #include "qemu/host-utils.h"
26 #include "exec/exec-all.h"
27 #include "exec/cpu_ldst.h"
28 
29 //#define CRIS_OP_HELPER_DEBUG
30 
31 
32 #ifdef CRIS_OP_HELPER_DEBUG
33 #define D(x) x
34 #define D_LOG(...) qemu_log(__VA_ARGS__)
35 #else
36 #define D(x)
37 #define D_LOG(...) do { } while (0)
38 #endif
39 
40 void helper_raise_exception(CPUCRISState *env, uint32_t index)
41 {
42     CPUState *cs = CPU(cris_env_get_cpu(env));
43 
44     cs->exception_index = index;
45     cpu_loop_exit(cs);
46 }
47 
48 void helper_tlb_flush_pid(CPUCRISState *env, uint32_t pid)
49 {
50 #if !defined(CONFIG_USER_ONLY)
51 	pid &= 0xff;
52 	if (pid != (env->pregs[PR_PID] & 0xff))
53 		cris_mmu_flush_pid(env, env->pregs[PR_PID]);
54 #endif
55 }
56 
57 void helper_spc_write(CPUCRISState *env, uint32_t new_spc)
58 {
59 #if !defined(CONFIG_USER_ONLY)
60     CRISCPU *cpu = cris_env_get_cpu(env);
61     CPUState *cs = CPU(cpu);
62 
63     tlb_flush_page(cs, env->pregs[PR_SPC]);
64     tlb_flush_page(cs, new_spc);
65 #endif
66 }
67 
68 /* Used by the tlb decoder.  */
69 #define EXTRACT_FIELD(src, start, end) \
70 	    (((src) >> start) & ((1 << (end - start + 1)) - 1))
71 
72 void helper_movl_sreg_reg(CPUCRISState *env, uint32_t sreg, uint32_t reg)
73 {
74 #if !defined(CONFIG_USER_ONLY)
75     CRISCPU *cpu = cris_env_get_cpu(env);
76 #endif
77 	uint32_t srs;
78 	srs = env->pregs[PR_SRS];
79 	srs &= 3;
80 	env->sregs[srs][sreg] = env->regs[reg];
81 
82 #if !defined(CONFIG_USER_ONLY)
83 	if (srs == 1 || srs == 2) {
84 		if (sreg == 6) {
85 			/* Writes to tlb-hi write to mm_cause as a side
86 			   effect.  */
87 			env->sregs[SFR_RW_MM_TLB_HI] = env->regs[reg];
88 			env->sregs[SFR_R_MM_CAUSE] = env->regs[reg];
89 		}
90 		else if (sreg == 5) {
91 			uint32_t set;
92 			uint32_t idx;
93 			uint32_t lo, hi;
94 			uint32_t vaddr;
95 			int tlb_v;
96 
97 			idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
98 			set >>= 4;
99 			set &= 3;
100 
101 			idx &= 15;
102 			/* We've just made a write to tlb_lo.  */
103 			lo = env->sregs[SFR_RW_MM_TLB_LO];
104 			/* Writes are done via r_mm_cause.  */
105 			hi = env->sregs[SFR_R_MM_CAUSE];
106 
107 			vaddr = EXTRACT_FIELD(env->tlbsets[srs-1][set][idx].hi,
108 					      13, 31);
109 			vaddr <<= TARGET_PAGE_BITS;
110 			tlb_v = EXTRACT_FIELD(env->tlbsets[srs-1][set][idx].lo,
111 					    3, 3);
112 			env->tlbsets[srs - 1][set][idx].lo = lo;
113 			env->tlbsets[srs - 1][set][idx].hi = hi;
114 
115 			D_LOG("tlb flush vaddr=%x v=%d pc=%x\n",
116 				  vaddr, tlb_v, env->pc);
117 			if (tlb_v) {
118                 tlb_flush_page(CPU(cpu), vaddr);
119 			}
120 		}
121 	}
122 #endif
123 }
124 
125 void helper_movl_reg_sreg(CPUCRISState *env, uint32_t reg, uint32_t sreg)
126 {
127 	uint32_t srs;
128 	env->pregs[PR_SRS] &= 3;
129 	srs = env->pregs[PR_SRS];
130 
131 #if !defined(CONFIG_USER_ONLY)
132 	if (srs == 1 || srs == 2)
133 	{
134 		uint32_t set;
135 		uint32_t idx;
136 		uint32_t lo, hi;
137 
138 		idx = set = env->sregs[SFR_RW_MM_TLB_SEL];
139 		set >>= 4;
140 		set &= 3;
141 		idx &= 15;
142 
143 		/* Update the mirror regs.  */
144 		hi = env->tlbsets[srs - 1][set][idx].hi;
145 		lo = env->tlbsets[srs - 1][set][idx].lo;
146 		env->sregs[SFR_RW_MM_TLB_HI] = hi;
147 		env->sregs[SFR_RW_MM_TLB_LO] = lo;
148 	}
149 #endif
150 	env->regs[reg] = env->sregs[srs][sreg];
151 }
152 
153 static void cris_ccs_rshift(CPUCRISState *env)
154 {
155 	uint32_t ccs;
156 
157 	/* Apply the ccs shift.  */
158 	ccs = env->pregs[PR_CCS];
159 	ccs = (ccs & 0xc0000000) | ((ccs & 0x0fffffff) >> 10);
160 	if (ccs & U_FLAG)
161 	{
162 		/* Enter user mode.  */
163 		env->ksp = env->regs[R_SP];
164 		env->regs[R_SP] = env->pregs[PR_USP];
165 	}
166 
167 	env->pregs[PR_CCS] = ccs;
168 }
169 
170 void helper_rfe(CPUCRISState *env)
171 {
172 	int rflag = env->pregs[PR_CCS] & R_FLAG;
173 
174 	D_LOG("rfe: erp=%x pid=%x ccs=%x btarget=%x\n",
175 		 env->pregs[PR_ERP], env->pregs[PR_PID],
176 		 env->pregs[PR_CCS],
177 		 env->btarget);
178 
179 	cris_ccs_rshift(env);
180 
181 	/* RFE sets the P_FLAG only if the R_FLAG is not set.  */
182 	if (!rflag)
183 		env->pregs[PR_CCS] |= P_FLAG;
184 }
185 
186 void helper_rfn(CPUCRISState *env)
187 {
188 	int rflag = env->pregs[PR_CCS] & R_FLAG;
189 
190 	D_LOG("rfn: erp=%x pid=%x ccs=%x btarget=%x\n",
191 		 env->pregs[PR_ERP], env->pregs[PR_PID],
192 		 env->pregs[PR_CCS],
193 		 env->btarget);
194 
195 	cris_ccs_rshift(env);
196 
197 	/* Set the P_FLAG only if the R_FLAG is not set.  */
198 	if (!rflag)
199 		env->pregs[PR_CCS] |= P_FLAG;
200 
201 	/* Always set the M flag.  */
202 	env->pregs[PR_CCS] |= M_FLAG_V32;
203 }
204 
205 uint32_t helper_btst(CPUCRISState *env, uint32_t t0, uint32_t t1, uint32_t ccs)
206 {
207 	/* FIXME: clean this up.  */
208 
209 	/* des ref:
210 	   The N flag is set according to the selected bit in the dest reg.
211 	   The Z flag is set if the selected bit and all bits to the right are
212 	   zero.
213 	   The X flag is cleared.
214 	   Other flags are left untouched.
215 	   The destination reg is not affected.*/
216 	unsigned int fz, sbit, bset, mask, masked_t0;
217 
218 	sbit = t1 & 31;
219 	bset = !!(t0 & (1 << sbit));
220 	mask = sbit == 31 ? -1 : (1 << (sbit + 1)) - 1;
221 	masked_t0 = t0 & mask;
222 	fz = !(masked_t0 | bset);
223 
224 	/* Clear the X, N and Z flags.  */
225 	ccs = ccs & ~(X_FLAG | N_FLAG | Z_FLAG);
226 	if (env->pregs[PR_VR] < 32)
227 		ccs &= ~(V_FLAG | C_FLAG);
228 	/* Set the N and Z flags accordingly.  */
229 	ccs |= (bset << 3) | (fz << 2);
230 	return ccs;
231 }
232 
233 static inline uint32_t evaluate_flags_writeback(CPUCRISState *env,
234                                                 uint32_t flags, uint32_t ccs)
235 {
236 	unsigned int x, z, mask;
237 
238 	/* Extended arithmetics, leave the z flag alone.  */
239 	x = env->cc_x;
240 	mask = env->cc_mask | X_FLAG;
241         if (x) {
242 		z = flags & Z_FLAG;
243 		mask = mask & ~z;
244 	}
245 	flags &= mask;
246 
247 	/* all insn clear the x-flag except setf or clrf.  */
248 	ccs &= ~mask;
249 	ccs |= flags;
250 	return ccs;
251 }
252 
253 uint32_t helper_evaluate_flags_muls(CPUCRISState *env,
254                                     uint32_t ccs, uint32_t res, uint32_t mof)
255 {
256 	uint32_t flags = 0;
257 	int64_t tmp;
258 	int dneg;
259 
260 	dneg = ((int32_t)res) < 0;
261 
262 	tmp = mof;
263 	tmp <<= 32;
264 	tmp |= res;
265 	if (tmp == 0)
266 		flags |= Z_FLAG;
267 	else if (tmp < 0)
268 		flags |= N_FLAG;
269 	if ((dneg && mof != -1)
270 	    || (!dneg && mof != 0))
271 		flags |= V_FLAG;
272         return evaluate_flags_writeback(env, flags, ccs);
273 }
274 
275 uint32_t helper_evaluate_flags_mulu(CPUCRISState *env,
276                                     uint32_t ccs, uint32_t res, uint32_t mof)
277 {
278 	uint32_t flags = 0;
279 	uint64_t tmp;
280 
281 	tmp = mof;
282 	tmp <<= 32;
283 	tmp |= res;
284 	if (tmp == 0)
285 		flags |= Z_FLAG;
286 	else if (tmp >> 63)
287 		flags |= N_FLAG;
288 	if (mof)
289 		flags |= V_FLAG;
290 
291         return evaluate_flags_writeback(env, flags, ccs);
292 }
293 
294 uint32_t helper_evaluate_flags_mcp(CPUCRISState *env, uint32_t ccs,
295 				   uint32_t src, uint32_t dst, uint32_t res)
296 {
297 	uint32_t flags = 0;
298 
299 	src = src & 0x80000000;
300 	dst = dst & 0x80000000;
301 
302 	if ((res & 0x80000000L) != 0L)
303 	{
304 		flags |= N_FLAG;
305 		if (!src && !dst)
306 			flags |= V_FLAG;
307 		else if (src & dst)
308 			flags |= R_FLAG;
309 	}
310 	else
311 	{
312 		if (res == 0L)
313 			flags |= Z_FLAG;
314 		if (src & dst)
315 			flags |= V_FLAG;
316 		if (dst | src)
317 			flags |= R_FLAG;
318 	}
319 
320         return evaluate_flags_writeback(env, flags, ccs);
321 }
322 
323 uint32_t helper_evaluate_flags_alu_4(CPUCRISState *env, uint32_t ccs,
324 				     uint32_t src, uint32_t dst, uint32_t res)
325 {
326 	uint32_t flags = 0;
327 
328 	src = src & 0x80000000;
329 	dst = dst & 0x80000000;
330 
331 	if ((res & 0x80000000L) != 0L)
332 	{
333 		flags |= N_FLAG;
334 		if (!src && !dst)
335 			flags |= V_FLAG;
336 		else if (src & dst)
337 			flags |= C_FLAG;
338 	}
339 	else
340 	{
341 		if (res == 0L)
342 			flags |= Z_FLAG;
343 		if (src & dst)
344 			flags |= V_FLAG;
345 		if (dst | src)
346 			flags |= C_FLAG;
347 	}
348 
349         return evaluate_flags_writeback(env, flags, ccs);
350 }
351 
352 uint32_t helper_evaluate_flags_sub_4(CPUCRISState *env, uint32_t ccs,
353 				     uint32_t src, uint32_t dst, uint32_t res)
354 {
355 	uint32_t flags = 0;
356 
357 	src = (~src) & 0x80000000;
358 	dst = dst & 0x80000000;
359 
360 	if ((res & 0x80000000L) != 0L)
361 	{
362 		flags |= N_FLAG;
363 		if (!src && !dst)
364 			flags |= V_FLAG;
365 		else if (src & dst)
366 			flags |= C_FLAG;
367 	}
368 	else
369 	{
370 		if (res == 0L)
371 			flags |= Z_FLAG;
372 		if (src & dst)
373 			flags |= V_FLAG;
374 		if (dst | src)
375 			flags |= C_FLAG;
376 	}
377 
378 	flags ^= C_FLAG;
379         return evaluate_flags_writeback(env, flags, ccs);
380 }
381 
382 uint32_t helper_evaluate_flags_move_4(CPUCRISState *env,
383                                       uint32_t ccs, uint32_t res)
384 {
385 	uint32_t flags = 0;
386 
387 	if ((int32_t)res < 0)
388 		flags |= N_FLAG;
389 	else if (res == 0L)
390 		flags |= Z_FLAG;
391 
392         return evaluate_flags_writeback(env, flags, ccs);
393 }
394 uint32_t helper_evaluate_flags_move_2(CPUCRISState *env,
395                                       uint32_t ccs, uint32_t res)
396 {
397 	uint32_t flags = 0;
398 
399 	if ((int16_t)res < 0L)
400 		flags |= N_FLAG;
401 	else if (res == 0)
402 		flags |= Z_FLAG;
403 
404         return evaluate_flags_writeback(env, flags, ccs);
405 }
406 
407 /* TODO: This is expensive. We could split things up and only evaluate part of
408    CCR on a need to know basis. For now, we simply re-evaluate everything.  */
409 void helper_evaluate_flags(CPUCRISState *env)
410 {
411 	uint32_t src, dst, res;
412 	uint32_t flags = 0;
413 
414 	src = env->cc_src;
415 	dst = env->cc_dest;
416 	res = env->cc_result;
417 
418 	if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP)
419 		src = ~src;
420 
421 	/* Now, evaluate the flags. This stuff is based on
422 	   Per Zander's CRISv10 simulator.  */
423 	switch (env->cc_size)
424 	{
425 		case 1:
426 			if ((res & 0x80L) != 0L)
427 			{
428 				flags |= N_FLAG;
429 				if (((src & 0x80L) == 0L)
430 				    && ((dst & 0x80L) == 0L))
431 				{
432 					flags |= V_FLAG;
433 				}
434 				else if (((src & 0x80L) != 0L)
435 					 && ((dst & 0x80L) != 0L))
436 				{
437 					flags |= C_FLAG;
438 				}
439 			}
440 			else
441 			{
442 				if ((res & 0xFFL) == 0L)
443 				{
444 					flags |= Z_FLAG;
445 				}
446 				if (((src & 0x80L) != 0L)
447 				    && ((dst & 0x80L) != 0L))
448 				{
449 					flags |= V_FLAG;
450 				}
451 				if ((dst & 0x80L) != 0L
452 				    || (src & 0x80L) != 0L)
453 				{
454 					flags |= C_FLAG;
455 				}
456 			}
457 			break;
458 		case 2:
459 			if ((res & 0x8000L) != 0L)
460 			{
461 				flags |= N_FLAG;
462 				if (((src & 0x8000L) == 0L)
463 				    && ((dst & 0x8000L) == 0L))
464 				{
465 					flags |= V_FLAG;
466 				}
467 				else if (((src & 0x8000L) != 0L)
468 					 && ((dst & 0x8000L) != 0L))
469 				{
470 					flags |= C_FLAG;
471 				}
472 			}
473 			else
474 			{
475 				if ((res & 0xFFFFL) == 0L)
476 				{
477 					flags |= Z_FLAG;
478 				}
479 				if (((src & 0x8000L) != 0L)
480 				    && ((dst & 0x8000L) != 0L))
481 				{
482 					flags |= V_FLAG;
483 				}
484 				if ((dst & 0x8000L) != 0L
485 				    || (src & 0x8000L) != 0L)
486 				{
487 					flags |= C_FLAG;
488 				}
489 			}
490 			break;
491 		case 4:
492 			if ((res & 0x80000000L) != 0L)
493 			{
494 				flags |= N_FLAG;
495 				if (((src & 0x80000000L) == 0L)
496 				    && ((dst & 0x80000000L) == 0L))
497 				{
498 					flags |= V_FLAG;
499 				}
500 				else if (((src & 0x80000000L) != 0L) &&
501 					 ((dst & 0x80000000L) != 0L))
502 				{
503 					flags |= C_FLAG;
504 				}
505 			}
506 			else
507 			{
508 				if (res == 0L)
509 					flags |= Z_FLAG;
510 				if (((src & 0x80000000L) != 0L)
511 				    && ((dst & 0x80000000L) != 0L))
512 					flags |= V_FLAG;
513 				if ((dst & 0x80000000L) != 0L
514 				    || (src & 0x80000000L) != 0L)
515 					flags |= C_FLAG;
516 			}
517 			break;
518 		default:
519 			break;
520 	}
521 
522 	if (env->cc_op == CC_OP_SUB || env->cc_op == CC_OP_CMP)
523 		flags ^= C_FLAG;
524 
525         env->pregs[PR_CCS] = evaluate_flags_writeback(env, flags,
526                                                       env->pregs[PR_CCS]);
527 }
528 
529 void helper_top_evaluate_flags(CPUCRISState *env)
530 {
531 	switch (env->cc_op)
532 	{
533 		case CC_OP_MCP:
534                         env->pregs[PR_CCS] = helper_evaluate_flags_mcp(env,
535 					env->pregs[PR_CCS], env->cc_src,
536 					env->cc_dest, env->cc_result);
537 			break;
538 		case CC_OP_MULS:
539                         env->pregs[PR_CCS] = helper_evaluate_flags_muls(env,
540 					env->pregs[PR_CCS], env->cc_result,
541 					env->pregs[PR_MOF]);
542 			break;
543 		case CC_OP_MULU:
544                         env->pregs[PR_CCS] = helper_evaluate_flags_mulu(env,
545 					env->pregs[PR_CCS], env->cc_result,
546 					env->pregs[PR_MOF]);
547 			break;
548 		case CC_OP_MOVE:
549 		case CC_OP_AND:
550 		case CC_OP_OR:
551 		case CC_OP_XOR:
552 		case CC_OP_ASR:
553 		case CC_OP_LSR:
554 		case CC_OP_LSL:
555 		switch (env->cc_size)
556 		{
557 			case 4:
558 				env->pregs[PR_CCS] =
559                                         helper_evaluate_flags_move_4(env,
560 							env->pregs[PR_CCS],
561 							env->cc_result);
562 				break;
563 			case 2:
564 				env->pregs[PR_CCS] =
565                                         helper_evaluate_flags_move_2(env,
566 							env->pregs[PR_CCS],
567 							env->cc_result);
568 				break;
569 			default:
570                                 helper_evaluate_flags(env);
571 				break;
572 		}
573 		break;
574 		case CC_OP_FLAGS:
575 			/* live.  */
576 			break;
577 		case CC_OP_SUB:
578 		case CC_OP_CMP:
579 			if (env->cc_size == 4)
580 				env->pregs[PR_CCS] =
581                                         helper_evaluate_flags_sub_4(env,
582 						env->pregs[PR_CCS],
583 						env->cc_src, env->cc_dest,
584 						env->cc_result);
585 			else
586                                 helper_evaluate_flags(env);
587 			break;
588 		default:
589 		{
590 			switch (env->cc_size)
591 			{
592 			case 4:
593 				env->pregs[PR_CCS] =
594                                         helper_evaluate_flags_alu_4(env,
595 						env->pregs[PR_CCS],
596 						env->cc_src, env->cc_dest,
597 						env->cc_result);
598 				break;
599 			default:
600                                 helper_evaluate_flags(env);
601 				break;
602 			}
603 		}
604 		break;
605 	}
606 }
607