xref: /qemu/target/microblaze/op_helper.c (revision e1aa325409baeefad4f214ab8c34fb21968bdb2e)
14acb54baSEdgar E. Iglesias /*
24acb54baSEdgar E. Iglesias  *  Microblaze helper routines.
34acb54baSEdgar E. Iglesias  *
44acb54baSEdgar E. Iglesias  *  Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>.
54acb54baSEdgar E. Iglesias  *
64acb54baSEdgar E. Iglesias  * This library is free software; you can redistribute it and/or
74acb54baSEdgar E. Iglesias  * modify it under the terms of the GNU Lesser General Public
84acb54baSEdgar E. Iglesias  * License as published by the Free Software Foundation; either
94acb54baSEdgar E. Iglesias  * version 2 of the License, or (at your option) any later version.
104acb54baSEdgar E. Iglesias  *
114acb54baSEdgar E. Iglesias  * This library is distributed in the hope that it will be useful,
124acb54baSEdgar E. Iglesias  * but WITHOUT ANY WARRANTY; without even the implied warranty of
134acb54baSEdgar E. Iglesias  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
144acb54baSEdgar E. Iglesias  * Lesser General Public License for more details.
154acb54baSEdgar E. Iglesias  *
164acb54baSEdgar E. Iglesias  * You should have received a copy of the GNU Lesser General Public
178167ee88SBlue Swirl  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
184acb54baSEdgar E. Iglesias  */
194acb54baSEdgar E. Iglesias 
204acb54baSEdgar E. Iglesias #include <assert.h>
214acb54baSEdgar E. Iglesias #include "exec.h"
224acb54baSEdgar E. Iglesias #include "helper.h"
234acb54baSEdgar E. Iglesias #include "host-utils.h"
244acb54baSEdgar E. Iglesias 
254acb54baSEdgar E. Iglesias #define D(x)
264acb54baSEdgar E. Iglesias 
274acb54baSEdgar E. Iglesias #if !defined(CONFIG_USER_ONLY)
284acb54baSEdgar E. Iglesias #define MMUSUFFIX _mmu
294acb54baSEdgar E. Iglesias #define SHIFT 0
304acb54baSEdgar E. Iglesias #include "softmmu_template.h"
314acb54baSEdgar E. Iglesias #define SHIFT 1
324acb54baSEdgar E. Iglesias #include "softmmu_template.h"
334acb54baSEdgar E. Iglesias #define SHIFT 2
344acb54baSEdgar E. Iglesias #include "softmmu_template.h"
354acb54baSEdgar E. Iglesias #define SHIFT 3
364acb54baSEdgar E. Iglesias #include "softmmu_template.h"
374acb54baSEdgar E. Iglesias 
384acb54baSEdgar E. Iglesias /* Try to fill the TLB and return an exception if error. If retaddr is
394acb54baSEdgar E. Iglesias    NULL, it means that the function was called in C code (i.e. not
404acb54baSEdgar E. Iglesias    from generated code or from helper.c) */
414acb54baSEdgar E. Iglesias /* XXX: fix it to restore all registers */
424acb54baSEdgar E. Iglesias void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
434acb54baSEdgar E. Iglesias {
444acb54baSEdgar E. Iglesias     TranslationBlock *tb;
454acb54baSEdgar E. Iglesias     CPUState *saved_env;
464acb54baSEdgar E. Iglesias     unsigned long pc;
474acb54baSEdgar E. Iglesias     int ret;
484acb54baSEdgar E. Iglesias 
494acb54baSEdgar E. Iglesias     /* XXX: hack to restore env in all cases, even if not called from
504acb54baSEdgar E. Iglesias        generated code */
514acb54baSEdgar E. Iglesias     saved_env = env;
524acb54baSEdgar E. Iglesias     env = cpu_single_env;
534acb54baSEdgar E. Iglesias 
544acb54baSEdgar E. Iglesias     ret = cpu_mb_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
554acb54baSEdgar E. Iglesias     if (unlikely(ret)) {
564acb54baSEdgar E. Iglesias         if (retaddr) {
574acb54baSEdgar E. Iglesias             /* now we have a real cpu fault */
584acb54baSEdgar E. Iglesias             pc = (unsigned long)retaddr;
594acb54baSEdgar E. Iglesias             tb = tb_find_pc(pc);
604acb54baSEdgar E. Iglesias             if (tb) {
614acb54baSEdgar E. Iglesias                 /* the PC is inside the translated code. It means that we have
624acb54baSEdgar E. Iglesias                    a virtual CPU fault */
634acb54baSEdgar E. Iglesias                 cpu_restore_state(tb, env, pc, NULL);
644acb54baSEdgar E. Iglesias             }
654acb54baSEdgar E. Iglesias         }
664acb54baSEdgar E. Iglesias         cpu_loop_exit();
674acb54baSEdgar E. Iglesias     }
684acb54baSEdgar E. Iglesias     env = saved_env;
694acb54baSEdgar E. Iglesias }
704acb54baSEdgar E. Iglesias #endif
714acb54baSEdgar E. Iglesias 
724acb54baSEdgar E. Iglesias void helper_raise_exception(uint32_t index)
734acb54baSEdgar E. Iglesias {
744acb54baSEdgar E. Iglesias     env->exception_index = index;
754acb54baSEdgar E. Iglesias     cpu_loop_exit();
764acb54baSEdgar E. Iglesias }
774acb54baSEdgar E. Iglesias 
784acb54baSEdgar E. Iglesias void helper_debug(void)
794acb54baSEdgar E. Iglesias {
804acb54baSEdgar E. Iglesias     int i;
814acb54baSEdgar E. Iglesias 
824acb54baSEdgar E. Iglesias     qemu_log("PC=%8.8x\n", env->sregs[SR_PC]);
834c24aa0aSMichal Simek     qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n",
844c24aa0aSMichal Simek              env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR],
8517c52a43SEdgar E. Iglesias              env->debug, env->imm, env->iflags);
8617c52a43SEdgar E. Iglesias     qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n",
8717c52a43SEdgar E. Iglesias              env->btaken, env->btarget,
8817c52a43SEdgar E. Iglesias              (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel",
8917c52a43SEdgar E. Iglesias              (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel",
9017c52a43SEdgar E. Iglesias              (env->sregs[SR_MSR] & MSR_EIP),
9117c52a43SEdgar E. Iglesias              (env->sregs[SR_MSR] & MSR_IE));
924acb54baSEdgar E. Iglesias     for (i = 0; i < 32; i++) {
934acb54baSEdgar E. Iglesias         qemu_log("r%2.2d=%8.8x ", i, env->regs[i]);
944acb54baSEdgar E. Iglesias         if ((i + 1) % 4 == 0)
954acb54baSEdgar E. Iglesias             qemu_log("\n");
964acb54baSEdgar E. Iglesias     }
974acb54baSEdgar E. Iglesias     qemu_log("\n\n");
984acb54baSEdgar E. Iglesias }
994acb54baSEdgar E. Iglesias 
1004acb54baSEdgar E. Iglesias static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin)
1014acb54baSEdgar E. Iglesias {
1024acb54baSEdgar E. Iglesias     uint32_t cout = 0;
1034acb54baSEdgar E. Iglesias 
1044acb54baSEdgar E. Iglesias     if ((b == ~0) && cin)
1054acb54baSEdgar E. Iglesias         cout = 1;
1064acb54baSEdgar E. Iglesias     else if ((~0 - a) < (b + cin))
1074acb54baSEdgar E. Iglesias         cout = 1;
1084acb54baSEdgar E. Iglesias     return cout;
1094acb54baSEdgar E. Iglesias }
1104acb54baSEdgar E. Iglesias 
1114acb54baSEdgar E. Iglesias uint32_t helper_cmp(uint32_t a, uint32_t b)
1124acb54baSEdgar E. Iglesias {
1134acb54baSEdgar E. Iglesias     uint32_t t;
1144acb54baSEdgar E. Iglesias 
1154acb54baSEdgar E. Iglesias     t = b + ~a + 1;
1164acb54baSEdgar E. Iglesias     if ((b & 0x80000000) ^ (a & 0x80000000))
1174acb54baSEdgar E. Iglesias         t = (t & 0x7fffffff) | (b & 0x80000000);
1184acb54baSEdgar E. Iglesias     return t;
1194acb54baSEdgar E. Iglesias }
1204acb54baSEdgar E. Iglesias 
1214acb54baSEdgar E. Iglesias uint32_t helper_cmpu(uint32_t a, uint32_t b)
1224acb54baSEdgar E. Iglesias {
1234acb54baSEdgar E. Iglesias     uint32_t t;
1244acb54baSEdgar E. Iglesias 
1254acb54baSEdgar E. Iglesias     t = b + ~a + 1;
1264acb54baSEdgar E. Iglesias     if ((b & 0x80000000) ^ (a & 0x80000000))
1274acb54baSEdgar E. Iglesias         t = (t & 0x7fffffff) | (a & 0x80000000);
1284acb54baSEdgar E. Iglesias     return t;
1294acb54baSEdgar E. Iglesias }
1304acb54baSEdgar E. Iglesias 
1314acb54baSEdgar E. Iglesias uint32_t helper_addkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c)
1324acb54baSEdgar E. Iglesias {
1334acb54baSEdgar E. Iglesias     uint32_t d, cf = 0, ncf;
1344acb54baSEdgar E. Iglesias 
1354acb54baSEdgar E. Iglesias     if (c)
1364acb54baSEdgar E. Iglesias         cf = env->sregs[SR_MSR] >> 31;
1374acb54baSEdgar E. Iglesias     assert(cf == 0 || cf == 1);
1384acb54baSEdgar E. Iglesias     d = a + b + cf;
1394acb54baSEdgar E. Iglesias 
1404acb54baSEdgar E. Iglesias     if (!k) {
1414acb54baSEdgar E. Iglesias         ncf = compute_carry(a, b, cf);
1424acb54baSEdgar E. Iglesias         assert(ncf == 0 || ncf == 1);
1434acb54baSEdgar E. Iglesias         if (ncf)
1444acb54baSEdgar E. Iglesias             env->sregs[SR_MSR] |= MSR_C | MSR_CC;
1454acb54baSEdgar E. Iglesias         else
1464acb54baSEdgar E. Iglesias             env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC);
1474acb54baSEdgar E. Iglesias     }
1484acb54baSEdgar E. Iglesias     D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n",
1494acb54baSEdgar E. Iglesias                d, a, b, cf, ncf, k, c));
1504acb54baSEdgar E. Iglesias     return d;
1514acb54baSEdgar E. Iglesias }
1524acb54baSEdgar E. Iglesias 
1534acb54baSEdgar E. Iglesias uint32_t helper_subkc(uint32_t a, uint32_t b, uint32_t k, uint32_t c)
1544acb54baSEdgar E. Iglesias {
1554acb54baSEdgar E. Iglesias     uint32_t d, cf = 1, ncf;
1564acb54baSEdgar E. Iglesias 
1574acb54baSEdgar E. Iglesias     if (c)
1584acb54baSEdgar E. Iglesias         cf = env->sregs[SR_MSR] >> 31;
1594acb54baSEdgar E. Iglesias     assert(cf == 0 || cf == 1);
1604acb54baSEdgar E. Iglesias     d = b + ~a + cf;
1614acb54baSEdgar E. Iglesias 
1624acb54baSEdgar E. Iglesias     if (!k) {
1634acb54baSEdgar E. Iglesias         ncf = compute_carry(b, ~a, cf);
1644acb54baSEdgar E. Iglesias         assert(ncf == 0 || ncf == 1);
1654acb54baSEdgar E. Iglesias         if (ncf)
1664acb54baSEdgar E. Iglesias             env->sregs[SR_MSR] |= MSR_C | MSR_CC;
1674acb54baSEdgar E. Iglesias         else
1684acb54baSEdgar E. Iglesias             env->sregs[SR_MSR] &= ~(MSR_C | MSR_CC);
1694acb54baSEdgar E. Iglesias     }
1704acb54baSEdgar E. Iglesias     D(qemu_log("%x = %x + %x cf=%d ncf=%d k=%d c=%d\n",
1714acb54baSEdgar E. Iglesias                d, a, b, cf, ncf, k, c));
1724acb54baSEdgar E. Iglesias     return d;
1734acb54baSEdgar E. Iglesias }
1744acb54baSEdgar E. Iglesias 
1754acb54baSEdgar E. Iglesias static inline int div_prepare(uint32_t a, uint32_t b)
1764acb54baSEdgar E. Iglesias {
1774acb54baSEdgar E. Iglesias     if (b == 0) {
1784acb54baSEdgar E. Iglesias         env->sregs[SR_MSR] |= MSR_DZ;
179821ebb33SEdgar E. Iglesias 
180821ebb33SEdgar E. Iglesias         if ((env->sregs[SR_MSR] & MSR_EE)
181821ebb33SEdgar E. Iglesias             && !(env->pvr.regs[2] & PVR2_DIV_ZERO_EXC_MASK)) {
182821ebb33SEdgar E. Iglesias             env->sregs[SR_ESR] = ESR_EC_DIVZERO;
183821ebb33SEdgar E. Iglesias             helper_raise_exception(EXCP_HW_EXCP);
184821ebb33SEdgar E. Iglesias         }
1854acb54baSEdgar E. Iglesias         return 0;
1864acb54baSEdgar E. Iglesias     }
1874acb54baSEdgar E. Iglesias     env->sregs[SR_MSR] &= ~MSR_DZ;
1884acb54baSEdgar E. Iglesias     return 1;
1894acb54baSEdgar E. Iglesias }
1904acb54baSEdgar E. Iglesias 
1914acb54baSEdgar E. Iglesias uint32_t helper_divs(uint32_t a, uint32_t b)
1924acb54baSEdgar E. Iglesias {
1934acb54baSEdgar E. Iglesias     if (!div_prepare(a, b))
1944acb54baSEdgar E. Iglesias         return 0;
1954acb54baSEdgar E. Iglesias     return (int32_t)a / (int32_t)b;
1964acb54baSEdgar E. Iglesias }
1974acb54baSEdgar E. Iglesias 
1984acb54baSEdgar E. Iglesias uint32_t helper_divu(uint32_t a, uint32_t b)
1994acb54baSEdgar E. Iglesias {
2004acb54baSEdgar E. Iglesias     if (!div_prepare(a, b))
2014acb54baSEdgar E. Iglesias         return 0;
2024acb54baSEdgar E. Iglesias     return a / b;
2034acb54baSEdgar E. Iglesias }
2044acb54baSEdgar E. Iglesias 
2054acb54baSEdgar E. Iglesias uint32_t helper_pcmpbf(uint32_t a, uint32_t b)
2064acb54baSEdgar E. Iglesias {
2074acb54baSEdgar E. Iglesias     unsigned int i;
2084acb54baSEdgar E. Iglesias     uint32_t mask = 0xff000000;
2094acb54baSEdgar E. Iglesias 
2104acb54baSEdgar E. Iglesias     for (i = 0; i < 4; i++) {
2114acb54baSEdgar E. Iglesias         if ((a & mask) == (b & mask))
2124acb54baSEdgar E. Iglesias             return i + 1;
2134acb54baSEdgar E. Iglesias         mask >>= 8;
2144acb54baSEdgar E. Iglesias     }
2154acb54baSEdgar E. Iglesias     return 0;
2164acb54baSEdgar E. Iglesias }
2174acb54baSEdgar E. Iglesias 
2183aa80988SEdgar E. Iglesias void helper_memalign(uint32_t addr, uint32_t dr, uint32_t wr, uint32_t mask)
219968a40f6SEdgar E. Iglesias {
220968a40f6SEdgar E. Iglesias     if (addr & mask) {
22197f90cbfSEdgar E. Iglesias             qemu_log_mask(CPU_LOG_INT,
22297f90cbfSEdgar E. Iglesias                           "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n",
22397f90cbfSEdgar E. Iglesias                           addr, mask, wr, dr);
22497f90cbfSEdgar E. Iglesias             env->sregs[SR_EAR] = addr;
225968a40f6SEdgar E. Iglesias             env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \
226968a40f6SEdgar E. Iglesias                                  | (dr & 31) << 5;
2273aa80988SEdgar E. Iglesias             if (mask == 3) {
228968a40f6SEdgar E. Iglesias                 env->sregs[SR_ESR] |= 1 << 11;
229968a40f6SEdgar E. Iglesias             }
23097f90cbfSEdgar E. Iglesias             if (!(env->sregs[SR_MSR] & MSR_EE)) {
23197f90cbfSEdgar E. Iglesias                 return;
23297f90cbfSEdgar E. Iglesias             }
233968a40f6SEdgar E. Iglesias             helper_raise_exception(EXCP_HW_EXCP);
234968a40f6SEdgar E. Iglesias     }
235968a40f6SEdgar E. Iglesias }
236968a40f6SEdgar E. Iglesias 
2374acb54baSEdgar E. Iglesias #if !defined(CONFIG_USER_ONLY)
2384acb54baSEdgar E. Iglesias /* Writes/reads to the MMU's special regs end up here.  */
2394acb54baSEdgar E. Iglesias uint32_t helper_mmu_read(uint32_t rn)
2404acb54baSEdgar E. Iglesias {
2414acb54baSEdgar E. Iglesias     return mmu_read(env, rn);
2424acb54baSEdgar E. Iglesias }
2434acb54baSEdgar E. Iglesias 
2444acb54baSEdgar E. Iglesias void helper_mmu_write(uint32_t rn, uint32_t v)
2454acb54baSEdgar E. Iglesias {
2464acb54baSEdgar E. Iglesias     mmu_write(env, rn, v);
2474acb54baSEdgar E. Iglesias }
2484acb54baSEdgar E. Iglesias #endif
249faed1c2aSEdgar E. Iglesias 
250c227f099SAnthony Liguori void do_unassigned_access(target_phys_addr_t addr, int is_write, int is_exec,
251faed1c2aSEdgar E. Iglesias                           int is_asi, int size)
252faed1c2aSEdgar E. Iglesias {
253faed1c2aSEdgar E. Iglesias     CPUState *saved_env;
254e1aa3254SEdgar E. Iglesias 
255e1aa3254SEdgar E. Iglesias     if (!cpu_single_env) {
256e1aa3254SEdgar E. Iglesias         /* XXX: ???   */
257e1aa3254SEdgar E. Iglesias         return;
258e1aa3254SEdgar E. Iglesias     }
259e1aa3254SEdgar E. Iglesias 
260faed1c2aSEdgar E. Iglesias     /* XXX: hack to restore env in all cases, even if not called from
261faed1c2aSEdgar E. Iglesias        generated code */
262faed1c2aSEdgar E. Iglesias     saved_env = env;
263faed1c2aSEdgar E. Iglesias     env = cpu_single_env;
26497f90cbfSEdgar E. Iglesias     qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n",
265faed1c2aSEdgar E. Iglesias              addr, is_write, is_exec);
266faed1c2aSEdgar E. Iglesias     if (!(env->sregs[SR_MSR] & MSR_EE)) {
26795b279deSEdgar E. Iglesias         env = saved_env;
268faed1c2aSEdgar E. Iglesias         return;
269faed1c2aSEdgar E. Iglesias     }
270faed1c2aSEdgar E. Iglesias 
27197f90cbfSEdgar E. Iglesias     env->sregs[SR_EAR] = addr;
272faed1c2aSEdgar E. Iglesias     if (is_exec) {
27397f90cbfSEdgar E. Iglesias         if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) {
274faed1c2aSEdgar E. Iglesias             env->sregs[SR_ESR] = ESR_EC_INSN_BUS;
275faed1c2aSEdgar E. Iglesias             helper_raise_exception(EXCP_HW_EXCP);
276faed1c2aSEdgar E. Iglesias         }
277faed1c2aSEdgar E. Iglesias     } else {
27897f90cbfSEdgar E. Iglesias         if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) {
279faed1c2aSEdgar E. Iglesias             env->sregs[SR_ESR] = ESR_EC_DATA_BUS;
280faed1c2aSEdgar E. Iglesias             helper_raise_exception(EXCP_HW_EXCP);
281faed1c2aSEdgar E. Iglesias         }
282faed1c2aSEdgar E. Iglesias     }
28395b279deSEdgar E. Iglesias     env = saved_env;
284faed1c2aSEdgar E. Iglesias }
285