Statistics
| Branch: | Revision:

root / target-ppc / op_helper.c @ 6de673d4

History | View | Annotate | Download (11 kB)

1
/*
2
 *  PowerPC emulation helpers for QEMU.
3
 *
4
 *  Copyright (c) 2003-2007 Jocelyn Mayer
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <string.h>
20
#include "cpu.h"
21
#include "dyngen-exec.h"
22
#include "host-utils.h"
23
#include "helper.h"
24

    
25
#include "helper_regs.h"
26

    
27
#if !defined(CONFIG_USER_ONLY)
28
#include "softmmu_exec.h"
29
#endif /* !defined(CONFIG_USER_ONLY) */
30

    
31
//#define DEBUG_OP
32

    
33
/*****************************************************************************/
34
/* SPR accesses */
35
void helper_load_dump_spr(uint32_t sprn)
36
{
37
    qemu_log("Read SPR %d %03x => " TARGET_FMT_lx "\n", sprn, sprn,
38
             env->spr[sprn]);
39
}
40

    
41
void helper_store_dump_spr(uint32_t sprn)
42
{
43
    qemu_log("Write SPR %d %03x <= " TARGET_FMT_lx "\n", sprn, sprn,
44
             env->spr[sprn]);
45
}
46
#if !defined(CONFIG_USER_ONLY)
47
#if defined(TARGET_PPC64)
48
void helper_store_asr(target_ulong val)
49
{
50
    ppc_store_asr(env, val);
51
}
52
#endif
53

    
54
void helper_store_sdr1(target_ulong val)
55
{
56
    ppc_store_sdr1(env, val);
57
}
58

    
59
void helper_store_hid0_601(target_ulong val)
60
{
61
    target_ulong hid0;
62

    
63
    hid0 = env->spr[SPR_HID0];
64
    if ((val ^ hid0) & 0x00000008) {
65
        /* Change current endianness */
66
        env->hflags &= ~(1 << MSR_LE);
67
        env->hflags_nmsr &= ~(1 << MSR_LE);
68
        env->hflags_nmsr |= (1 << MSR_LE) & (((val >> 3) & 1) << MSR_LE);
69
        env->hflags |= env->hflags_nmsr;
70
        qemu_log("%s: set endianness to %c => " TARGET_FMT_lx "\n", __func__,
71
                 val & 0x8 ? 'l' : 'b', env->hflags);
72
    }
73
    env->spr[SPR_HID0] = (uint32_t)val;
74
}
75

    
76
void helper_store_403_pbr(uint32_t num, target_ulong value)
77
{
78
    if (likely(env->pb[num] != value)) {
79
        env->pb[num] = value;
80
        /* Should be optimized */
81
        tlb_flush(env, 1);
82
    }
83
}
84

    
85
void helper_store_40x_dbcr0(target_ulong val)
86
{
87
    store_40x_dbcr0(env, val);
88
}
89

    
90
void helper_store_40x_sler(target_ulong val)
91
{
92
    store_40x_sler(env, val);
93
}
94
#endif
95

    
96
/*****************************************************************************/
97
/* Memory load and stores */
98

    
99
static inline target_ulong addr_add(target_ulong addr, target_long arg)
100
{
101
#if defined(TARGET_PPC64)
102
    if (!msr_sf) {
103
        return (uint32_t)(addr + arg);
104
    } else
105
#endif
106
    {
107
        return addr + arg;
108
    }
109
}
110

    
111
void helper_lmw(target_ulong addr, uint32_t reg)
112
{
113
    for (; reg < 32; reg++) {
114
        if (msr_le) {
115
            env->gpr[reg] = bswap32(ldl(addr));
116
        } else {
117
            env->gpr[reg] = ldl(addr);
118
        }
119
        addr = addr_add(addr, 4);
120
    }
121
}
122

    
123
void helper_stmw(target_ulong addr, uint32_t reg)
124
{
125
    for (; reg < 32; reg++) {
126
        if (msr_le) {
127
            stl(addr, bswap32((uint32_t)env->gpr[reg]));
128
        } else {
129
            stl(addr, (uint32_t)env->gpr[reg]);
130
        }
131
        addr = addr_add(addr, 4);
132
    }
133
}
134

    
135
void helper_lsw(target_ulong addr, uint32_t nb, uint32_t reg)
136
{
137
    int sh;
138

    
139
    for (; nb > 3; nb -= 4) {
140
        env->gpr[reg] = ldl(addr);
141
        reg = (reg + 1) % 32;
142
        addr = addr_add(addr, 4);
143
    }
144
    if (unlikely(nb > 0)) {
145
        env->gpr[reg] = 0;
146
        for (sh = 24; nb > 0; nb--, sh -= 8) {
147
            env->gpr[reg] |= ldub(addr) << sh;
148
            addr = addr_add(addr, 1);
149
        }
150
    }
151
}
152
/* PPC32 specification says we must generate an exception if
153
 * rA is in the range of registers to be loaded.
154
 * In an other hand, IBM says this is valid, but rA won't be loaded.
155
 * For now, I'll follow the spec...
156
 */
157
void helper_lswx(target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb)
158
{
159
    if (likely(xer_bc != 0)) {
160
        if (unlikely((ra != 0 && reg < ra && (reg + xer_bc) > ra) ||
161
                     (reg < rb && (reg + xer_bc) > rb))) {
162
            helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM,
163
                                       POWERPC_EXCP_INVAL |
164
                                       POWERPC_EXCP_INVAL_LSWX);
165
        } else {
166
            helper_lsw(addr, xer_bc, reg);
167
        }
168
    }
169
}
170

    
171
void helper_stsw(target_ulong addr, uint32_t nb, uint32_t reg)
172
{
173
    int sh;
174

    
175
    for (; nb > 3; nb -= 4) {
176
        stl(addr, env->gpr[reg]);
177
        reg = (reg + 1) % 32;
178
        addr = addr_add(addr, 4);
179
    }
180
    if (unlikely(nb > 0)) {
181
        for (sh = 24; nb > 0; nb--, sh -= 8) {
182
            stb(addr, (env->gpr[reg] >> sh) & 0xFF);
183
            addr = addr_add(addr, 1);
184
        }
185
    }
186
}
187

    
188
static void do_dcbz(target_ulong addr, int dcache_line_size)
189
{
190
    int i;
191

    
192
    addr &= ~(dcache_line_size - 1);
193
    for (i = 0; i < dcache_line_size; i += 4) {
194
        stl(addr + i, 0);
195
    }
196
    if (env->reserve_addr == addr) {
197
        env->reserve_addr = (target_ulong)-1ULL;
198
    }
199
}
200

    
201
void helper_dcbz(target_ulong addr)
202
{
203
    do_dcbz(addr, env->dcache_line_size);
204
}
205

    
206
void helper_dcbz_970(target_ulong addr)
207
{
208
    if (((env->spr[SPR_970_HID5] >> 7) & 0x3) == 1) {
209
        do_dcbz(addr, 32);
210
    } else {
211
        do_dcbz(addr, env->dcache_line_size);
212
    }
213
}
214

    
215
void helper_icbi(target_ulong addr)
216
{
217
    addr &= ~(env->dcache_line_size - 1);
218
    /* Invalidate one cache line :
219
     * PowerPC specification says this is to be treated like a load
220
     * (not a fetch) by the MMU. To be sure it will be so,
221
     * do the load "by hand".
222
     */
223
    ldl(addr);
224
}
225

    
226
/* XXX: to be tested */
227
target_ulong helper_lscbx(target_ulong addr, uint32_t reg, uint32_t ra,
228
                          uint32_t rb)
229
{
230
    int i, c, d;
231

    
232
    d = 24;
233
    for (i = 0; i < xer_bc; i++) {
234
        c = ldub(addr);
235
        addr = addr_add(addr, 1);
236
        /* ra (if not 0) and rb are never modified */
237
        if (likely(reg != rb && (ra == 0 || reg != ra))) {
238
            env->gpr[reg] = (env->gpr[reg] & ~(0xFF << d)) | (c << d);
239
        }
240
        if (unlikely(c == xer_cmp)) {
241
            break;
242
        }
243
        if (likely(d != 0)) {
244
            d -= 8;
245
        } else {
246
            d = 24;
247
            reg++;
248
            reg = reg & 0x1F;
249
        }
250
    }
251
    return i;
252
}
253

    
254
/*****************************************************************************/
255
/* PowerPC 601 specific instructions (POWER bridge) */
256

    
257
target_ulong helper_clcs(uint32_t arg)
258
{
259
    switch (arg) {
260
    case 0x0CUL:
261
        /* Instruction cache line size */
262
        return env->icache_line_size;
263
        break;
264
    case 0x0DUL:
265
        /* Data cache line size */
266
        return env->dcache_line_size;
267
        break;
268
    case 0x0EUL:
269
        /* Minimum cache line size */
270
        return (env->icache_line_size < env->dcache_line_size) ?
271
            env->icache_line_size : env->dcache_line_size;
272
        break;
273
    case 0x0FUL:
274
        /* Maximum cache line size */
275
        return (env->icache_line_size > env->dcache_line_size) ?
276
            env->icache_line_size : env->dcache_line_size;
277
        break;
278
    default:
279
        /* Undefined */
280
        return 0;
281
        break;
282
    }
283
}
284

    
285
/*****************************************************************************/
286
/* Altivec extension helpers */
287
#if defined(HOST_WORDS_BIGENDIAN)
288
#define HI_IDX 0
289
#define LO_IDX 1
290
#else
291
#define HI_IDX 1
292
#define LO_IDX 0
293
#endif
294

    
295
#define LVE(name, access, swap, element)                        \
296
    void helper_##name(ppc_avr_t *r, target_ulong addr)         \
297
    {                                                           \
298
        size_t n_elems = ARRAY_SIZE(r->element);                \
299
        int adjust = HI_IDX*(n_elems - 1);                      \
300
        int sh = sizeof(r->element[0]) >> 1;                    \
301
        int index = (addr & 0xf) >> sh;                         \
302
                                                                \
303
        if (msr_le) {                                           \
304
            r->element[LO_IDX ? index : (adjust - index)] =     \
305
                swap(access(addr));                             \
306
        } else {                                                \
307
            r->element[LO_IDX ? index : (adjust - index)] =     \
308
                access(addr);                                   \
309
        }                                                       \
310
    }
311
#define I(x) (x)
312
LVE(lvebx, ldub, I, u8)
313
LVE(lvehx, lduw, bswap16, u16)
314
LVE(lvewx, ldl, bswap32, u32)
315
#undef I
316
#undef LVE
317

    
318
#define STVE(name, access, swap, element)                               \
319
    void helper_##name(ppc_avr_t *r, target_ulong addr)                 \
320
    {                                                                   \
321
        size_t n_elems = ARRAY_SIZE(r->element);                        \
322
        int adjust = HI_IDX * (n_elems - 1);                            \
323
        int sh = sizeof(r->element[0]) >> 1;                            \
324
        int index = (addr & 0xf) >> sh;                                 \
325
                                                                        \
326
        if (msr_le) {                                                   \
327
            access(addr, swap(r->element[LO_IDX ? index : (adjust - index)])); \
328
        } else {                                                        \
329
            access(addr, r->element[LO_IDX ? index : (adjust - index)]); \
330
        }                                                               \
331
    }
332
#define I(x) (x)
333
STVE(stvebx, stb, I, u8)
334
STVE(stvehx, stw, bswap16, u16)
335
STVE(stvewx, stl, bswap32, u32)
336
#undef I
337
#undef LVE
338

    
339
#undef HI_IDX
340
#undef LO_IDX
341

    
342
/*****************************************************************************/
343
/* Softmmu support */
344
#if !defined(CONFIG_USER_ONLY)
345

    
346
#define MMUSUFFIX _mmu
347

    
348
#define SHIFT 0
349
#include "softmmu_template.h"
350

    
351
#define SHIFT 1
352
#include "softmmu_template.h"
353

    
354
#define SHIFT 2
355
#include "softmmu_template.h"
356

    
357
#define SHIFT 3
358
#include "softmmu_template.h"
359

    
360
/* try to fill the TLB and return an exception if error. If retaddr is
361
   NULL, it means that the function was called in C code (i.e. not
362
   from generated code or from helper.c) */
363
/* XXX: fix it to restore all registers */
364
void tlb_fill(CPUPPCState *env1, target_ulong addr, int is_write, int mmu_idx,
365
              uintptr_t retaddr)
366
{
367
    TranslationBlock *tb;
368
    CPUPPCState *saved_env;
369
    int ret;
370

    
371
    saved_env = env;
372
    env = env1;
373
    ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, mmu_idx);
374
    if (unlikely(ret != 0)) {
375
        if (likely(retaddr)) {
376
            /* now we have a real cpu fault */
377
            tb = tb_find_pc(retaddr);
378
            if (likely(tb)) {
379
                /* the PC is inside the translated code. It means that we have
380
                   a virtual CPU fault */
381
                cpu_restore_state(tb, env, retaddr);
382
            }
383
        }
384
        helper_raise_exception_err(env, env->exception_index, env->error_code);
385
    }
386
    env = saved_env;
387
}
388
#endif /* !CONFIG_USER_ONLY */