spapr_hcall.c 22.7 KB
Newer Older
1
#include "sysemu/sysemu.h"
2
#include "cpu.h"
3
#include "helper_regs.h"
P
Paolo Bonzini 已提交
4
#include "hw/ppc/spapr.h"
5
#include "mmu-hash64.h"
6

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
struct SPRSyncState {
    CPUState *cs;
    int spr;
    target_ulong value;
    target_ulong mask;
};

static void do_spr_sync(void *arg)
{
    struct SPRSyncState *s = arg;
    PowerPCCPU *cpu = POWERPC_CPU(s->cs);
    CPUPPCState *env = &cpu->env;

    cpu_synchronize_state(s->cs);
    env->spr[s->spr] &= ~s->mask;
    env->spr[s->spr] |= s->value;
}

static void set_spr(CPUState *cs, int spr, target_ulong value,
                    target_ulong mask)
{
    struct SPRSyncState s = {
        .cs = cs,
        .spr = spr,
        .value = value,
        .mask = mask
    };
    run_on_cpu(cs, do_spr_sync, &s);
}

37 38 39 40 41 42 43
static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
                                     target_ulong pte_index)
{
    target_ulong rb, va_low;

    rb = (v & ~0x7fULL) << 16; /* AVA field */
    va_low = pte_index >> 3;
44
    if (v & HPTE64_V_SECONDARY) {
45 46 47
        va_low = ~va_low;
    }
    /* xor vsid from AVA */
48
    if (!(v & HPTE64_V_1TB_SEG)) {
49 50 51 52 53
        va_low ^= v >> 12;
    } else {
        va_low ^= v >> 24;
    }
    va_low &= 0x7ff;
54
    if (v & HPTE64_V_LARGE) {
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
        rb |= 1;                         /* L field */
#if 0 /* Disable that P7 specific bit for now */
        if (r & 0xff000) {
            /* non-16MB large page, must be 64k */
            /* (masks depend on page size) */
            rb |= 0x1000;                /* page encoding in LP field */
            rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */
            rb |= (va_low & 0xfe);       /* AVAL field */
        }
#endif
    } else {
        /* 4kB page */
        rb |= (va_low & 0x7ff) << 12;   /* remaining 11b of AVA */
    }
    rb |= (v >> 54) & 0x300;            /* B field */
    return rb;
}

73 74 75 76 77 78 79 80 81 82 83
static inline bool valid_pte_index(CPUPPCState *env, target_ulong pte_index)
{
    /*
     * hash value/pteg group index is normalized by htab_mask
     */
    if (((pte_index & ~7ULL) / HPTES_PER_GROUP) & ~env->htab_mask) {
        return false;
    }
    return true;
}

84
static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr,
85 86
                            target_ulong opcode, target_ulong *args)
{
87
    CPUPPCState *env = &cpu->env;
88 89 90 91
    target_ulong flags = args[0];
    target_ulong pte_index = args[1];
    target_ulong pteh = args[2];
    target_ulong ptel = args[3];
92 93
    target_ulong page_shift = 12;
    target_ulong raddr;
94 95
    target_ulong index;
    uint64_t token;
96 97

    /* only handle 4k and 16M pages for now */
98
    if (pteh & HPTE64_V_LARGE) {
99 100 101 102 103 104 105
#if 0 /* We don't support 64k pages yet */
        if ((ptel & 0xf000) == 0x1000) {
            /* 64k page */
        } else
#endif
        if ((ptel & 0xff000) == 0) {
            /* 16M page */
106
            page_shift = 24;
107 108 109 110 111 112 113 114 115
            /* lowest AVA bit must be 0 for 16M pages */
            if (pteh & 0x80) {
                return H_PARAMETER;
            }
        } else {
            return H_PARAMETER;
        }
    }

116
    raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1);
117

118 119
    if (raddr < spapr->ram_limit) {
        /* Regular RAM - should have WIMG=0010 */
120
        if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) {
121 122 123 124 125 126 127
            return H_PARAMETER;
        }
    } else {
        /* Looks like an IO address */
        /* FIXME: What WIMG combinations could be sensible for IO?
         * For now we allow WIMG=010x, but are there others? */
        /* FIXME: Should we check against registered IO addresses? */
128
        if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) {
129 130
            return H_PARAMETER;
        }
131
    }
132

133 134
    pteh &= ~0x60ULL;

135
    if (!valid_pte_index(env, pte_index)) {
136 137
        return H_PARAMETER;
    }
138 139

    index = 0;
140 141
    if (likely((flags & H_EXACT) == 0)) {
        pte_index &= ~7ULL;
142
        token = ppc_hash64_start_access(cpu, pte_index);
143
        for (; index < 8; index++) {
144
            if ((ppc_hash64_load_hpte0(env, token, index) & HPTE64_V_VALID) == 0) {
145 146
                break;
            }
147
        }
148
        ppc_hash64_stop_access(token);
149 150 151
        if (index == 8) {
            return H_PTEG_FULL;
        }
152
    } else {
153 154 155
        token = ppc_hash64_start_access(cpu, pte_index);
        if (ppc_hash64_load_hpte0(env, token, 0) & HPTE64_V_VALID) {
            ppc_hash64_stop_access(token);
156 157
            return H_PTEG_FULL;
        }
158
        ppc_hash64_stop_access(token);
159
    }
160

161 162
    ppc_hash64_store_hpte(env, pte_index + index,
                          pteh | HPTE64_V_HPTE_DIRTY, ptel);
163

164
    args[0] = pte_index + index;
165 166 167
    return H_SUCCESS;
}

168
typedef enum {
169 170 171 172
    REMOVE_SUCCESS = 0,
    REMOVE_NOT_FOUND = 1,
    REMOVE_PARM = 2,
    REMOVE_HW = 3,
173
} RemoveResult;
174

175
static RemoveResult remove_hpte(CPUPPCState *env, target_ulong ptex,
176 177 178
                                target_ulong avpn,
                                target_ulong flags,
                                target_ulong *vp, target_ulong *rp)
179
{
180
    uint64_t token;
181 182
    target_ulong v, r, rb;

183
    if (!valid_pte_index(env, ptex)) {
184
        return REMOVE_PARM;
185 186
    }

187 188 189 190
    token = ppc_hash64_start_access(ppc_env_get_cpu(env), ptex);
    v = ppc_hash64_load_hpte0(env, token, 0);
    r = ppc_hash64_load_hpte1(env, token, 0);
    ppc_hash64_stop_access(token);
191

192
    if ((v & HPTE64_V_VALID) == 0 ||
193 194
        ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
        ((flags & H_ANDCOND) && (v & avpn) != 0)) {
195
        return REMOVE_NOT_FOUND;
196
    }
197
    *vp = v;
198
    *rp = r;
199
    ppc_hash64_store_hpte(env, ptex, HPTE64_V_HPTE_DIRTY, 0);
200
    rb = compute_tlbie_rb(v, r, ptex);
201
    ppc_tlb_invalidate_one(env, rb);
202 203 204
    return REMOVE_SUCCESS;
}

205
static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
206 207
                             target_ulong opcode, target_ulong *args)
{
208
    CPUPPCState *env = &cpu->env;
209 210 211
    target_ulong flags = args[0];
    target_ulong pte_index = args[1];
    target_ulong avpn = args[2];
212
    RemoveResult ret;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230

    ret = remove_hpte(env, pte_index, avpn, flags,
                      &args[0], &args[1]);

    switch (ret) {
    case REMOVE_SUCCESS:
        return H_SUCCESS;

    case REMOVE_NOT_FOUND:
        return H_NOT_FOUND;

    case REMOVE_PARM:
        return H_PARAMETER;

    case REMOVE_HW:
        return H_HARDWARE;
    }

231
    g_assert_not_reached();
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
}

#define H_BULK_REMOVE_TYPE             0xc000000000000000ULL
#define   H_BULK_REMOVE_REQUEST        0x4000000000000000ULL
#define   H_BULK_REMOVE_RESPONSE       0x8000000000000000ULL
#define   H_BULK_REMOVE_END            0xc000000000000000ULL
#define H_BULK_REMOVE_CODE             0x3000000000000000ULL
#define   H_BULK_REMOVE_SUCCESS        0x0000000000000000ULL
#define   H_BULK_REMOVE_NOT_FOUND      0x1000000000000000ULL
#define   H_BULK_REMOVE_PARM           0x2000000000000000ULL
#define   H_BULK_REMOVE_HW             0x3000000000000000ULL
#define H_BULK_REMOVE_RC               0x0c00000000000000ULL
#define H_BULK_REMOVE_FLAGS            0x0300000000000000ULL
#define   H_BULK_REMOVE_ABSOLUTE       0x0000000000000000ULL
#define   H_BULK_REMOVE_ANDCOND        0x0100000000000000ULL
#define   H_BULK_REMOVE_AVPN           0x0200000000000000ULL
#define H_BULK_REMOVE_PTEX             0x00ffffffffffffffULL

#define H_BULK_REMOVE_MAX_BATCH        4

252
static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr,
253 254
                                  target_ulong opcode, target_ulong *args)
{
255
    CPUPPCState *env = &cpu->env;
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
    int i;

    for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) {
        target_ulong *tsh = &args[i*2];
        target_ulong tsl = args[i*2 + 1];
        target_ulong v, r, ret;

        if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) {
            break;
        } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) {
            return H_PARAMETER;
        }

        *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS;
        *tsh |= H_BULK_REMOVE_RESPONSE;

        if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) {
            *tsh |= H_BULK_REMOVE_PARM;
            return H_PARAMETER;
        }

        ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl,
                          (*tsh & H_BULK_REMOVE_FLAGS) >> 26,
                          &v, &r);

        *tsh |= ret << 60;

        switch (ret) {
        case REMOVE_SUCCESS:
285
            *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43;
286 287 288 289 290 291 292 293 294 295
            break;

        case REMOVE_PARM:
            return H_PARAMETER;

        case REMOVE_HW:
            return H_HARDWARE;
        }
    }

296 297 298
    return H_SUCCESS;
}

299
static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr,
300 301
                              target_ulong opcode, target_ulong *args)
{
302
    CPUPPCState *env = &cpu->env;
303 304 305
    target_ulong flags = args[0];
    target_ulong pte_index = args[1];
    target_ulong avpn = args[2];
306
    uint64_t token;
307 308
    target_ulong v, r, rb;

309
    if (!valid_pte_index(env, pte_index)) {
310 311 312
        return H_PARAMETER;
    }

313 314 315 316
    token = ppc_hash64_start_access(cpu, pte_index);
    v = ppc_hash64_load_hpte0(env, token, 0);
    r = ppc_hash64_load_hpte1(env, token, 0);
    ppc_hash64_stop_access(token);
317

318
    if ((v & HPTE64_V_VALID) == 0 ||
319 320 321 322
        ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
        return H_NOT_FOUND;
    }

323 324 325 326 327
    r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N |
           HPTE64_R_KEY_HI | HPTE64_R_KEY_LO);
    r |= (flags << 55) & HPTE64_R_PP0;
    r |= (flags << 48) & HPTE64_R_KEY_HI;
    r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO);
328
    rb = compute_tlbie_rb(v, r, pte_index);
329 330
    ppc_hash64_store_hpte(env, pte_index,
                          (v & ~HPTE64_V_VALID) | HPTE64_V_HPTE_DIRTY, 0);
331 332
    ppc_tlb_invalidate_one(env, rb);
    /* Don't need a memory barrier, due to qemu's global lock */
333
    ppc_hash64_store_hpte(env, pte_index, v | HPTE64_V_HPTE_DIRTY, r);
334 335 336
    return H_SUCCESS;
}

E
Erlon Cruz 已提交
337 338 339 340 341 342 343 344 345
static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr,
                           target_ulong opcode, target_ulong *args)
{
    CPUPPCState *env = &cpu->env;
    target_ulong flags = args[0];
    target_ulong pte_index = args[1];
    uint8_t *hpte;
    int i, ridx, n_entries = 1;

346
    if (!valid_pte_index(env, pte_index)) {
E
Erlon Cruz 已提交
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        return H_PARAMETER;
    }

    if (flags & H_READ_4) {
        /* Clear the two low order bits */
        pte_index &= ~(3ULL);
        n_entries = 4;
    }

    hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);

    for (i = 0, ridx = 0; i < n_entries; i++) {
        args[ridx++] = ldq_p(hpte);
        args[ridx++] = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
        hpte += HASH_PTE_SIZE_64;
    }

    return H_SUCCESS;
}

367
static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPREnvironment *spapr,
368 369 370 371 372 373
                               target_ulong opcode, target_ulong *args)
{
    /* FIXME: actually implement this */
    return H_HARDWARE;
}

374 375 376 377 378 379 380 381 382 383 384 385
#define FLAGS_REGISTER_VPA         0x0000200000000000ULL
#define FLAGS_REGISTER_DTL         0x0000400000000000ULL
#define FLAGS_REGISTER_SLBSHADOW   0x0000600000000000ULL
#define FLAGS_DEREGISTER_VPA       0x0000a00000000000ULL
#define FLAGS_DEREGISTER_DTL       0x0000c00000000000ULL
#define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL

#define VPA_MIN_SIZE           640
#define VPA_SIZE_OFFSET        0x4
#define VPA_SHARED_PROC_OFFSET 0x9
#define VPA_SHARED_PROC_VAL    0x2

A
Andreas Färber 已提交
386
static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa)
387
{
388
    CPUState *cs = CPU(ppc_env_get_cpu(env));
389 390 391 392 393 394 395 396 397 398 399 400 401
    uint16_t size;
    uint8_t tmp;

    if (vpa == 0) {
        hcall_dprintf("Can't cope with registering a VPA at logical 0\n");
        return H_HARDWARE;
    }

    if (vpa % env->dcache_line_size) {
        return H_PARAMETER;
    }
    /* FIXME: bounds check the address */

402
    size = lduw_be_phys(cs->as, vpa + 0x4);
403 404 405 406 407 408 409 410 411 412

    if (size < VPA_MIN_SIZE) {
        return H_PARAMETER;
    }

    /* VPA is not allowed to cross a page boundary */
    if ((vpa / 4096) != ((vpa + size - 1) / 4096)) {
        return H_PARAMETER;
    }

413
    env->vpa_addr = vpa;
414

415
    tmp = ldub_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET);
416
    tmp |= VPA_SHARED_PROC_VAL;
417
    stb_phys(cs->as, env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp);
418 419 420 421

    return H_SUCCESS;
}

A
Andreas Färber 已提交
422
static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa)
423
{
424
    if (env->slb_shadow_addr) {
425 426 427
        return H_RESOURCE;
    }

428
    if (env->dtl_addr) {
429 430 431
        return H_RESOURCE;
    }

432
    env->vpa_addr = 0;
433 434 435
    return H_SUCCESS;
}

A
Andreas Färber 已提交
436
static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr)
437
{
438
    CPUState *cs = CPU(ppc_env_get_cpu(env));
439 440 441 442 443 444 445
    uint32_t size;

    if (addr == 0) {
        hcall_dprintf("Can't cope with SLB shadow at logical 0\n");
        return H_HARDWARE;
    }

446
    size = ldl_be_phys(cs->as, addr + 0x4);
447 448 449 450 451 452 453 454
    if (size < 0x8) {
        return H_PARAMETER;
    }

    if ((addr / 4096) != ((addr + size - 1) / 4096)) {
        return H_PARAMETER;
    }

455
    if (!env->vpa_addr) {
456 457 458
        return H_RESOURCE;
    }

459 460
    env->slb_shadow_addr = addr;
    env->slb_shadow_size = size;
461 462 463 464

    return H_SUCCESS;
}

A
Andreas Färber 已提交
465
static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr)
466
{
467 468
    env->slb_shadow_addr = 0;
    env->slb_shadow_size = 0;
469 470 471
    return H_SUCCESS;
}

A
Andreas Färber 已提交
472
static target_ulong register_dtl(CPUPPCState *env, target_ulong addr)
473
{
474
    CPUState *cs = CPU(ppc_env_get_cpu(env));
475 476 477 478 479 480 481
    uint32_t size;

    if (addr == 0) {
        hcall_dprintf("Can't cope with DTL at logical 0\n");
        return H_HARDWARE;
    }

482
    size = ldl_be_phys(cs->as, addr + 0x4);
483 484 485 486 487

    if (size < 48) {
        return H_PARAMETER;
    }

488
    if (!env->vpa_addr) {
489 490 491
        return H_RESOURCE;
    }

492
    env->dtl_addr = addr;
493 494 495 496 497
    env->dtl_size = size;

    return H_SUCCESS;
}

498
static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr)
499
{
500
    env->dtl_addr = 0;
501 502 503 504 505
    env->dtl_size = 0;

    return H_SUCCESS;
}

506
static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr,
507 508 509 510 511 512
                                   target_ulong opcode, target_ulong *args)
{
    target_ulong flags = args[0];
    target_ulong procno = args[1];
    target_ulong vpa = args[2];
    target_ulong ret = H_PARAMETER;
A
Andreas Färber 已提交
513
    CPUPPCState *tenv;
514
    PowerPCCPU *tcpu;
515

516
    tcpu = ppc_get_vcpu_by_dt_id(procno);
517
    if (!tcpu) {
518 519
        return H_PARAMETER;
    }
520
    tenv = &tcpu->env;
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550

    switch (flags) {
    case FLAGS_REGISTER_VPA:
        ret = register_vpa(tenv, vpa);
        break;

    case FLAGS_DEREGISTER_VPA:
        ret = deregister_vpa(tenv, vpa);
        break;

    case FLAGS_REGISTER_SLBSHADOW:
        ret = register_slb_shadow(tenv, vpa);
        break;

    case FLAGS_DEREGISTER_SLBSHADOW:
        ret = deregister_slb_shadow(tenv, vpa);
        break;

    case FLAGS_REGISTER_DTL:
        ret = register_dtl(tenv, vpa);
        break;

    case FLAGS_DEREGISTER_DTL:
        ret = deregister_dtl(tenv, vpa);
        break;
    }

    return ret;
}

551
static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr,
552 553
                           target_ulong opcode, target_ulong *args)
{
554
    CPUPPCState *env = &cpu->env;
555
    CPUState *cs = CPU(cpu);
556

557 558
    env->msr |= (1ULL << MSR_EE);
    hreg_compute_hflags(env);
559
    if (!cpu_has_work(cs)) {
560
        cs->halted = 1;
561
        cs->exception_index = EXCP_HLT;
562
        cs->exit_request = 1;
563 564 565 566
    }
    return H_SUCCESS;
}

567
static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr,
568 569 570
                           target_ulong opcode, target_ulong *args)
{
    target_ulong rtas_r3 = args[0];
571 572 573
    uint32_t token = rtas_ld(rtas_r3, 0);
    uint32_t nargs = rtas_ld(rtas_r3, 1);
    uint32_t nret = rtas_ld(rtas_r3, 2);
574

575
    return spapr_rtas_call(cpu, spapr, token, nargs, rtas_r3 + 12,
576 577 578
                           nret, rtas_r3 + 12 + 4*nargs);
}

579
static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr,
580 581
                                   target_ulong opcode, target_ulong *args)
{
582
    CPUState *cs = CPU(cpu);
583 584 585 586 587
    target_ulong size = args[0];
    target_ulong addr = args[1];

    switch (size) {
    case 1:
588
        args[0] = ldub_phys(cs->as, addr);
589 590
        return H_SUCCESS;
    case 2:
591
        args[0] = lduw_phys(cs->as, addr);
592 593
        return H_SUCCESS;
    case 4:
594
        args[0] = ldl_phys(cs->as, addr);
595 596
        return H_SUCCESS;
    case 8:
597
        args[0] = ldq_phys(cs->as, addr);
598 599 600 601 602
        return H_SUCCESS;
    }
    return H_PARAMETER;
}

603
static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr,
604 605
                                    target_ulong opcode, target_ulong *args)
{
606 607
    CPUState *cs = CPU(cpu);

608 609 610 611 612 613
    target_ulong size = args[0];
    target_ulong addr = args[1];
    target_ulong val  = args[2];

    switch (size) {
    case 1:
614
        stb_phys(cs->as, addr, val);
615 616
        return H_SUCCESS;
    case 2:
617
        stw_phys(cs->as, addr, val);
618 619
        return H_SUCCESS;
    case 4:
620
        stl_phys(cs->as, addr, val);
621 622
        return H_SUCCESS;
    case 8:
623
        stq_phys(cs->as, addr, val);
624 625 626 627 628
        return H_SUCCESS;
    }
    return H_PARAMETER;
}

629
static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr,
630 631
                                    target_ulong opcode, target_ulong *args)
{
632 633
    CPUState *cs = CPU(cpu);

634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
    target_ulong dst   = args[0]; /* Destination address */
    target_ulong src   = args[1]; /* Source address */
    target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */
    target_ulong count = args[3]; /* Element count */
    target_ulong op    = args[4]; /* 0 = copy, 1 = invert */
    uint64_t tmp;
    unsigned int mask = (1 << esize) - 1;
    int step = 1 << esize;

    if (count > 0x80000000) {
        return H_PARAMETER;
    }

    if ((dst & mask) || (src & mask) || (op > 1)) {
        return H_PARAMETER;
    }

    if (dst >= src && dst < (src + (count << esize))) {
            dst = dst + ((count - 1) << esize);
            src = src + ((count - 1) << esize);
            step = -step;
    }

    while (count--) {
        switch (esize) {
        case 0:
660
            tmp = ldub_phys(cs->as, src);
661 662
            break;
        case 1:
663
            tmp = lduw_phys(cs->as, src);
664 665
            break;
        case 2:
666
            tmp = ldl_phys(cs->as, src);
667 668
            break;
        case 3:
669
            tmp = ldq_phys(cs->as, src);
670 671 672 673 674 675 676 677 678
            break;
        default:
            return H_PARAMETER;
        }
        if (op == 1) {
            tmp = ~tmp;
        }
        switch (esize) {
        case 0:
679
            stb_phys(cs->as, dst, tmp);
680 681
            break;
        case 1:
682
            stw_phys(cs->as, dst, tmp);
683 684
            break;
        case 2:
685
            stl_phys(cs->as, dst, tmp);
686 687
            break;
        case 3:
688
            stq_phys(cs->as, dst, tmp);
689 690 691 692 693 694 695 696 697
            break;
        }
        dst = dst + step;
        src = src + step;
    }

    return H_SUCCESS;
}

698
static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPREnvironment *spapr,
699 700 701 702 703 704
                                   target_ulong opcode, target_ulong *args)
{
    /* Nothing to do on emulation, KVM will trap this in the kernel */
    return H_SUCCESS;
}

705
static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPREnvironment *spapr,
706 707 708 709 710 711
                                   target_ulong opcode, target_ulong *args)
{
    /* Nothing to do on emulation, KVM will trap this in the kernel */
    return H_SUCCESS;
}

712 713 714 715 716 717 718 719 720 721
static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPREnvironment *spapr,
                               target_ulong opcode, target_ulong *args)
{
    CPUState *cs;
    target_ulong mflags = args[0];
    target_ulong resource = args[1];
    target_ulong value1 = args[2];
    target_ulong value2 = args[3];
    target_ulong ret = H_P2;

722
    if (resource == H_SET_MODE_RESOURCE_LE) {
723 724 725 726 727 728 729 730 731 732
        if (value1) {
            ret = H_P3;
            goto out;
        }
        if (value2) {
            ret = H_P4;
            goto out;
        }
        switch (mflags) {
        case H_SET_MODE_ENDIAN_BIG:
A
Andreas Färber 已提交
733
            CPU_FOREACH(cs) {
734
                set_spr(cs, SPR_LPCR, 0, LPCR_ILE);
735 736 737 738 739
            }
            ret = H_SUCCESS;
            break;

        case H_SET_MODE_ENDIAN_LITTLE:
A
Andreas Färber 已提交
740
            CPU_FOREACH(cs) {
741
                set_spr(cs, SPR_LPCR, LPCR_ILE, LPCR_ILE);
742 743 744 745 746 747 748 749 750 751 752 753 754
            }
            ret = H_SUCCESS;
            break;

        default:
            ret = H_UNSUPPORTED_FLAG;
        }
    }

out:
    return ret;
}

755 756
static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
757 758 759

void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
{
760 761 762 763
    spapr_hcall_fn *slot;

    if (opcode <= MAX_HCALL_OPCODE) {
        assert((opcode & 0x3) == 0);
764

765 766 767
        slot = &papr_hypercall_table[opcode / 4];
    } else {
        assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
768

769 770
        slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
    }
771

772
    assert(!(*slot));
773
    *slot = fn;
774 775
}

776
target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode,
777 778 779 780
                             target_ulong *args)
{
    if ((opcode <= MAX_HCALL_OPCODE)
        && ((opcode & 0x3) == 0)) {
781 782 783
        spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];

        if (fn) {
784
            return fn(cpu, spapr, opcode, args);
785 786 787 788
        }
    } else if ((opcode >= KVMPPC_HCALL_BASE) &&
               (opcode <= KVMPPC_HCALL_MAX)) {
        spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
789 790

        if (fn) {
791
            return fn(cpu, spapr, opcode, args);
792 793 794 795 796 797
        }
    }

    hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
    return H_FUNCTION;
}
798

A
Andreas Färber 已提交
799
static void hypercall_register_types(void)
800 801 802 803 804
{
    /* hcall-pft */
    spapr_register_hypercall(H_ENTER, h_enter);
    spapr_register_hypercall(H_REMOVE, h_remove);
    spapr_register_hypercall(H_PROTECT, h_protect);
E
Erlon Cruz 已提交
805
    spapr_register_hypercall(H_READ, h_read);
806

807 808 809
    /* hcall-bulk */
    spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove);

810 811 812
    /* hcall-dabr */
    spapr_register_hypercall(H_SET_DABR, h_set_dabr);

813 814 815 816
    /* hcall-splpar */
    spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
    spapr_register_hypercall(H_CEDE, h_cede);

817 818 819 820 821 822 823 824 825 826 827
    /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate
     * here between the "CI" and the "CACHE" variants, they will use whatever
     * mapping attributes qemu is using. When using KVM, the kernel will
     * enforce the attributes more strongly
     */
    spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load);
    spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store);
    spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load);
    spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store);
    spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi);
    spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf);
828
    spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop);
829

830 831
    /* qemu/KVM-PPC specific hcalls */
    spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
832 833

    spapr_register_hypercall(H_SET_MODE, h_set_mode);
834
}
A
Andreas Färber 已提交
835 836

type_init(hypercall_register_types)