helper.c 26.8 KB
Newer Older
B
bellard 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  PPC emulation helpers for qemu.
 * 
 *  Copyright (c) 2003 Jocelyn Mayer
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include "exec.h"
21 22 23 24 25 26 27

//#define DEBUG_MMU
//#define DEBUG_BATS
//#define DEBUG_EXCEPTIONS

/*****************************************************************************/
/* PPC MMU emulation */
28

29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
/* Perform BAT hit & translation */
static int get_bat (CPUState *env, uint32_t *real, int *prot,
                    uint32_t virtual, int rw, int type)
{
    uint32_t *BATlt, *BATut, *BATu, *BATl;
    uint32_t base, BEPIl, BEPIu, bl;
    int i;
    int ret = -1;

#if defined (DEBUG_BATS)
    if (loglevel > 0) {
        fprintf(logfile, "%s: %cBAT v 0x%08x\n", __func__,
               type == ACCESS_CODE ? 'I' : 'D', virtual);
    }
#endif
    switch (type) {
    case ACCESS_CODE:
        BATlt = env->IBAT[1];
        BATut = env->IBAT[0];
        break;
    default:
        BATlt = env->DBAT[1];
        BATut = env->DBAT[0];
        break;
    }
#if defined (DEBUG_BATS)
    if (loglevel > 0) {
        fprintf(logfile, "%s...: %cBAT v 0x%08x\n", __func__,
               type == ACCESS_CODE ? 'I' : 'D', virtual);
    }
#endif
    base = virtual & 0xFFFC0000;
    for (i = 0; i < 4; i++) {
        BATu = &BATut[i];
        BATl = &BATlt[i];
        BEPIu = *BATu & 0xF0000000;
        BEPIl = *BATu & 0x0FFE0000;
        bl = (*BATu & 0x00001FFC) << 15;
#if defined (DEBUG_BATS)
        if (loglevel > 0) {
            fprintf(logfile, "%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x\n",
                    __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
                    *BATu, *BATl);
        }
#endif
        if ((virtual & 0xF0000000) == BEPIu &&
            ((virtual & 0x0FFE0000) & ~bl) == BEPIl) {
            /* BAT matches */
            if ((msr_pr == 0 && (*BATu & 0x00000002)) ||
                (msr_pr == 1 && (*BATu & 0x00000001))) {
                /* Get physical address */
                *real = (*BATl & 0xF0000000) |
                    ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) |
82
                    (virtual & 0x0001F000);
83
                if (*BATl & 0x00000001)
B
bellard 已提交
84
                    *prot = PAGE_READ;
85
                if (*BATl & 0x00000002)
B
bellard 已提交
86
                    *prot = PAGE_WRITE | PAGE_READ;
87 88 89
#if defined (DEBUG_BATS)
                if (loglevel > 0) {
                    fprintf(logfile, "BAT %d match: r 0x%08x prot=%c%c\n",
B
bellard 已提交
90 91
                            i, *real, *prot & PAGE_READ ? 'R' : '-',
                            *prot & PAGE_WRITE ? 'W' : '-');
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
                }
#endif
                ret = 0;
                break;
            }
        }
    }
    if (ret < 0) {
#if defined (DEBUG_BATS)
        printf("no BAT match for 0x%08x:\n", virtual);
        for (i = 0; i < 4; i++) {
            BATu = &BATut[i];
            BATl = &BATlt[i];
            BEPIu = *BATu & 0xF0000000;
            BEPIl = *BATu & 0x0FFE0000;
            bl = (*BATu & 0x00001FFC) << 15;
            printf("%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x \n\t"
                   "0x%08x 0x%08x 0x%08x\n",
                   __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
                   *BATu, *BATl, BEPIu, BEPIl, bl);
        }
#endif
    }
    /* No hit */
    return ret;
}

/* PTE table lookup */
static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va,
                     int h, int key, int rw)
{
123
    uint32_t pte0, pte1, keep = 0, access = 0;
124 125 126 127
    int i, good = -1, store = 0;
    int ret = -1; /* No entry found */

    for (i = 0; i < 8; i++) {
B
bellard 已提交
128 129
        pte0 = ldl_phys(base + (i * 8));
        pte1 =  ldl_phys(base + (i * 8) + 4);
130
#if defined (DEBUG_MMU)
131 132 133 134 135
	if (loglevel > 0) {
	    fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x "
		    "%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1,
		    pte0 >> 31, h, (pte0 >> 6) & 1, va);
	}
136 137 138 139 140 141 142 143 144 145 146
#endif
        /* Check validity and table match */
        if (pte0 & 0x80000000 && (h == ((pte0 >> 6) & 1))) {
            /* Check vsid & api */
            if ((pte0 & 0x7FFFFFBF) == va) {
                if (good == -1) {
                    good = i;
                    keep = pte1;
                } else {
                    /* All matches should have equal RPN, WIMG & PP */
                    if ((keep & 0xFFFFF07B) != (pte1 & 0xFFFFF07B)) {
147 148
			if (loglevel > 0)
			    fprintf(logfile, "Bad RPN/WIMG/PP\n");
149 150 151 152 153
                        return -1;
                    }
                }
                /* Check access rights */
                if (key == 0) {
B
bellard 已提交
154
                    access = PAGE_READ;
155
                    if ((pte1 & 0x00000003) != 0x3)
B
bellard 已提交
156
                        access |= PAGE_WRITE;
157 158 159
                } else {
                    switch (pte1 & 0x00000003) {
                    case 0x0:
160
                        access = 0;
161 162 163
                        break;
                    case 0x1:
                    case 0x3:
B
bellard 已提交
164
                        access = PAGE_READ;
165 166
                        break;
                    case 0x2:
B
bellard 已提交
167
                        access = PAGE_READ | PAGE_WRITE;
168 169 170
                        break;
                    }
                }
171
                if (ret < 0) {
B
bellard 已提交
172 173
		    if ((rw == 0 && (access & PAGE_READ)) ||
			(rw == 1 && (access & PAGE_WRITE))) {
174
#if defined (DEBUG_MMU)
175 176
			if (loglevel > 0)
			    fprintf(logfile, "PTE access granted !\n");
177 178 179 180
#endif
                    good = i;
                    keep = pte1;
                    ret = 0;
181 182 183
		    } else {
			/* Access right violation */
			ret = -2;
184
#if defined (DEBUG_MMU)
185 186
			if (loglevel > 0)
			    fprintf(logfile, "PTE access rejected\n");
187 188
#endif
                }
189 190
		    *prot = access;
		}
191 192 193 194 195 196
            }
        }
    }
    if (good != -1) {
        *RPN = keep & 0xFFFFF000;
#if defined (DEBUG_MMU)
197 198
	if (loglevel > 0) {
	    fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n",
199
               *RPN, *prot, ret);
200
	}
201 202 203
#endif
        /* Update page flags */
        if (!(keep & 0x00000100)) {
204
	    /* Access flag */
205 206 207 208
            keep |= 0x00000100;
            store = 1;
        }
            if (!(keep & 0x00000080)) {
209 210
	    if (rw && ret == 0) {
		/* Change flag */
211 212
                keep |= 0x00000080;
                store = 1;
213 214
	    } else {
		/* Force page fault for first write access */
B
bellard 已提交
215
		*prot &= ~PAGE_WRITE;
216 217
            }
        }
218
        if (store) {
B
bellard 已提交
219
	    stl_phys_notdirty(base + (good * 8) + 4, keep);
220
	}
221 222 223
    }

    return ret;
B
bellard 已提交
224 225
}

226
static inline uint32_t get_pgaddr (uint32_t sdr1, uint32_t hash, uint32_t mask)
B
bellard 已提交
227
{
228
    return (sdr1 & 0xFFFF0000) | (hash & mask);
B
bellard 已提交
229 230
}

231 232 233
/* Perform segment based translation */
static int get_segment (CPUState *env, uint32_t *real, int *prot,
                        uint32_t virtual, int rw, int type)
B
bellard 已提交
234
{
235 236 237 238 239
    uint32_t pg_addr, sdr, ptem, vsid, pgidx;
    uint32_t hash, mask;
    uint32_t sr;
    int key;
    int ret = -1, ret2;
B
bellard 已提交
240

241 242
    sr = env->sr[virtual >> 28];
#if defined (DEBUG_MMU)
243 244 245 246 247 248
    if (loglevel > 0) {
	fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x "
		"lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n",
		virtual, virtual >> 28, sr, env->nip,
		env->lr, msr_ir, msr_dr, msr_pr, rw, type);
    }
249
#endif
250 251
    key = (((sr & 0x20000000) && msr_pr == 1) ||
        ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
252 253
    if ((sr & 0x80000000) == 0) {
#if defined (DEBUG_MMU)
254 255 256
	if (loglevel > 0)
	    fprintf(logfile, "pte segment: key=%d n=0x%08x\n",
		    key, sr & 0x10000000);
257 258 259 260 261 262
#endif
        /* Check if instruction fetch is allowed, if needed */
        if (type != ACCESS_CODE || (sr & 0x10000000) == 0) {
            /* Page address translation */
            vsid = sr & 0x00FFFFFF;
            pgidx = (virtual >> 12) & 0xFFFF;
263 264
            sdr = env->sdr1;
            hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6;
265 266 267 268
            mask = ((sdr & 0x000001FF) << 16) | 0xFFC0;
            pg_addr = get_pgaddr(sdr, hash, mask);
            ptem = (vsid << 7) | (pgidx >> 10);
#if defined (DEBUG_MMU)
269 270 271 272 273
	    if (loglevel > 0) {
		fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x "
			"hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx, hash,
			pg_addr);
	    }
274 275 276 277 278 279 280 281
#endif
            /* Primary table lookup */
            ret = find_pte(real, prot, pg_addr, ptem, 0, key, rw);
            if (ret < 0) {
                /* Secondary table lookup */
                hash = (~hash) & 0x01FFFFC0;
                pg_addr = get_pgaddr(sdr, hash, mask);
#if defined (DEBUG_MMU)
282 283 284 285 286
		if (virtual != 0xEFFFFFFF && loglevel > 0) {
		    fprintf(logfile, "1 sdr1=0x%08x vsid=0x%06x api=0x%04x "
			    "hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, pgidx,
			    hash, pg_addr);
		}
287 288 289 290 291 292 293
#endif
                ret2 = find_pte(real, prot, pg_addr, ptem, 1, key, rw);
                if (ret2 != -1)
                    ret = ret2;
            }
        } else {
#if defined (DEBUG_MMU)
294 295
	    if (loglevel > 0)
		fprintf(logfile, "No access allowed\n");
296
#endif
297
	    ret = -3;
298 299 300
        }
    } else {
#if defined (DEBUG_MMU)
301 302
        if (loglevel > 0)
	    fprintf(logfile, "direct store...\n");
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
#endif
        /* Direct-store segment : absolutely *BUGGY* for now */
        switch (type) {
        case ACCESS_INT:
            /* Integer load/store : only access allowed */
            break;
        case ACCESS_CODE:
            /* No code fetch is allowed in direct-store areas */
            return -4;
        case ACCESS_FLOAT:
            /* Floating point load/store */
            return -4;
        case ACCESS_RES:
            /* lwarx, ldarx or srwcx. */
            return -4;
        case ACCESS_CACHE:
            /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
            /* Should make the instruction do no-op.
             * As it already do no-op, it's quite easy :-)
             */
            *real = virtual;
            return 0;
        case ACCESS_EXT:
            /* eciwx or ecowx */
            return -4;
        default:
            if (logfile) {
                fprintf(logfile, "ERROR: instruction should not need "
                        "address translation\n");
            }
            printf("ERROR: instruction should not need "
                   "address translation\n");
            return -4;
        }
        if ((rw == 1 || key != 1) && (rw == 0 || key != 0)) {
            *real = virtual;
            ret = 2;
        } else {
            ret = -2;
        }
B
bellard 已提交
343
    }
344 345

    return ret;
B
bellard 已提交
346 347
}

348 349 350 351
int get_physical_address (CPUState *env, uint32_t *physical, int *prot,
                          uint32_t address, int rw, int access_type)
{
    int ret;
B
bellard 已提交
352
#if 0
353 354 355
    if (loglevel > 0) {
        fprintf(logfile, "%s\n", __func__);
    }
B
bellard 已提交
356
#endif    
B
bellard 已提交
357 358
    if ((access_type == ACCESS_CODE && msr_ir == 0) ||
        (access_type != ACCESS_CODE && msr_dr == 0)) {
359
        /* No address translation */
360
        *physical = address & ~0xFFF;
B
bellard 已提交
361
        *prot = PAGE_READ | PAGE_WRITE;
362 363 364 365 366 367 368 369 370
        ret = 0;
    } else {
        /* Try to find a BAT */
        ret = get_bat(env, physical, prot, address, rw, access_type);
        if (ret < 0) {
            /* We didn't match any BAT entry */
            ret = get_segment(env, physical, prot, address, rw, access_type);
        }
    }
B
bellard 已提交
371
#if 0
372 373 374 375
    if (loglevel > 0) {
        fprintf(logfile, "%s address %08x => %08x\n",
		__func__, address, *physical);
    }
B
bellard 已提交
376
#endif    
377 378 379
    return ret;
}

B
bellard 已提交
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
#if defined(CONFIG_USER_ONLY) 
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
    return addr;
}
#else
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
{
    uint32_t phys_addr;
    int prot;

    if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
        return -1;
    return phys_addr;
}
#endif
396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

#if !defined(CONFIG_USER_ONLY) 

#define MMUSUFFIX _mmu
#define GETPC() (__builtin_return_address(0))

#define SHIFT 0
#include "softmmu_template.h"

#define SHIFT 1
#include "softmmu_template.h"

#define SHIFT 2
#include "softmmu_template.h"

#define SHIFT 3
#include "softmmu_template.h"

/* try to fill the TLB and return an exception if error. If retaddr is
   NULL, it means that the function was called in C code (i.e. not
   from generated code or from helper.c) */
/* XXX: fix it to restore all registers */
B
bellard 已提交
418
void tlb_fill(target_ulong addr, int is_write, int is_user, void *retaddr)
419 420 421
{
    TranslationBlock *tb;
    CPUState *saved_env;
422 423
    unsigned long pc;
    int ret;
424 425 426 427 428

    /* XXX: hack to restore env in all cases, even if not called from
       generated code */
    saved_env = env;
    env = cpu_single_env;
B
bellard 已提交
429
#if 0
430 431 432 433 434 435
    {
        unsigned long tlb_addrr, tlb_addrw;
        int index;
        index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
        tlb_addrr = env->tlb_read[is_user][index].address;
        tlb_addrw = env->tlb_write[is_user][index].address;
B
bellard 已提交
436 437 438
        if (loglevel) {
            fprintf(logfile,
                    "%s 1 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx "
439 440 441 442
               "(0x%08lx 0x%08lx)\n", __func__, env,
               &env->tlb_read[is_user][index], index, addr,
               tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK,
               tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK));
B
bellard 已提交
443
        }
444
    }
B
bellard 已提交
445
#endif
446
    ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1);
447 448 449 450 451 452 453 454
    if (ret) {
        if (retaddr) {
            /* now we have a real cpu fault */
            pc = (unsigned long)retaddr;
            tb = tb_find_pc(pc);
            if (tb) {
                /* the PC is inside the translated code. It means that we have
                   a virtual CPU fault */
B
bellard 已提交
455
                cpu_restore_state(tb, env, pc, NULL);
456 457
            }
        }
458
        do_raise_exception_err(env->exception_index, env->error_code);
459
    }
B
bellard 已提交
460
#if 0
461 462 463 464 465 466 467 468 469 470 471 472
    {
        unsigned long tlb_addrr, tlb_addrw;
        int index;
        index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
        tlb_addrr = env->tlb_read[is_user][index].address;
        tlb_addrw = env->tlb_write[is_user][index].address;
        printf("%s 2 %p %p idx=%d addr=0x%08lx tbl_addr=0x%08lx 0x%08lx "
               "(0x%08lx 0x%08lx)\n", __func__, env,
               &env->tlb_read[is_user][index], index, addr,
               tlb_addrr, tlb_addrw, addr & TARGET_PAGE_MASK,
               tlb_addrr & (TARGET_PAGE_MASK | TLB_INVALID_MASK));
    }
B
bellard 已提交
473
#endif
474 475 476
    env = saved_env;
}

477
void cpu_ppc_init_mmu(CPUState *env)
478 479 480 481 482 483 484
{
    /* Nothing to do: all translation are disabled */
}
#endif

/* Perform address translation */
int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
485
                              int is_user, int is_softmmu)
486 487 488 489
{
    uint32_t physical;
    int prot;
    int exception = 0, error_code = 0;
490
    int access_type;
491 492
    int ret = 0;

B
bellard 已提交
493 494 495 496 497 498 499 500 501 502 503
    if (rw == 2) {
        /* code access */
        rw = 0;
        access_type = ACCESS_CODE;
    } else {
        /* data access */
        /* XXX: put correct access by using cpu_restore_state()
           correctly */
        access_type = ACCESS_INT;
        //        access_type = env->access_type;
    }
504 505
    if (env->user_mode_only) {
        /* user mode only emulation */
B
bellard 已提交
506
        ret = -2;
507 508 509 510 511
        goto do_fault;
    }
    ret = get_physical_address(env, &physical, &prot,
                               address, rw, access_type);
    if (ret == 0) {
512 513
	ret = tlb_set_page(env, address & ~0xFFF, physical, prot,
			   is_user, is_softmmu);
514 515 516
    } else if (ret < 0) {
    do_fault:
#if defined (DEBUG_MMU)
517
	if (loglevel > 0)
B
bellard 已提交
518
	    cpu_dump_state(env, logfile, fprintf, 0);
519 520 521 522 523 524 525 526 527 528 529 530 531
#endif
        if (access_type == ACCESS_CODE) {
            exception = EXCP_ISI;
            switch (ret) {
            case -1:
                /* No matches in page tables */
                error_code = EXCP_ISI_TRANSLATE;
                break;
            case -2:
                /* Access rights violation */
                error_code = EXCP_ISI_PROT;
                break;
            case -3:
532
		/* No execute protection violation */
533 534 535 536 537
                error_code = EXCP_ISI_NOEXEC;
                break;
            case -4:
                /* Direct store exception */
                /* No code fetch is allowed in direct-store areas */
538
                error_code = EXCP_ISI_DIRECT;
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
                break;
            }
        } else {
            exception = EXCP_DSI;
            switch (ret) {
            case -1:
                /* No matches in page tables */
                error_code = EXCP_DSI_TRANSLATE;
                break;
            case -2:
                /* Access rights violation */
                error_code = EXCP_DSI_PROT;
                break;
            case -4:
                /* Direct store exception */
                switch (access_type) {
                case ACCESS_FLOAT:
                    /* Floating point load/store */
                    exception = EXCP_ALIGN;
                    error_code = EXCP_ALIGN_FP;
                    break;
                case ACCESS_RES:
                    /* lwarx, ldarx or srwcx. */
                    exception = EXCP_DSI;
                    error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT;
                    break;
                case ACCESS_EXT:
                    /* eciwx or ecowx */
                    exception = EXCP_DSI;
568 569
                    error_code = EXCP_DSI_NOTSUP | EXCP_DSI_DIRECT |
			EXCP_DSI_ECXW;
570 571
                    break;
                default:
572
		    printf("DSI: invalid exception (%d)\n", ret);
573 574 575 576 577 578 579
                    exception = EXCP_PROGRAM;
                    error_code = EXCP_INVAL | EXCP_INVAL_INVAL;
                    break;
                }
            }
            if (rw)
                error_code |= EXCP_DSI_STORE;
580 581
	    /* Store fault address */
	    env->spr[DAR] = address;
582 583 584 585 586 587 588 589 590 591 592 593
        }
#if 0
        printf("%s: set exception to %d %02x\n",
               __func__, exception, error_code);
#endif
        env->exception_index = exception;
        env->error_code = error_code;
        ret = 1;
    }
    return ret;
}

594
uint32_t _load_xer (CPUState *env)
B
bellard 已提交
595 596 597 598 599 600 601
{
    return (xer_so << XER_SO) |
        (xer_ov << XER_OV) |
        (xer_ca << XER_CA) |
        (xer_bc << XER_BC);
}

602
void _store_xer (CPUState *env, uint32_t value)
B
bellard 已提交
603 604 605 606 607 608 609
{
    xer_so = (value >> XER_SO) & 0x01;
    xer_ov = (value >> XER_OV) & 0x01;
    xer_ca = (value >> XER_CA) & 0x01;
    xer_bc = (value >> XER_BC) & 0x1f;
}

610
uint32_t _load_msr (CPUState *env)
B
bellard 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
{
    return (msr_pow << MSR_POW) |
        (msr_ile << MSR_ILE) |
        (msr_ee << MSR_EE) |
        (msr_pr << MSR_PR) |
        (msr_fp << MSR_FP) |
        (msr_me << MSR_ME) |
        (msr_fe0 << MSR_FE0) |
        (msr_se << MSR_SE) |
        (msr_be << MSR_BE) |
        (msr_fe1 << MSR_FE1) |
        (msr_ip << MSR_IP) |
        (msr_ir << MSR_IR) |
        (msr_dr << MSR_DR) |
        (msr_ri << MSR_RI) |
        (msr_le << MSR_LE);
}

629
void _store_msr (CPUState *env, uint32_t value)
B
bellard 已提交
630
{
B
bellard 已提交
631
#if 0 // TRY
B
bellard 已提交
632
    if (((value >> MSR_IR) & 0x01) != msr_ir ||
B
bellard 已提交
633 634
        ((value >> MSR_DR) & 0x01) != msr_dr)
    {
635
        /* Flush all tlb when changing translation mode or privilege level */
B
bellard 已提交
636
	tlb_flush(env, 1);
637
    }
B
bellard 已提交
638
#endif
639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
    msr_pow = (value >> MSR_POW) & 0x03;
    msr_ile = (value >> MSR_ILE) & 0x01;
    msr_ee = (value >> MSR_EE) & 0x01;
    msr_pr = (value >> MSR_PR) & 0x01;
    msr_fp = (value >> MSR_FP) & 0x01;
    msr_me = (value >> MSR_ME) & 0x01;
    msr_fe0 = (value >> MSR_FE0) & 0x01;
    msr_se = (value >> MSR_SE) & 0x01;
    msr_be = (value >> MSR_BE) & 0x01;
    msr_fe1 = (value >> MSR_FE1) & 0x01;
    msr_ip = (value >> MSR_IP) & 0x01;
    msr_ir = (value >> MSR_IR) & 0x01;
    msr_dr = (value >> MSR_DR) & 0x01;
    msr_ri = (value >> MSR_RI) & 0x01;
    msr_le = (value >> MSR_LE) & 0x01;
654
    /* XXX: should enter PM state if msr_pow has been set */
B
bellard 已提交
655 656
}

657
#if defined (CONFIG_USER_ONLY)
658
void do_interrupt (CPUState *env)
B
bellard 已提交
659
{
660 661
    env->exception_index = -1;
}
662
#else
663 664
void do_interrupt (CPUState *env)
{
665
    uint32_t msr;
666
    int excp;
B
bellard 已提交
667

668
    excp = env->exception_index;
669
    msr = _load_msr(env);
670
#if defined (DEBUG_EXCEPTIONS)
671
    if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) 
672 673 674 675
    {
        if (loglevel > 0) {
            fprintf(logfile, "Raise exception at 0x%08x => 0x%08x (%02x)\n",
                    env->nip, excp << 8, env->error_code);
B
bellard 已提交
676
        }
677
	if (loglevel > 0)
B
bellard 已提交
678
	    cpu_dump_state(env, logfile, fprintf, 0);
B
bellard 已提交
679
    }
680
#endif
B
bellard 已提交
681 682 683 684
    if (loglevel & CPU_LOG_INT) {
        fprintf(logfile, "Raise exception at 0x%08x => 0x%08x (%02x)\n",
                env->nip, excp << 8, env->error_code);
    }
685 686 687 688 689 690 691 692 693 694 695 696 697 698
    /* Generate informations in save/restore registers */
    switch (excp) {
    case EXCP_NONE:
        /* Do nothing */
#if defined (DEBUG_EXCEPTIONS)
        printf("%s: escape EXCP_NONE\n", __func__);
#endif
        return;
    case EXCP_RESET:
        if (msr_ip)
            excp += 0xFFC00;
        goto store_next;
    case EXCP_MACHINE_CHECK:
        if (msr_me == 0) {
B
bellard 已提交
699
            cpu_abort(env, "Machine check exception while not allowed\n");
B
bellard 已提交
700
        }
701 702 703 704 705 706 707
        msr_me = 0;
        break;
    case EXCP_DSI:
        /* Store exception cause */
        /* data location address has been stored
         * when the fault has been detected
     */
708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
	msr &= ~0xFFFF0000;
	env->spr[DSISR] = 0;
	if (env->error_code &  EXCP_DSI_TRANSLATE)
	    env->spr[DSISR] |= 0x40000000;
	else if (env->error_code & EXCP_DSI_PROT)
	    env->spr[DSISR] |= 0x08000000;
	else if (env->error_code & EXCP_DSI_NOTSUP) {
	    env->spr[DSISR] |= 0x80000000;
	    if (env->error_code & EXCP_DSI_DIRECT)
		env->spr[DSISR] |= 0x04000000;
	}
	if (env->error_code & EXCP_DSI_STORE)
	    env->spr[DSISR] |= 0x02000000;
	if ((env->error_code & 0xF) == EXCP_DSI_DABR)
	    env->spr[DSISR] |= 0x00400000;
	if (env->error_code & EXCP_DSI_ECXW)
	    env->spr[DSISR] |= 0x00100000;
#if defined (DEBUG_EXCEPTIONS)
	if (loglevel) {
	    fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
		    env->spr[DSISR], env->spr[DAR]);
	} else {
	    printf("DSI exception: DSISR=0x%08x, DAR=0x%08x nip=0x%08x\n",
		   env->spr[DSISR], env->spr[DAR], env->nip);
	}
#endif
        goto store_next;
735 736
    case EXCP_ISI:
        /* Store exception cause */
737
	msr &= ~0xFFFF0000;
738 739 740
        if (env->error_code == EXCP_ISI_TRANSLATE)
            msr |= 0x40000000;
        else if (env->error_code == EXCP_ISI_NOEXEC ||
741 742
		 env->error_code == EXCP_ISI_GUARD ||
		 env->error_code == EXCP_ISI_DIRECT)
743 744 745
            msr |= 0x10000000;
        else
            msr |= 0x08000000;
746 747 748 749 750 751 752 753 754
#if defined (DEBUG_EXCEPTIONS)
	if (loglevel) {
	    fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n",
		    msr, env->nip);
	} else {
	    printf("ISI exception: msr=0x%08x, nip=0x%08x tbl:0x%08x\n",
		   msr, env->nip, env->spr[V_TBL]);
	}
#endif
755 756 757 758 759 760
        goto store_next;
    case EXCP_EXTERNAL:
        if (msr_ee == 0) {
#if defined (DEBUG_EXCEPTIONS)
            if (loglevel > 0) {
                fprintf(logfile, "Skipping hardware interrupt\n");
B
bellard 已提交
761
    }
762
#endif
763
            /* Requeue it */
764
            do_raise_exception(EXCP_EXTERNAL);
765
            return;
B
bellard 已提交
766
            }
767 768 769 770 771
        goto store_next;
    case EXCP_ALIGN:
        /* Store exception cause */
        /* Get rS/rD and rA from faulting opcode */
        env->spr[DSISR] |=
B
bellard 已提交
772
            (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
773 774 775 776 777 778 779 780 781 782 783 784 785
        /* data location address has been stored
         * when the fault has been detected
         */
        goto store_current;
    case EXCP_PROGRAM:
        msr &= ~0xFFFF0000;
        switch (env->error_code & ~0xF) {
        case EXCP_FP:
            if (msr_fe0 == 0 && msr_fe1 == 0) {
#if defined (DEBUG_EXCEPTIONS)
                printf("Ignore floating point exception\n");
#endif
                return;
B
bellard 已提交
786
        }
787 788 789 790 791 792 793 794 795
            msr |= 0x00100000;
            /* Set FX */
            env->fpscr[7] |= 0x8;
            /* Finally, update FEX */
            if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
                ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
                env->fpscr[7] |= 0x4;
        break;
        case EXCP_INVAL:
B
bellard 已提交
796
            //	    printf("Invalid instruction at 0x%08x\n", env->nip);
797 798 799 800 801 802 803 804 805 806 807
            msr |= 0x00080000;
        break;
        case EXCP_PRIV:
            msr |= 0x00040000;
        break;
        case EXCP_TRAP:
            msr |= 0x00020000;
            break;
        default:
            /* Should never occur */
        break;
B
bellard 已提交
808
    }
809 810 811 812 813 814 815
        msr |= 0x00010000;
        goto store_current;
    case EXCP_NO_FP:
        goto store_current;
    case EXCP_DECR:
        if (msr_ee == 0) {
            /* Requeue it */
816
            do_raise_exception(EXCP_DECR);
817 818 819 820
            return;
        }
        goto store_next;
    case EXCP_SYSCALL:
B
bellard 已提交
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
        if (loglevel & CPU_LOG_INT) {
            fprintf(logfile, "syscall %d 0x%08x 0x%08x 0x%08x 0x%08x\n",
                    env->gpr[0], env->gpr[3], env->gpr[4],
                    env->gpr[5], env->gpr[6]);
            if (env->gpr[0] == 4 && env->gpr[3] == 1) {
                int len, addr, i;
                uint8_t c;

                fprintf(logfile, "write: ");
                addr = env->gpr[4];
                len = env->gpr[5];
                if (len > 64)
                    len = 64;
                for(i = 0; i < len; i++) {
                    c = 0;
                    cpu_memory_rw_debug(env, addr + i, &c, 1, 0);
                    if (c < 32 || c > 126)
                        c = '.';
                    fprintf(logfile, "%c", c);
                }
                fprintf(logfile, "\n");
            }
        }
844 845 846 847 848 849 850 851 852 853 854 855 856
        goto store_next;
    case EXCP_TRACE:
        goto store_next;
    case EXCP_FP_ASSIST:
        goto store_next;
    case EXCP_MTMSR:
        /* Nothing to do */
        return;
    case EXCP_BRANCH:
        /* Nothing to do */
        return;
    case EXCP_RFI:
        /* Restore user-mode state */
857
	tb_flush(env);
858
#if defined (DEBUG_EXCEPTIONS)
859 860
	if (msr_pr == 1)
	    printf("Return from exception => 0x%08x\n", (uint32_t)env->nip);
861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889
#endif
        return;
    store_current:
        /* SRR0 is set to current instruction */
        env->spr[SRR0] = (uint32_t)env->nip - 4;
        break;
    store_next:
        /* SRR0 is set to next instruction */
        env->spr[SRR0] = (uint32_t)env->nip;
        break;
    }
    env->spr[SRR1] = msr;
    /* reload MSR with correct bits */
    msr_pow = 0;
    msr_ee = 0;
    msr_pr = 0;
    msr_fp = 0;
    msr_fe0 = 0;
    msr_se = 0;
    msr_be = 0;
    msr_fe1 = 0;
    msr_ir = 0;
    msr_dr = 0;
    msr_ri = 0;
    msr_le = msr_ile;
    /* Jump to handler */
    env->nip = excp << 8;
    env->exception_index = EXCP_NONE;
    /* Invalidate all TLB as we may have changed translation mode */
B
bellard 已提交
890
    tlb_flush(env, 1);
891 892 893 894 895 896 897
    /* ensure that no TB jump will be modified as
       the program flow was changed */
#ifdef __sparc__
    tmp_T0 = 0;
#else
    T0 = 0;
#endif
898
    env->exception_index = -1;
B
bellard 已提交
899
}
900
#endif /* !CONFIG_USER_ONLY */