ppc_oldworld.c 13.9 KB
Newer Older
J
j_mayer 已提交
1
/*
2
 * QEMU OldWorld PowerMac (currently ~G3 Beige) hardware System Emulator
J
j_mayer 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * Copyright (c) 2004-2007 Fabrice Bellard
 * Copyright (c) 2007 Jocelyn Mayer
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
P
pbrook 已提交
25 26
#include "hw.h"
#include "ppc.h"
J
j_mayer 已提交
27
#include "ppc_mac.h"
A
aurel32 已提交
28
#include "mac_dbdma.h"
P
pbrook 已提交
29 30 31 32 33 34
#include "nvram.h"
#include "pc.h"
#include "sysemu.h"
#include "net.h"
#include "isa.h"
#include "pci.h"
35
#include "usb-ohci.h"
P
pbrook 已提交
36
#include "boards.h"
B
blueswir1 已提交
37
#include "fw_cfg.h"
B
blueswir1 已提交
38
#include "escc.h"
G
Gerd Hoffmann 已提交
39
#include "ide.h"
B
Blue Swirl 已提交
40 41
#include "loader.h"
#include "elf.h"
A
Alexander Graf 已提交
42
#include "kvm.h"
43
#include "kvm_ppc.h"
J
j_mayer 已提交
44

T
ths 已提交
45
#define MAX_IDE_BUS 2
46
#define VGA_BIOS_SIZE 65536
B
blueswir1 已提交
47 48
#define CFG_ADDR 0xf0000510

J
j_mayer 已提交
49 50 51 52 53 54 55 56
/* temporary frame buffer OSI calls for the video.x driver. The right
   solution is to modify the driver to use VGA PCI I/Os */
/* XXX: to be removed. This is no way related to emulation */
static int vga_osi_call (CPUState *env)
{
    static int vga_vbl_enabled;
    int linesize;

B
Blue Swirl 已提交
57 58 59
#if 0
    printf("osi_call R5=%016" PRIx64 "\n", ppc_dump_gpr(env, 5));
#endif
J
j_mayer 已提交
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

    /* same handler as PearPC, coming from the original MOL video
       driver. */
    switch(env->gpr[5]) {
    case 4:
        break;
    case 28: /* set_vmode */
        if (env->gpr[6] != 1 || env->gpr[7] != 0)
            env->gpr[3] = 1;
        else
            env->gpr[3] = 0;
        break;
    case 29: /* get_vmode_info */
        if (env->gpr[6] != 0) {
            if (env->gpr[6] != 1 || env->gpr[7] != 0) {
                env->gpr[3] = 1;
                break;
            }
        }
        env->gpr[3] = 0;
        env->gpr[4] = (1 << 16) | 1; /* num_vmodes, cur_vmode */
        env->gpr[5] = (1 << 16) | 0; /* num_depths, cur_depth_mode */
        env->gpr[6] = (graphic_width << 16) | graphic_height; /* w, h */
        env->gpr[7] = 85 << 16; /* refresh rate */
        env->gpr[8] = (graphic_depth + 7) & ~7; /* depth (round to byte) */
        linesize = ((graphic_depth + 7) >> 3) * graphic_width;
        linesize = (linesize + 3) & ~3;
        env->gpr[9] = (linesize << 16) | 0; /* row_bytes, offset */
        break;
    case 31: /* set_video power */
        env->gpr[3] = 0;
        break;
    case 39: /* video_ctrl */
        if (env->gpr[6] == 0 || env->gpr[6] == 1)
            vga_vbl_enabled = env->gpr[6];
        env->gpr[3] = 0;
        break;
    case 47:
        break;
    case 59: /* set_color */
        /* R6 = index, R7 = RGB */
        env->gpr[3] = 0;
        break;
    case 64: /* get color */
        /* R6 = index */
        env->gpr[3] = 0;
        break;
    case 116: /* set hwcursor */
        /* R6 = x, R7 = y, R8 = visible, R9 = data */
        break;
    default:
B
Blue Swirl 已提交
111
        fprintf(stderr, "unsupported OSI call R5=%016" PRIx64 "\n",
112
                ppc_dump_gpr(env, 5));
J
j_mayer 已提交
113 114 115 116 117 118
        break;
    }

    return 1; /* osi_call handled */
}

119 120 121 122 123 124
static int fw_cfg_boot_set(void *opaque, const char *boot_device)
{
    fw_cfg_add_i16(opaque, FW_CFG_BOOT_DEVICE, boot_device[0]);
    return 0;
}

125 126 127 128 129 130

static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
{
    return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
}

A
Anthony Liguori 已提交
131
static void ppc_heathrow_init (ram_addr_t ram_size,
132
                               const char *boot_device,
J
j_mayer 已提交
133 134 135 136 137
                               const char *kernel_filename,
                               const char *kernel_cmdline,
                               const char *initrd_filename,
                               const char *cpu_model)
{
B
bellard 已提交
138
    CPUState *env = NULL, *envs[MAX_CPUS];
P
Paul Brook 已提交
139
    char *filename;
J
j_mayer 已提交
140 141
    qemu_irq *pic, **heathrow_irqs;
    int linux_boot, i;
A
Anthony Liguori 已提交
142
    ram_addr_t ram_offset, bios_offset, vga_bios_offset;
143 144
    uint32_t kernel_base, initrd_base;
    int32_t kernel_size, initrd_size;
J
j_mayer 已提交
145 146 147 148
    PCIBus *pci_bus;
    MacIONVRAMState *nvr;
    int vga_bios_size, bios_size;
    int pic_mem_index, nvram_mem_index, dbdma_mem_index, cuda_mem_index;
B
blueswir1 已提交
149
    int escc_mem_index, ide_mem_index[2];
150
    uint16_t ppc_boot_device;
151
    DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
B
blueswir1 已提交
152
    void *fw_cfg;
A
aurel32 已提交
153
    void *dbdma;
P
pbrook 已提交
154
    uint8_t *vga_bios_ptr;
J
j_mayer 已提交
155 156 157 158 159

    linux_boot = (kernel_filename != NULL);

    /* init CPUs */
    if (cpu_model == NULL)
160
        cpu_model = "G3";
J
j_mayer 已提交
161
    for (i = 0; i < smp_cpus; i++) {
B
bellard 已提交
162 163 164 165 166
        env = cpu_init(cpu_model);
        if (!env) {
            fprintf(stderr, "Unable to find PowerPC CPU definition\n");
            exit(1);
        }
167 168
        /* Set time-base frequency to 16.6 Mhz */
        cpu_ppc_tb_init(env,  16600000UL);
J
j_mayer 已提交
169
        env->osi_call = vga_osi_call;
170
        qemu_register_reset((QEMUResetHandler*)&cpu_reset, env);
J
j_mayer 已提交
171 172 173 174
        envs[i] = env;
    }

    /* allocate RAM */
175 176 177 178 179 180 181
    if (ram_size > (2047 << 20)) {
        fprintf(stderr,
                "qemu: Too much memory for this machine: %d MB, maximum 2047 MB\n",
                ((unsigned int)ram_size / (1 << 20)));
        exit(1);
    }

182 183 184
    ram_offset = qemu_ram_alloc(ram_size);
    cpu_register_physical_memory(0, ram_size, ram_offset);

J
j_mayer 已提交
185
    /* allocate and load BIOS */
186
    bios_offset = qemu_ram_alloc(BIOS_SIZE);
J
j_mayer 已提交
187
    if (bios_name == NULL)
B
blueswir1 已提交
188
        bios_name = PROM_FILENAME;
P
Paul Brook 已提交
189
    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
B
blueswir1 已提交
190 191 192
    cpu_register_physical_memory(PROM_ADDR, BIOS_SIZE, bios_offset | IO_MEM_ROM);

    /* Load OpenBIOS (ELF) */
P
Paul Brook 已提交
193
    if (filename) {
194 195
        bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL,
                             1, ELF_MACHINE, 0);
P
Paul Brook 已提交
196 197 198 199
        qemu_free(filename);
    } else {
        bios_size = -1;
    }
J
j_mayer 已提交
200
    if (bios_size < 0 || bios_size > BIOS_SIZE) {
P
Paul Brook 已提交
201
        hw_error("qemu: could not load PowerPC bios '%s'\n", bios_name);
J
j_mayer 已提交
202 203 204 205
        exit(1);
    }

    /* allocate and load VGA BIOS */
206
    vga_bios_offset = qemu_ram_alloc(VGA_BIOS_SIZE);
P
pbrook 已提交
207
    vga_bios_ptr = qemu_get_ram_ptr(vga_bios_offset);
P
Paul Brook 已提交
208 209 210 211 212 213 214
    filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, VGABIOS_FILENAME);
    if (filename) {
        vga_bios_size = load_image(filename, vga_bios_ptr + 8);
        qemu_free(filename);
    } else {
        vga_bios_size = -1;
    }
J
j_mayer 已提交
215 216
    if (vga_bios_size < 0) {
        /* if no bios is present, we can still work */
P
Paul Brook 已提交
217 218
        fprintf(stderr, "qemu: warning: could not load VGA bios '%s'\n",
                VGABIOS_FILENAME);
J
j_mayer 已提交
219 220 221 222
        vga_bios_size = 0;
    } else {
        /* set a specific header (XXX: find real Apple format for NDRV
           drivers) */
P
pbrook 已提交
223 224 225 226 227
        vga_bios_ptr[0] = 'N';
        vga_bios_ptr[1] = 'D';
        vga_bios_ptr[2] = 'R';
        vga_bios_ptr[3] = 'V';
        cpu_to_be32w((uint32_t *)(vga_bios_ptr + 4), vga_bios_size);
J
j_mayer 已提交
228
        vga_bios_size += 8;
229 230 231 232

        /* Round to page boundary */
        vga_bios_size = (vga_bios_size + TARGET_PAGE_SIZE - 1) &
            TARGET_PAGE_MASK;
J
j_mayer 已提交
233 234 235
    }

    if (linux_boot) {
236
        uint64_t lowaddr = 0;
B
Blue Swirl 已提交
237 238 239 240 241 242 243
        int bswap_needed;

#ifdef BSWAP_NEEDED
        bswap_needed = 1;
#else
        bswap_needed = 0;
#endif
J
j_mayer 已提交
244
        kernel_base = KERNEL_LOAD_ADDR;
245 246
        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
                               NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
B
blueswir1 已提交
247 248
        if (kernel_size < 0)
            kernel_size = load_aout(kernel_filename, kernel_base,
B
Blue Swirl 已提交
249 250
                                    ram_size - kernel_base, bswap_needed,
                                    TARGET_PAGE_SIZE);
B
blueswir1 已提交
251 252 253 254
        if (kernel_size < 0)
            kernel_size = load_image_targphys(kernel_filename,
                                              kernel_base,
                                              ram_size - kernel_base);
J
j_mayer 已提交
255
        if (kernel_size < 0) {
P
Paul Brook 已提交
256
            hw_error("qemu: could not load kernel '%s'\n",
J
j_mayer 已提交
257 258 259 260 261 262
                      kernel_filename);
            exit(1);
        }
        /* load initrd */
        if (initrd_filename) {
            initrd_base = INITRD_LOAD_ADDR;
263 264
            initrd_size = load_image_targphys(initrd_filename, initrd_base,
                                              ram_size - initrd_base);
J
j_mayer 已提交
265
            if (initrd_size < 0) {
P
Paul Brook 已提交
266 267
                hw_error("qemu: could not load initial ram disk '%s'\n",
                         initrd_filename);
J
j_mayer 已提交
268 269 270 271 272 273
                exit(1);
            }
        } else {
            initrd_base = 0;
            initrd_size = 0;
        }
274
        ppc_boot_device = 'm';
J
j_mayer 已提交
275 276 277 278 279
    } else {
        kernel_base = 0;
        kernel_size = 0;
        initrd_base = 0;
        initrd_size = 0;
280
        ppc_boot_device = '\0';
J
j_mayer 已提交
281
        for (i = 0; boot_device[i] != '\0'; i++) {
282
            /* TOFIX: for now, the second IDE channel is not properly
J
j_mayer 已提交
283
             *        used by OHW. The Mac floppy disk are not emulated.
284 285 286
             *        For now, OHW cannot boot from the network.
             */
#if 0
J
j_mayer 已提交
287 288
            if (boot_device[i] >= 'a' && boot_device[i] <= 'f') {
                ppc_boot_device = boot_device[i];
289
                break;
J
j_mayer 已提交
290
            }
291
#else
J
j_mayer 已提交
292 293
            if (boot_device[i] >= 'c' && boot_device[i] <= 'd') {
                ppc_boot_device = boot_device[i];
294
                break;
J
j_mayer 已提交
295
            }
296 297 298
#endif
        }
        if (ppc_boot_device == '\0') {
299
            fprintf(stderr, "No valid boot device for G3 Beige machine\n");
300 301
            exit(1);
        }
J
j_mayer 已提交
302 303 304
    }

    isa_mem_base = 0x80000000;
305

J
j_mayer 已提交
306
    /* Register 2 MB of ISA IO space */
B
Blue Swirl 已提交
307
    isa_mmio_init(0xfe000000, 0x00200000, 1);
J
j_mayer 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321

    /* XXX: we register only 1 output pin for heathrow PIC */
    heathrow_irqs = qemu_mallocz(smp_cpus * sizeof(qemu_irq *));
    heathrow_irqs[0] =
        qemu_mallocz(smp_cpus * sizeof(qemu_irq) * 1);
    /* Connect the heathrow PIC outputs to the 6xx bus */
    for (i = 0; i < smp_cpus; i++) {
        switch (PPC_INPUT(env)) {
        case PPC_FLAGS_INPUT_6xx:
            heathrow_irqs[i] = heathrow_irqs[0] + (i * 1);
            heathrow_irqs[i][0] =
                ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT];
            break;
        default:
P
Paul Brook 已提交
322
            hw_error("Bus model not supported on OldWorld Mac machine\n");
J
j_mayer 已提交
323 324 325 326 327
        }
    }

    /* init basic PC hardware */
    if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) {
P
Paul Brook 已提交
328
        hw_error("Only 6xx bus is supported on heathrow machine\n");
J
j_mayer 已提交
329 330 331
    }
    pic = heathrow_pic_init(&pic_mem_index, 1, heathrow_irqs);
    pci_bus = pci_grackle_init(0xfec00000, pic);
P
Paul Brook 已提交
332
    pci_vga_init(pci_bus, vga_bios_offset, vga_bios_size);
333

A
aurel32 已提交
334
    escc_mem_index = escc_init(0x80013000, pic[0x0f], pic[0x10], serial_hds[0],
B
blueswir1 已提交
335
                               serial_hds[1], ESCC_CLOCK, 4);
336

337
    for(i = 0; i < nb_nics; i++)
338
        pci_nic_init_nofail(&nd_table[i], "ne2k_pci", NULL);
J
j_mayer 已提交
339

T
ths 已提交
340 341 342 343 344

    if (drive_get_max_bus(IF_IDE) >= MAX_IDE_BUS) {
        fprintf(stderr, "qemu: too many IDE bus\n");
        exit(1);
    }
345 346

    /* First IDE channel is a MAC IDE on the MacIO bus */
347 348
    hd[0] = drive_get(IF_IDE, 0, 0);
    hd[1] = drive_get(IF_IDE, 0, 1);
349 350 351
    dbdma = DBDMA_init(&dbdma_mem_index);
    ide_mem_index[0] = -1;
    ide_mem_index[1] = pmac_ide_init(hd, pic[0x0D], dbdma, 0x16, pic[0x02]);
T
ths 已提交
352

353
    /* Second IDE channel is a CMD646 on the PCI bus */
354 355
    hd[0] = drive_get(IF_IDE, 1, 0);
    hd[1] = drive_get(IF_IDE, 1, 1);
356 357
    hd[3] = hd[2] = NULL;
    pci_cmd646_ide_init(pci_bus, hd, 0);
J
j_mayer 已提交
358 359 360 361 362 363

    /* cuda also initialize ADB */
    cuda_init(&cuda_mem_index, pic[0x12]);

    adb_kbd_init(&adb_bus);
    adb_mouse_init(&adb_bus);
364

B
blueswir1 已提交
365
    nvr = macio_nvram_init(&nvram_mem_index, 0x2000, 4);
J
j_mayer 已提交
366 367
    pmac_format_nvram_partition(nvr, 0x2000);

368 369 370
    macio_init(pci_bus, PCI_DEVICE_ID_APPLE_343S1201, 1, pic_mem_index,
               dbdma_mem_index, cuda_mem_index, nvr, 2, ide_mem_index,
               escc_mem_index);
J
j_mayer 已提交
371 372

    if (usb_enabled) {
B
Blue Swirl 已提交
373
        usb_ohci_init_pci(pci_bus, -1, 1);
J
j_mayer 已提交
374 375 376 377 378 379 380
    }

    if (graphic_depth != 15 && graphic_depth != 32 && graphic_depth != 8)
        graphic_depth = 15;

    /* No PCI init: the BIOS will do it */

B
blueswir1 已提交
381 382 383 384
    fw_cfg = fw_cfg_init(0, 0, CFG_ADDR, CFG_ADDR + 2);
    fw_cfg_add_i32(fw_cfg, FW_CFG_ID, 1);
    fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size);
    fw_cfg_add_i16(fw_cfg, FW_CFG_MACHINE_ID, ARCH_HEATHROW);
385 386 387 388
    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, kernel_base);
    fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, kernel_size);
    if (kernel_cmdline) {
        fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, CMDLINE_ADDR);
389
        pstrcpy_targphys("cmdline", CMDLINE_ADDR, TARGET_PAGE_SIZE, kernel_cmdline);
390 391 392 393 394 395
    } else {
        fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_CMDLINE, 0);
    }
    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_base);
    fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size);
    fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, ppc_boot_device);
396 397 398 399 400

    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_WIDTH, graphic_width);
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_HEIGHT, graphic_height);
    fw_cfg_add_i16(fw_cfg, FW_CFG_PPC_DEPTH, graphic_depth);

401 402 403 404 405 406 407 408
    if (kvm_enabled()) {
#ifdef CONFIG_KVM
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, kvmppc_get_tbfreq());
#endif
    } else {
        fw_cfg_add_i32(fw_cfg, FW_CFG_PPC_TBFREQ, get_ticks_per_sec());
    }

409
    qemu_register_boot_set(fw_cfg_boot_set, fw_cfg);
J
j_mayer 已提交
410 411
}

412
static QEMUMachine heathrow_machine = {
413
    .name = "g3beige",
414 415
    .desc = "Heathrow based PowerMAC",
    .init = ppc_heathrow_init,
B
balrog 已提交
416
    .max_cpus = MAX_CPUS,
417
#ifndef TARGET_PPC64
418
    .is_default = 1,
419
#endif
J
j_mayer 已提交
420
};
421 422 423 424 425 426 427

static void heathrow_machine_init(void)
{
    qemu_register_machine(&heathrow_machine);
}

machine_init(heathrow_machine_init);