xref: /openbmc/qemu/linux-user/hppa/cpu_loop.c (revision fc8c745d)
1 /*
2  *  qemu user cpu loop
3  *
4  *  Copyright (c) 2003-2008 Fabrice Bellard
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "qemu.h"
22 #include "cpu_loop-common.h"
23 
24 static abi_ulong hppa_lws(CPUHPPAState *env)
25 {
26     CPUState *cs = env_cpu(env);
27     uint32_t which = env->gr[20];
28     abi_ulong addr = env->gr[26];
29     abi_ulong old = env->gr[25];
30     abi_ulong new = env->gr[24];
31     abi_ulong size, ret;
32 
33     switch (which) {
34     default:
35         return -TARGET_ENOSYS;
36 
37     case 0: /* elf32 atomic 32bit cmpxchg */
38         if ((addr & 3) || !access_ok(cs, VERIFY_WRITE, addr, 4)) {
39             return -TARGET_EFAULT;
40         }
41         old = tswap32(old);
42         new = tswap32(new);
43         ret = qatomic_cmpxchg((uint32_t *)g2h(cs, addr), old, new);
44         ret = tswap32(ret);
45         break;
46 
47     case 2: /* elf32 atomic "new" cmpxchg */
48         size = env->gr[23];
49         if (size >= 4) {
50             return -TARGET_ENOSYS;
51         }
52         if (((addr | old | new) & ((1 << size) - 1))
53             || !access_ok(cs, VERIFY_WRITE, addr, 1 << size)
54             || !access_ok(cs, VERIFY_READ, old, 1 << size)
55             || !access_ok(cs, VERIFY_READ, new, 1 << size)) {
56             return -TARGET_EFAULT;
57         }
58         /* Note that below we use host-endian loads so that the cmpxchg
59            can be host-endian as well.  */
60         switch (size) {
61         case 0:
62             old = *(uint8_t *)g2h(cs, old);
63             new = *(uint8_t *)g2h(cs, new);
64             ret = qatomic_cmpxchg((uint8_t *)g2h(cs, addr), old, new);
65             ret = ret != old;
66             break;
67         case 1:
68             old = *(uint16_t *)g2h(cs, old);
69             new = *(uint16_t *)g2h(cs, new);
70             ret = qatomic_cmpxchg((uint16_t *)g2h(cs, addr), old, new);
71             ret = ret != old;
72             break;
73         case 2:
74             old = *(uint32_t *)g2h(cs, old);
75             new = *(uint32_t *)g2h(cs, new);
76             ret = qatomic_cmpxchg((uint32_t *)g2h(cs, addr), old, new);
77             ret = ret != old;
78             break;
79         case 3:
80             {
81                 uint64_t o64, n64, r64;
82                 o64 = *(uint64_t *)g2h(cs, old);
83                 n64 = *(uint64_t *)g2h(cs, new);
84 #ifdef CONFIG_ATOMIC64
85                 r64 = qatomic_cmpxchg__nocheck((uint64_t *)g2h(cs, addr),
86                                                o64, n64);
87                 ret = r64 != o64;
88 #else
89                 start_exclusive();
90                 r64 = *(uint64_t *)g2h(cs, addr);
91                 ret = 1;
92                 if (r64 == o64) {
93                     *(uint64_t *)g2h(cs, addr) = n64;
94                     ret = 0;
95                 }
96                 end_exclusive();
97 #endif
98             }
99             break;
100         }
101         break;
102     }
103 
104     env->gr[28] = ret;
105     return 0;
106 }
107 
108 void cpu_loop(CPUHPPAState *env)
109 {
110     CPUState *cs = env_cpu(env);
111     target_siginfo_t info;
112     abi_ulong ret;
113     int trapnr;
114 
115     while (1) {
116         cpu_exec_start(cs);
117         trapnr = cpu_exec(cs);
118         cpu_exec_end(cs);
119         process_queued_cpu_work(cs);
120 
121         switch (trapnr) {
122         case EXCP_SYSCALL:
123             ret = do_syscall(env, env->gr[20],
124                              env->gr[26], env->gr[25],
125                              env->gr[24], env->gr[23],
126                              env->gr[22], env->gr[21], 0, 0);
127             switch (ret) {
128             default:
129                 env->gr[28] = ret;
130                 /* We arrived here by faking the gateway page.  Return.  */
131                 env->iaoq_f = env->gr[31];
132                 env->iaoq_b = env->gr[31] + 4;
133                 break;
134             case -TARGET_ERESTARTSYS:
135             case -TARGET_QEMU_ESIGRETURN:
136                 break;
137             }
138             break;
139         case EXCP_SYSCALL_LWS:
140             env->gr[21] = hppa_lws(env);
141             /* We arrived here by faking the gateway page.  Return.  */
142             env->iaoq_f = env->gr[31];
143             env->iaoq_b = env->gr[31] + 4;
144             break;
145         case EXCP_ITLB_MISS:
146         case EXCP_DTLB_MISS:
147         case EXCP_NA_ITLB_MISS:
148         case EXCP_NA_DTLB_MISS:
149         case EXCP_IMP:
150         case EXCP_DMP:
151         case EXCP_DMB:
152         case EXCP_PAGE_REF:
153         case EXCP_DMAR:
154         case EXCP_DMPI:
155             info.si_signo = TARGET_SIGSEGV;
156             info.si_errno = 0;
157             info.si_code = TARGET_SEGV_ACCERR;
158             info._sifields._sigfault._addr = env->cr[CR_IOR];
159             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
160             break;
161         case EXCP_UNALIGN:
162             info.si_signo = TARGET_SIGBUS;
163             info.si_errno = 0;
164             info.si_code = 0;
165             info._sifields._sigfault._addr = env->cr[CR_IOR];
166             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
167             break;
168         case EXCP_ILL:
169         case EXCP_PRIV_OPR:
170         case EXCP_PRIV_REG:
171             info.si_signo = TARGET_SIGILL;
172             info.si_errno = 0;
173             info.si_code = TARGET_ILL_ILLOPN;
174             info._sifields._sigfault._addr = env->iaoq_f;
175             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
176             break;
177         case EXCP_OVERFLOW:
178         case EXCP_COND:
179         case EXCP_ASSIST:
180             info.si_signo = TARGET_SIGFPE;
181             info.si_errno = 0;
182             info.si_code = 0;
183             info._sifields._sigfault._addr = env->iaoq_f;
184             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
185             break;
186         case EXCP_DEBUG:
187             info.si_signo = TARGET_SIGTRAP;
188             info.si_errno = 0;
189             info.si_code = TARGET_TRAP_BRKPT;
190             queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info);
191             break;
192         case EXCP_INTERRUPT:
193             /* just indicate that signals should be handled asap */
194             break;
195         default:
196             g_assert_not_reached();
197         }
198         process_pending_signals(env);
199     }
200 }
201 
202 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
203 {
204     int i;
205     for (i = 1; i < 32; i++) {
206         env->gr[i] = regs->gr[i];
207     }
208     env->iaoq_f = regs->iaoq[0];
209     env->iaoq_b = regs->iaoq[1];
210 }
211