|
|
1.1 ! root 1: /* ! 2: * QEMU generic PPC hardware System Emulator ! 3: * ! 4: * Copyright (c) 2003-2004 Jocelyn Mayer ! 5: * ! 6: * Permission is hereby granted, free of charge, to any person obtaining a copy ! 7: * of this software and associated documentation files (the "Software"), to deal ! 8: * in the Software without restriction, including without limitation the rights ! 9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ! 10: * copies of the Software, and to permit persons to whom the Software is ! 11: * furnished to do so, subject to the following conditions: ! 12: * ! 13: * The above copyright notice and this permission notice shall be included in ! 14: * all copies or substantial portions of the Software. ! 15: * ! 16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ! 17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ! 18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ! 19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ! 20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ! 21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ! 22: * THE SOFTWARE. ! 23: */ ! 24: #include "vl.h" ! 25: #include "m48t59.h" ! 26: ! 27: /*****************************************************************************/ ! 28: /* PPC time base and decrementer emulation */ ! 29: //#define DEBUG_TB ! 30: ! 31: struct ppc_tb_t { ! 32: /* Time base management */ ! 33: int64_t tb_offset; /* Compensation */ ! 34: uint32_t tb_freq; /* TB frequency */ ! 35: /* Decrementer management */ ! 36: uint64_t decr_next; /* Tick for next decr interrupt */ ! 37: struct QEMUTimer *decr_timer; ! 38: }; ! 39: ! 40: static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env) ! 41: { ! 42: /* TB time in tb periods */ ! 43: return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset, ! 44: tb_env->tb_freq, ticks_per_sec); ! 45: } ! 46: ! 47: uint32_t cpu_ppc_load_tbl (CPUState *env) ! 48: { ! 49: ppc_tb_t *tb_env = env->tb_env; ! 50: uint64_t tb; ! 51: ! 52: tb = cpu_ppc_get_tb(tb_env); ! 53: #ifdef DEBUG_TB ! 54: { ! 55: static int last_time; ! 56: int now; ! 57: now = time(NULL); ! 58: if (last_time != now) { ! 59: last_time = now; ! 60: printf("%s: tb=0x%016lx %d %08lx\n", ! 61: __func__, tb, now, tb_env->tb_offset); ! 62: } ! 63: } ! 64: #endif ! 65: ! 66: return tb & 0xFFFFFFFF; ! 67: } ! 68: ! 69: uint32_t cpu_ppc_load_tbu (CPUState *env) ! 70: { ! 71: ppc_tb_t *tb_env = env->tb_env; ! 72: uint64_t tb; ! 73: ! 74: tb = cpu_ppc_get_tb(tb_env); ! 75: #ifdef DEBUG_TB ! 76: printf("%s: tb=0x%016lx\n", __func__, tb); ! 77: #endif ! 78: return tb >> 32; ! 79: } ! 80: ! 81: static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value) ! 82: { ! 83: tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq) ! 84: - qemu_get_clock(vm_clock); ! 85: #ifdef DEBUG_TB ! 86: printf("%s: tb=0x%016lx offset=%08x\n", __func__, value); ! 87: #endif ! 88: } ! 89: ! 90: void cpu_ppc_store_tbu (CPUState *env, uint32_t value) ! 91: { ! 92: ppc_tb_t *tb_env = env->tb_env; ! 93: ! 94: cpu_ppc_store_tb(tb_env, ! 95: ((uint64_t)value << 32) | cpu_ppc_load_tbl(env)); ! 96: } ! 97: ! 98: void cpu_ppc_store_tbl (CPUState *env, uint32_t value) ! 99: { ! 100: ppc_tb_t *tb_env = env->tb_env; ! 101: ! 102: cpu_ppc_store_tb(tb_env, ! 103: ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value); ! 104: } ! 105: ! 106: uint32_t cpu_ppc_load_decr (CPUState *env) ! 107: { ! 108: ppc_tb_t *tb_env = env->tb_env; ! 109: uint32_t decr; ! 110: int64_t diff; ! 111: ! 112: diff = tb_env->decr_next - qemu_get_clock(vm_clock); ! 113: if (diff >= 0) ! 114: decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec); ! 115: else ! 116: decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec); ! 117: #if defined(DEBUG_TB) ! 118: printf("%s: 0x%08x\n", __func__, decr); ! 119: #endif ! 120: return decr; ! 121: } ! 122: ! 123: /* When decrementer expires, ! 124: * all we need to do is generate or queue a CPU exception ! 125: */ ! 126: static inline void cpu_ppc_decr_excp (CPUState *env) ! 127: { ! 128: /* Raise it */ ! 129: #ifdef DEBUG_TB ! 130: printf("raise decrementer exception\n"); ! 131: #endif ! 132: cpu_interrupt(env, CPU_INTERRUPT_TIMER); ! 133: } ! 134: ! 135: static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, ! 136: uint32_t value, int is_excp) ! 137: { ! 138: ppc_tb_t *tb_env = env->tb_env; ! 139: uint64_t now, next; ! 140: ! 141: #ifdef DEBUG_TB ! 142: printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value); ! 143: #endif ! 144: now = qemu_get_clock(vm_clock); ! 145: next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq); ! 146: if (is_excp) ! 147: next += tb_env->decr_next - now; ! 148: if (next == now) ! 149: next++; ! 150: tb_env->decr_next = next; ! 151: /* Adjust timer */ ! 152: qemu_mod_timer(tb_env->decr_timer, next); ! 153: /* If we set a negative value and the decrementer was positive, ! 154: * raise an exception. ! 155: */ ! 156: if ((value & 0x80000000) && !(decr & 0x80000000)) ! 157: cpu_ppc_decr_excp(env); ! 158: } ! 159: ! 160: void cpu_ppc_store_decr (CPUState *env, uint32_t value) ! 161: { ! 162: _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0); ! 163: } ! 164: ! 165: static void cpu_ppc_decr_cb (void *opaque) ! 166: { ! 167: _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); ! 168: } ! 169: ! 170: /* Set up (once) timebase frequency (in Hz) */ ! 171: ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq) ! 172: { ! 173: ppc_tb_t *tb_env; ! 174: ! 175: tb_env = qemu_mallocz(sizeof(ppc_tb_t)); ! 176: if (tb_env == NULL) ! 177: return NULL; ! 178: env->tb_env = tb_env; ! 179: if (tb_env->tb_freq == 0 || 1) { ! 180: tb_env->tb_freq = freq; ! 181: /* Create new timer */ ! 182: tb_env->decr_timer = ! 183: qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env); ! 184: /* There is a bug in 2.4 kernels: ! 185: * if a decrementer exception is pending when it enables msr_ee, ! 186: * it's not ready to handle it... ! 187: */ ! 188: _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); ! 189: } ! 190: ! 191: return tb_env; ! 192: } ! 193: ! 194: #if 0 ! 195: /*****************************************************************************/ ! 196: /* Handle system reset (for now, just stop emulation) */ ! 197: void cpu_ppc_reset (CPUState *env) ! 198: { ! 199: printf("Reset asked... Stop emulation\n"); ! 200: abort(); ! 201: } ! 202: #endif ! 203: ! 204: static void PPC_io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value) ! 205: { ! 206: cpu_outb(NULL, addr & 0xffff, value); ! 207: } ! 208: ! 209: static uint32_t PPC_io_readb (void *opaque, target_phys_addr_t addr) ! 210: { ! 211: uint32_t ret = cpu_inb(NULL, addr & 0xffff); ! 212: return ret; ! 213: } ! 214: ! 215: static void PPC_io_writew (void *opaque, target_phys_addr_t addr, uint32_t value) ! 216: { ! 217: #ifdef TARGET_WORDS_BIGENDIAN ! 218: value = bswap16(value); ! 219: #endif ! 220: cpu_outw(NULL, addr & 0xffff, value); ! 221: } ! 222: ! 223: static uint32_t PPC_io_readw (void *opaque, target_phys_addr_t addr) ! 224: { ! 225: uint32_t ret = cpu_inw(NULL, addr & 0xffff); ! 226: #ifdef TARGET_WORDS_BIGENDIAN ! 227: ret = bswap16(ret); ! 228: #endif ! 229: return ret; ! 230: } ! 231: ! 232: static void PPC_io_writel (void *opaque, target_phys_addr_t addr, uint32_t value) ! 233: { ! 234: #ifdef TARGET_WORDS_BIGENDIAN ! 235: value = bswap32(value); ! 236: #endif ! 237: cpu_outl(NULL, addr & 0xffff, value); ! 238: } ! 239: ! 240: static uint32_t PPC_io_readl (void *opaque, target_phys_addr_t addr) ! 241: { ! 242: uint32_t ret = cpu_inl(NULL, addr & 0xffff); ! 243: ! 244: #ifdef TARGET_WORDS_BIGENDIAN ! 245: ret = bswap32(ret); ! 246: #endif ! 247: return ret; ! 248: } ! 249: ! 250: CPUWriteMemoryFunc *PPC_io_write[] = { ! 251: &PPC_io_writeb, ! 252: &PPC_io_writew, ! 253: &PPC_io_writel, ! 254: }; ! 255: ! 256: CPUReadMemoryFunc *PPC_io_read[] = { ! 257: &PPC_io_readb, ! 258: &PPC_io_readw, ! 259: &PPC_io_readl, ! 260: }; ! 261: ! 262: /*****************************************************************************/ ! 263: /* Debug port */ ! 264: void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) ! 265: { ! 266: addr &= 0xF; ! 267: switch (addr) { ! 268: case 0: ! 269: printf("%c", val); ! 270: break; ! 271: case 1: ! 272: printf("\n"); ! 273: fflush(stdout); ! 274: break; ! 275: case 2: ! 276: printf("Set loglevel to %04x\n", val); ! 277: cpu_set_log(val | 0x100); ! 278: break; ! 279: } ! 280: } ! 281: ! 282: /*****************************************************************************/ ! 283: /* NVRAM helpers */ ! 284: void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value) ! 285: { ! 286: m48t59_set_addr(nvram, addr); ! 287: m48t59_write(nvram, value); ! 288: } ! 289: ! 290: uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr) ! 291: { ! 292: m48t59_set_addr(nvram, addr); ! 293: return m48t59_read(nvram); ! 294: } ! 295: ! 296: void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value) ! 297: { ! 298: m48t59_set_addr(nvram, addr); ! 299: m48t59_write(nvram, value >> 8); ! 300: m48t59_set_addr(nvram, addr + 1); ! 301: m48t59_write(nvram, value & 0xFF); ! 302: } ! 303: ! 304: uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr) ! 305: { ! 306: uint16_t tmp; ! 307: ! 308: m48t59_set_addr(nvram, addr); ! 309: tmp = m48t59_read(nvram) << 8; ! 310: m48t59_set_addr(nvram, addr + 1); ! 311: tmp |= m48t59_read(nvram); ! 312: ! 313: return tmp; ! 314: } ! 315: ! 316: void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value) ! 317: { ! 318: m48t59_set_addr(nvram, addr); ! 319: m48t59_write(nvram, value >> 24); ! 320: m48t59_set_addr(nvram, addr + 1); ! 321: m48t59_write(nvram, (value >> 16) & 0xFF); ! 322: m48t59_set_addr(nvram, addr + 2); ! 323: m48t59_write(nvram, (value >> 8) & 0xFF); ! 324: m48t59_set_addr(nvram, addr + 3); ! 325: m48t59_write(nvram, value & 0xFF); ! 326: } ! 327: ! 328: uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr) ! 329: { ! 330: uint32_t tmp; ! 331: ! 332: m48t59_set_addr(nvram, addr); ! 333: tmp = m48t59_read(nvram) << 24; ! 334: m48t59_set_addr(nvram, addr + 1); ! 335: tmp |= m48t59_read(nvram) << 16; ! 336: m48t59_set_addr(nvram, addr + 2); ! 337: tmp |= m48t59_read(nvram) << 8; ! 338: m48t59_set_addr(nvram, addr + 3); ! 339: tmp |= m48t59_read(nvram); ! 340: ! 341: return tmp; ! 342: } ! 343: ! 344: void NVRAM_set_string (m48t59_t *nvram, uint32_t addr, ! 345: const unsigned char *str, uint32_t max) ! 346: { ! 347: int i; ! 348: ! 349: for (i = 0; i < max && str[i] != '\0'; i++) { ! 350: m48t59_set_addr(nvram, addr + i); ! 351: m48t59_write(nvram, str[i]); ! 352: } ! 353: m48t59_set_addr(nvram, addr + max - 1); ! 354: m48t59_write(nvram, '\0'); ! 355: } ! 356: ! 357: int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max) ! 358: { ! 359: int i; ! 360: ! 361: memset(dst, 0, max); ! 362: for (i = 0; i < max; i++) { ! 363: dst[i] = NVRAM_get_byte(nvram, addr + i); ! 364: if (dst[i] == '\0') ! 365: break; ! 366: } ! 367: ! 368: return i; ! 369: } ! 370: ! 371: static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value) ! 372: { ! 373: uint16_t tmp; ! 374: uint16_t pd, pd1, pd2; ! 375: ! 376: tmp = prev >> 8; ! 377: pd = prev ^ value; ! 378: pd1 = pd & 0x000F; ! 379: pd2 = ((pd >> 4) & 0x000F) ^ pd1; ! 380: tmp ^= (pd1 << 3) | (pd1 << 8); ! 381: tmp ^= pd2 | (pd2 << 7) | (pd2 << 12); ! 382: ! 383: return tmp; ! 384: } ! 385: ! 386: uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count) ! 387: { ! 388: uint32_t i; ! 389: uint16_t crc = 0xFFFF; ! 390: int odd; ! 391: ! 392: odd = count & 1; ! 393: count &= ~1; ! 394: for (i = 0; i != count; i++) { ! 395: crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i)); ! 396: } ! 397: if (odd) { ! 398: crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8); ! 399: } ! 400: ! 401: return crc; ! 402: } ! 403: ! 404: #define CMDLINE_ADDR 0x017ff000 ! 405: ! 406: int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size, ! 407: const unsigned char *arch, ! 408: uint32_t RAM_size, int boot_device, ! 409: uint32_t kernel_image, uint32_t kernel_size, ! 410: const char *cmdline, ! 411: uint32_t initrd_image, uint32_t initrd_size, ! 412: uint32_t NVRAM_image, ! 413: int width, int height, int depth) ! 414: { ! 415: uint16_t crc; ! 416: ! 417: /* Set parameters for Open Hack'Ware BIOS */ ! 418: NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16); ! 419: NVRAM_set_lword(nvram, 0x10, 0x00000002); /* structure v2 */ ! 420: NVRAM_set_word(nvram, 0x14, NVRAM_size); ! 421: NVRAM_set_string(nvram, 0x20, arch, 16); ! 422: NVRAM_set_lword(nvram, 0x30, RAM_size); ! 423: NVRAM_set_byte(nvram, 0x34, boot_device); ! 424: NVRAM_set_lword(nvram, 0x38, kernel_image); ! 425: NVRAM_set_lword(nvram, 0x3C, kernel_size); ! 426: if (cmdline) { ! 427: /* XXX: put the cmdline in NVRAM too ? */ ! 428: strcpy(phys_ram_base + CMDLINE_ADDR, cmdline); ! 429: NVRAM_set_lword(nvram, 0x40, CMDLINE_ADDR); ! 430: NVRAM_set_lword(nvram, 0x44, strlen(cmdline)); ! 431: } else { ! 432: NVRAM_set_lword(nvram, 0x40, 0); ! 433: NVRAM_set_lword(nvram, 0x44, 0); ! 434: } ! 435: NVRAM_set_lword(nvram, 0x48, initrd_image); ! 436: NVRAM_set_lword(nvram, 0x4C, initrd_size); ! 437: NVRAM_set_lword(nvram, 0x50, NVRAM_image); ! 438: ! 439: NVRAM_set_word(nvram, 0x54, width); ! 440: NVRAM_set_word(nvram, 0x56, height); ! 441: NVRAM_set_word(nvram, 0x58, depth); ! 442: crc = NVRAM_compute_crc(nvram, 0x00, 0xF8); ! 443: NVRAM_set_word(nvram, 0xFC, crc); ! 444: ! 445: return 0; ! 446: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.