mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-10 02:54:58 -06:00
hw: move boards and other isolated files to hw/ARCH
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
e4c8b28cde
commit
530182169e
85 changed files with 72 additions and 62 deletions
|
@ -1,19 +1,14 @@
|
|||
# shared objects
|
||||
obj-y = ppc.o ppc_booke.o
|
||||
# PREP target
|
||||
obj-y += mc146818rtc.o
|
||||
# IBM pSeries (sPAPR)
|
||||
obj-$(CONFIG_PSERIES) += spapr.o spapr_hcall.o spapr_rtas.o spapr_vio.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_hcall.o spapr_rtas.o spapr_vio.o
|
||||
obj-$(CONFIG_PSERIES) += xics.o spapr_vty.o spapr_llan.o spapr_vscsi.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_pci.o pci/pci-hotplug.o spapr_iommu.o
|
||||
obj-$(CONFIG_PSERIES) += spapr_events.o spapr_nvram.o
|
||||
# PowerPC 4xx boards
|
||||
obj-y += ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
|
||||
obj-y += ppc440_bamboo.o
|
||||
obj-y += ppc4xx_devs.o ppc4xx_pci.o
|
||||
# PowerPC E500 boards
|
||||
obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
obj-y += virtex_ml507.o
|
||||
# PowerPC OpenPIC
|
||||
obj-y += openpic.o
|
||||
|
||||
|
@ -22,6 +17,12 @@ obj-y += xilinx_ethlite.o
|
|||
|
||||
obj-y := $(addprefix ../,$(obj-y))
|
||||
|
||||
# shared objects
|
||||
obj-y += ppc.o ppc_booke.o
|
||||
# IBM pSeries (sPAPR)
|
||||
obj-$(CONFIG_PSERIES) += spapr.o
|
||||
# PowerPC 4xx boards
|
||||
obj-y += ppc405_boards.o ppc405_uc.o ppc440_bamboo.o
|
||||
# PReP
|
||||
obj-y += prep.o
|
||||
# OldWorld PowerMac
|
||||
|
@ -30,3 +31,5 @@ obj-y += mac_oldworld.o
|
|||
obj-y += mac_newworld.o
|
||||
# e500
|
||||
obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o
|
||||
# PowerPC 440 Xilinx ML507 reference board.
|
||||
obj-y += virtex_ml507.o
|
||||
|
|
1356
hw/ppc/ppc.c
Normal file
1356
hw/ppc/ppc.c
Normal file
File diff suppressed because it is too large
Load diff
662
hw/ppc/ppc405_boards.c
Normal file
662
hw/ppc/ppc405_boards.c
Normal file
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* QEMU PowerPC 405 evaluation boards emulation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ppc.h"
|
||||
#include "hw/ppc405.h"
|
||||
#include "hw/nvram.h"
|
||||
#include "hw/flash.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "block/block.h"
|
||||
#include "hw/boards.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/loader.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
#define BIOS_FILENAME "ppc405_rom.bin"
|
||||
#define BIOS_SIZE (2048 * 1024)
|
||||
|
||||
#define KERNEL_LOAD_ADDR 0x00000000
|
||||
#define INITRD_LOAD_ADDR 0x01800000
|
||||
|
||||
#define USE_FLASH_BIOS
|
||||
|
||||
#define DEBUG_BOARD_INIT
|
||||
|
||||
/*****************************************************************************/
|
||||
/* PPC405EP reference board (IBM) */
|
||||
/* Standalone board with:
|
||||
* - PowerPC 405EP CPU
|
||||
* - SDRAM (0x00000000)
|
||||
* - Flash (0xFFF80000)
|
||||
* - SRAM (0xFFF00000)
|
||||
* - NVRAM (0xF0000000)
|
||||
* - FPGA (0xF0300000)
|
||||
*/
|
||||
typedef struct ref405ep_fpga_t ref405ep_fpga_t;
|
||||
struct ref405ep_fpga_t {
|
||||
uint8_t reg0;
|
||||
uint8_t reg1;
|
||||
};
|
||||
|
||||
static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
uint32_t ret;
|
||||
|
||||
fpga = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
ret = fpga->reg0;
|
||||
break;
|
||||
case 0x1:
|
||||
ret = fpga->reg1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_writeb (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
|
||||
fpga = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
/* Read only */
|
||||
break;
|
||||
case 0x1:
|
||||
fpga->reg1 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t ref405ep_fpga_readw (void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = ref405ep_fpga_readb(opaque, addr) << 8;
|
||||
ret |= ref405ep_fpga_readb(opaque, addr + 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_writew (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
ref405ep_fpga_writeb(opaque, addr, (value >> 8) & 0xFF);
|
||||
ref405ep_fpga_writeb(opaque, addr + 1, value & 0xFF);
|
||||
}
|
||||
|
||||
static uint32_t ref405ep_fpga_readl (void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = ref405ep_fpga_readb(opaque, addr) << 24;
|
||||
ret |= ref405ep_fpga_readb(opaque, addr + 1) << 16;
|
||||
ret |= ref405ep_fpga_readb(opaque, addr + 2) << 8;
|
||||
ret |= ref405ep_fpga_readb(opaque, addr + 3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_writel (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
ref405ep_fpga_writeb(opaque, addr, (value >> 24) & 0xFF);
|
||||
ref405ep_fpga_writeb(opaque, addr + 1, (value >> 16) & 0xFF);
|
||||
ref405ep_fpga_writeb(opaque, addr + 2, (value >> 8) & 0xFF);
|
||||
ref405ep_fpga_writeb(opaque, addr + 3, value & 0xFF);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps ref405ep_fpga_ops = {
|
||||
.old_mmio = {
|
||||
.read = {
|
||||
ref405ep_fpga_readb, ref405ep_fpga_readw, ref405ep_fpga_readl,
|
||||
},
|
||||
.write = {
|
||||
ref405ep_fpga_writeb, ref405ep_fpga_writew, ref405ep_fpga_writel,
|
||||
},
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void ref405ep_fpga_reset (void *opaque)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
|
||||
fpga = opaque;
|
||||
fpga->reg0 = 0x00;
|
||||
fpga->reg1 = 0x0F;
|
||||
}
|
||||
|
||||
static void ref405ep_fpga_init(MemoryRegion *sysmem, uint32_t base)
|
||||
{
|
||||
ref405ep_fpga_t *fpga;
|
||||
MemoryRegion *fpga_memory = g_new(MemoryRegion, 1);
|
||||
|
||||
fpga = g_malloc0(sizeof(ref405ep_fpga_t));
|
||||
memory_region_init_io(fpga_memory, &ref405ep_fpga_ops, fpga,
|
||||
"fpga", 0x00000100);
|
||||
memory_region_add_subregion(sysmem, base, fpga_memory);
|
||||
qemu_register_reset(&ref405ep_fpga_reset, fpga);
|
||||
}
|
||||
|
||||
static void ref405ep_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
ram_addr_t ram_size = args->ram_size;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
char *filename;
|
||||
ppc4xx_bd_info_t bd;
|
||||
CPUPPCState *env;
|
||||
qemu_irq *pic;
|
||||
MemoryRegion *bios;
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
ram_addr_t bdloc;
|
||||
MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories));
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
target_ulong sram_size;
|
||||
long bios_size;
|
||||
//int phy_addr = 0;
|
||||
//static int phy_addr = 1;
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
int linux_boot;
|
||||
int fl_idx, fl_sectors, len;
|
||||
DriveInfo *dinfo;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
|
||||
/* XXX: fix this */
|
||||
memory_region_init_ram(&ram_memories[0], "ef405ep.ram", 0x08000000);
|
||||
vmstate_register_ram_global(&ram_memories[0]);
|
||||
ram_bases[0] = 0;
|
||||
ram_sizes[0] = 0x08000000;
|
||||
memory_region_init(&ram_memories[1], "ef405ep.ram1", 0);
|
||||
ram_bases[1] = 0x00000000;
|
||||
ram_sizes[1] = 0x00000000;
|
||||
ram_size = 128 * 1024 * 1024;
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register cpu\n", __func__);
|
||||
#endif
|
||||
env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &pic, kernel_filename == NULL ? 0 : 1);
|
||||
/* allocate SRAM */
|
||||
sram_size = 512 * 1024;
|
||||
memory_region_init_ram(sram, "ef405ep.sram", sram_size);
|
||||
vmstate_register_ram_global(sram);
|
||||
memory_region_add_subregion(sysmem, 0xFFF00000, sram);
|
||||
/* allocate and load BIOS */
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register BIOS\n", __func__);
|
||||
#endif
|
||||
fl_idx = 0;
|
||||
#ifdef USE_FLASH_BIOS
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
if (dinfo) {
|
||||
bios_size = bdrv_getlength(dinfo->bdrv);
|
||||
fl_sectors = (bios_size + 65535) >> 16;
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("Register parallel flash %d size %lx"
|
||||
" at addr %lx '%s' %d\n",
|
||||
fl_idx, bios_size, -bios_size,
|
||||
bdrv_get_device_name(dinfo->bdrv), fl_sectors);
|
||||
#endif
|
||||
pflash_cfi02_register((uint32_t)(-bios_size),
|
||||
NULL, "ef405ep.bios", bios_size,
|
||||
dinfo->bdrv, 65536, fl_sectors, 1,
|
||||
2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
fl_idx++;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("Load BIOS from file\n");
|
||||
#endif
|
||||
bios = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(bios, "ef405ep.bios", BIOS_SIZE);
|
||||
vmstate_register_ram_global(bios);
|
||||
if (bios_name == NULL)
|
||||
bios_name = BIOS_FILENAME;
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (filename) {
|
||||
bios_size = load_image(filename, memory_region_get_ram_ptr(bios));
|
||||
g_free(filename);
|
||||
} else {
|
||||
bios_size = -1;
|
||||
}
|
||||
if (bios_size < 0 || bios_size > BIOS_SIZE) {
|
||||
fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n",
|
||||
bios_name);
|
||||
exit(1);
|
||||
}
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_set_readonly(bios, true);
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
}
|
||||
/* Register FPGA */
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register FPGA\n", __func__);
|
||||
#endif
|
||||
ref405ep_fpga_init(sysmem, 0xF0300000);
|
||||
/* Register NVRAM */
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register NVRAM\n", __func__);
|
||||
#endif
|
||||
m48t59_init(NULL, 0xF0000000, 0, 8192, 8);
|
||||
/* Load kernel */
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
if (linux_boot) {
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: load kernel\n", __func__);
|
||||
#endif
|
||||
memset(&bd, 0, sizeof(bd));
|
||||
bd.bi_memstart = 0x00000000;
|
||||
bd.bi_memsize = ram_size;
|
||||
bd.bi_flashstart = -bios_size;
|
||||
bd.bi_flashsize = -bios_size;
|
||||
bd.bi_flashoffset = 0;
|
||||
bd.bi_sramstart = 0xFFF00000;
|
||||
bd.bi_sramsize = sram_size;
|
||||
bd.bi_bootflags = 0;
|
||||
bd.bi_intfreq = 133333333;
|
||||
bd.bi_busfreq = 33333333;
|
||||
bd.bi_baudrate = 115200;
|
||||
bd.bi_s_version[0] = 'Q';
|
||||
bd.bi_s_version[1] = 'M';
|
||||
bd.bi_s_version[2] = 'U';
|
||||
bd.bi_s_version[3] = '\0';
|
||||
bd.bi_r_version[0] = 'Q';
|
||||
bd.bi_r_version[1] = 'E';
|
||||
bd.bi_r_version[2] = 'M';
|
||||
bd.bi_r_version[3] = 'U';
|
||||
bd.bi_r_version[4] = '\0';
|
||||
bd.bi_procfreq = 133333333;
|
||||
bd.bi_plb_busfreq = 33333333;
|
||||
bd.bi_pci_busfreq = 33333333;
|
||||
bd.bi_opbfreq = 33333333;
|
||||
bdloc = ppc405_set_bootinfo(env, &bd, 0x00000001);
|
||||
env->gpr[3] = bdloc;
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
printf("Load kernel size %ld at " TARGET_FMT_lx,
|
||||
kernel_size, kernel_base);
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
env->gpr[4] = initrd_base;
|
||||
env->gpr[5] = initrd_size;
|
||||
if (kernel_cmdline != NULL) {
|
||||
len = strlen(kernel_cmdline);
|
||||
bdloc -= ((len + 255) & ~255);
|
||||
cpu_physical_memory_write(bdloc, (void *)kernel_cmdline, len + 1);
|
||||
env->gpr[6] = bdloc;
|
||||
env->gpr[7] = bdloc + len;
|
||||
} else {
|
||||
env->gpr[6] = 0;
|
||||
env->gpr[7] = 0;
|
||||
}
|
||||
env->nip = KERNEL_LOAD_ADDR;
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
bdloc = 0;
|
||||
}
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: Done\n", __func__);
|
||||
#endif
|
||||
printf("bdloc " RAM_ADDR_FMT "\n", bdloc);
|
||||
}
|
||||
|
||||
static QEMUMachine ref405ep_machine = {
|
||||
.name = "ref405ep",
|
||||
.desc = "ref405ep",
|
||||
.init = ref405ep_init,
|
||||
DEFAULT_MACHINE_OPTIONS,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* AMCC Taihu evaluation board */
|
||||
/* - PowerPC 405EP processor
|
||||
* - SDRAM 128 MB at 0x00000000
|
||||
* - Boot flash 2 MB at 0xFFE00000
|
||||
* - Application flash 32 MB at 0xFC000000
|
||||
* - 2 serial ports
|
||||
* - 2 ethernet PHY
|
||||
* - 1 USB 1.1 device 0x50000000
|
||||
* - 1 LCD display 0x50100000
|
||||
* - 1 CPLD 0x50100000
|
||||
* - 1 I2C EEPROM
|
||||
* - 1 I2C thermal sensor
|
||||
* - a set of LEDs
|
||||
* - bit-bang SPI port using GPIOs
|
||||
* - 1 EBC interface connector 0 0x50200000
|
||||
* - 1 cardbus controller + expansion slot.
|
||||
* - 1 PCI expansion slot.
|
||||
*/
|
||||
typedef struct taihu_cpld_t taihu_cpld_t;
|
||||
struct taihu_cpld_t {
|
||||
uint8_t reg0;
|
||||
uint8_t reg1;
|
||||
};
|
||||
|
||||
static uint32_t taihu_cpld_readb (void *opaque, hwaddr addr)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
uint32_t ret;
|
||||
|
||||
cpld = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
ret = cpld->reg0;
|
||||
break;
|
||||
case 0x1:
|
||||
ret = cpld->reg1;
|
||||
break;
|
||||
default:
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void taihu_cpld_writeb (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
|
||||
cpld = opaque;
|
||||
switch (addr) {
|
||||
case 0x0:
|
||||
/* Read only */
|
||||
break;
|
||||
case 0x1:
|
||||
cpld->reg1 = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t taihu_cpld_readw (void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = taihu_cpld_readb(opaque, addr) << 8;
|
||||
ret |= taihu_cpld_readb(opaque, addr + 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void taihu_cpld_writew (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
taihu_cpld_writeb(opaque, addr, (value >> 8) & 0xFF);
|
||||
taihu_cpld_writeb(opaque, addr + 1, value & 0xFF);
|
||||
}
|
||||
|
||||
static uint32_t taihu_cpld_readl (void *opaque, hwaddr addr)
|
||||
{
|
||||
uint32_t ret;
|
||||
|
||||
ret = taihu_cpld_readb(opaque, addr) << 24;
|
||||
ret |= taihu_cpld_readb(opaque, addr + 1) << 16;
|
||||
ret |= taihu_cpld_readb(opaque, addr + 2) << 8;
|
||||
ret |= taihu_cpld_readb(opaque, addr + 3);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void taihu_cpld_writel (void *opaque,
|
||||
hwaddr addr, uint32_t value)
|
||||
{
|
||||
taihu_cpld_writel(opaque, addr, (value >> 24) & 0xFF);
|
||||
taihu_cpld_writel(opaque, addr + 1, (value >> 16) & 0xFF);
|
||||
taihu_cpld_writel(opaque, addr + 2, (value >> 8) & 0xFF);
|
||||
taihu_cpld_writeb(opaque, addr + 3, value & 0xFF);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps taihu_cpld_ops = {
|
||||
.old_mmio = {
|
||||
.read = { taihu_cpld_readb, taihu_cpld_readw, taihu_cpld_readl, },
|
||||
.write = { taihu_cpld_writeb, taihu_cpld_writew, taihu_cpld_writel, },
|
||||
},
|
||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||
};
|
||||
|
||||
static void taihu_cpld_reset (void *opaque)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
|
||||
cpld = opaque;
|
||||
cpld->reg0 = 0x01;
|
||||
cpld->reg1 = 0x80;
|
||||
}
|
||||
|
||||
static void taihu_cpld_init(MemoryRegion *sysmem, uint32_t base)
|
||||
{
|
||||
taihu_cpld_t *cpld;
|
||||
MemoryRegion *cpld_memory = g_new(MemoryRegion, 1);
|
||||
|
||||
cpld = g_malloc0(sizeof(taihu_cpld_t));
|
||||
memory_region_init_io(cpld_memory, &taihu_cpld_ops, cpld, "cpld", 0x100);
|
||||
memory_region_add_subregion(sysmem, base, cpld_memory);
|
||||
qemu_register_reset(&taihu_cpld_reset, cpld);
|
||||
}
|
||||
|
||||
static void taihu_405ep_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
ram_addr_t ram_size = args->ram_size;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
char *filename;
|
||||
qemu_irq *pic;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *bios;
|
||||
MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories));
|
||||
hwaddr ram_bases[2], ram_sizes[2];
|
||||
long bios_size;
|
||||
target_ulong kernel_base, initrd_base;
|
||||
long kernel_size, initrd_size;
|
||||
int linux_boot;
|
||||
int fl_idx, fl_sectors;
|
||||
DriveInfo *dinfo;
|
||||
|
||||
/* RAM is soldered to the board so the size cannot be changed */
|
||||
memory_region_init_ram(&ram_memories[0],
|
||||
"taihu_405ep.ram-0", 0x04000000);
|
||||
vmstate_register_ram_global(&ram_memories[0]);
|
||||
ram_bases[0] = 0;
|
||||
ram_sizes[0] = 0x04000000;
|
||||
memory_region_init_ram(&ram_memories[1],
|
||||
"taihu_405ep.ram-1", 0x04000000);
|
||||
vmstate_register_ram_global(&ram_memories[1]);
|
||||
ram_bases[1] = 0x04000000;
|
||||
ram_sizes[1] = 0x04000000;
|
||||
ram_size = 0x08000000;
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register cpu\n", __func__);
|
||||
#endif
|
||||
ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes,
|
||||
33333333, &pic, kernel_filename == NULL ? 0 : 1);
|
||||
/* allocate and load BIOS */
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register BIOS\n", __func__);
|
||||
#endif
|
||||
fl_idx = 0;
|
||||
#if defined(USE_FLASH_BIOS)
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
if (dinfo) {
|
||||
bios_size = bdrv_getlength(dinfo->bdrv);
|
||||
/* XXX: should check that size is 2MB */
|
||||
// bios_size = 2 * 1024 * 1024;
|
||||
fl_sectors = (bios_size + 65535) >> 16;
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("Register parallel flash %d size %lx"
|
||||
" at addr %lx '%s' %d\n",
|
||||
fl_idx, bios_size, -bios_size,
|
||||
bdrv_get_device_name(dinfo->bdrv), fl_sectors);
|
||||
#endif
|
||||
pflash_cfi02_register((uint32_t)(-bios_size),
|
||||
NULL, "taihu_405ep.bios", bios_size,
|
||||
dinfo->bdrv, 65536, fl_sectors, 1,
|
||||
4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
fl_idx++;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("Load BIOS from file\n");
|
||||
#endif
|
||||
if (bios_name == NULL)
|
||||
bios_name = BIOS_FILENAME;
|
||||
bios = g_new(MemoryRegion, 1);
|
||||
memory_region_init_ram(bios, "taihu_405ep.bios", BIOS_SIZE);
|
||||
vmstate_register_ram_global(bios);
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
||||
if (filename) {
|
||||
bios_size = load_image(filename, memory_region_get_ram_ptr(bios));
|
||||
g_free(filename);
|
||||
} else {
|
||||
bios_size = -1;
|
||||
}
|
||||
if (bios_size < 0 || bios_size > BIOS_SIZE) {
|
||||
fprintf(stderr, "qemu: could not load PowerPC bios '%s'\n",
|
||||
bios_name);
|
||||
exit(1);
|
||||
}
|
||||
bios_size = (bios_size + 0xfff) & ~0xfff;
|
||||
memory_region_set_readonly(bios, true);
|
||||
memory_region_add_subregion(sysmem, (uint32_t)(-bios_size), bios);
|
||||
}
|
||||
/* Register Linux flash */
|
||||
dinfo = drive_get(IF_PFLASH, 0, fl_idx);
|
||||
if (dinfo) {
|
||||
bios_size = bdrv_getlength(dinfo->bdrv);
|
||||
/* XXX: should check that size is 32MB */
|
||||
bios_size = 32 * 1024 * 1024;
|
||||
fl_sectors = (bios_size + 65535) >> 16;
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("Register parallel flash %d size %lx"
|
||||
" at addr " TARGET_FMT_lx " '%s'\n",
|
||||
fl_idx, bios_size, (target_ulong)0xfc000000,
|
||||
bdrv_get_device_name(dinfo->bdrv));
|
||||
#endif
|
||||
pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size,
|
||||
dinfo->bdrv, 65536, fl_sectors, 1,
|
||||
4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA,
|
||||
1);
|
||||
fl_idx++;
|
||||
}
|
||||
/* Register CLPD & LCD display */
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: register CPLD\n", __func__);
|
||||
#endif
|
||||
taihu_cpld_init(sysmem, 0x50100000);
|
||||
/* Load kernel */
|
||||
linux_boot = (kernel_filename != NULL);
|
||||
if (linux_boot) {
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: load kernel\n", __func__);
|
||||
#endif
|
||||
kernel_base = KERNEL_LOAD_ADDR;
|
||||
/* now we can load the kernel */
|
||||
kernel_size = load_image_targphys(kernel_filename, kernel_base,
|
||||
ram_size - kernel_base);
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
initrd_base = INITRD_LOAD_ADDR;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
ram_size - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr,
|
||||
"qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
} else {
|
||||
kernel_base = 0;
|
||||
kernel_size = 0;
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
#ifdef DEBUG_BOARD_INIT
|
||||
printf("%s: Done\n", __func__);
|
||||
#endif
|
||||
}
|
||||
|
||||
static QEMUMachine taihu_machine = {
|
||||
.name = "taihu",
|
||||
.desc = "taihu",
|
||||
.init = taihu_405ep_init,
|
||||
DEFAULT_MACHINE_OPTIONS,
|
||||
};
|
||||
|
||||
static void ppc405_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&ref405ep_machine);
|
||||
qemu_register_machine(&taihu_machine);
|
||||
}
|
||||
|
||||
machine_init(ppc405_machine_init);
|
2548
hw/ppc/ppc405_uc.c
Normal file
2548
hw/ppc/ppc405_uc.c
Normal file
File diff suppressed because it is too large
Load diff
306
hw/ppc/ppc440_bamboo.c
Normal file
306
hw/ppc/ppc440_bamboo.c
Normal file
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* QEMU PowerPC 440 Bamboo board emulation
|
||||
*
|
||||
* Copyright 2007 IBM Corporation.
|
||||
* Authors:
|
||||
* Jerone Young <jyoung5@us.ibm.com>
|
||||
* Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
|
||||
* Hollis Blanchard <hollisb@us.ibm.com>
|
||||
*
|
||||
* This work is licensed under the GNU GPL license version 2 or later.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "qemu-common.h"
|
||||
#include "net/net.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/loader.h"
|
||||
#include "elf.h"
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/serial.h"
|
||||
#include "hw/ppc.h"
|
||||
#include "hw/ppc405.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/sysbus.h"
|
||||
|
||||
#define BINARY_DEVICE_TREE_FILE "bamboo.dtb"
|
||||
|
||||
/* from u-boot */
|
||||
#define KERNEL_ADDR 0x1000000
|
||||
#define FDT_ADDR 0x1800000
|
||||
#define RAMDISK_ADDR 0x1900000
|
||||
|
||||
#define PPC440EP_PCI_CONFIG 0xeec00000
|
||||
#define PPC440EP_PCI_INTACK 0xeed00000
|
||||
#define PPC440EP_PCI_SPECIAL 0xeed00000
|
||||
#define PPC440EP_PCI_REGS 0xef400000
|
||||
#define PPC440EP_PCI_IO 0xe8000000
|
||||
#define PPC440EP_PCI_IOLEN 0x00010000
|
||||
|
||||
#define PPC440EP_SDRAM_NR_BANKS 4
|
||||
|
||||
static const unsigned int ppc440ep_sdram_bank_sizes[] = {
|
||||
256<<20, 128<<20, 64<<20, 32<<20, 16<<20, 8<<20, 0
|
||||
};
|
||||
|
||||
static hwaddr entry;
|
||||
|
||||
static int bamboo_load_device_tree(hwaddr addr,
|
||||
uint32_t ramsize,
|
||||
hwaddr initrd_base,
|
||||
hwaddr initrd_size,
|
||||
const char *kernel_cmdline)
|
||||
{
|
||||
int ret = -1;
|
||||
#ifdef CONFIG_FDT
|
||||
uint32_t mem_reg_property[] = { 0, 0, cpu_to_be32(ramsize) };
|
||||
char *filename;
|
||||
int fdt_size;
|
||||
void *fdt;
|
||||
uint32_t tb_freq = 400000000;
|
||||
uint32_t clock_freq = 400000000;
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
|
||||
if (!filename) {
|
||||
goto out;
|
||||
}
|
||||
fdt = load_device_tree(filename, &fdt_size);
|
||||
g_free(filename);
|
||||
if (fdt == NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Manipulate device tree in memory. */
|
||||
|
||||
ret = qemu_devtree_setprop(fdt, "/memory", "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property));
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "couldn't set /memory/reg\n");
|
||||
|
||||
ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
||||
initrd_base);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
||||
|
||||
ret = qemu_devtree_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
||||
(initrd_base + initrd_size));
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
||||
|
||||
ret = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs",
|
||||
kernel_cmdline);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
|
||||
/* Copy data from the host device tree into the guest. Since the guest can
|
||||
* directly access the timebase without host involvement, we must expose
|
||||
* the correct frequencies. */
|
||||
if (kvm_enabled()) {
|
||||
tb_freq = kvmppc_get_tbfreq();
|
||||
clock_freq = kvmppc_get_clockfreq();
|
||||
}
|
||||
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "clock-frequency",
|
||||
clock_freq);
|
||||
qemu_devtree_setprop_cell(fdt, "/cpus/cpu@0", "timebase-frequency",
|
||||
tb_freq);
|
||||
|
||||
ret = rom_add_blob_fixed(BINARY_DEVICE_TREE_FILE, fdt, fdt_size, addr);
|
||||
g_free(fdt);
|
||||
|
||||
out:
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Create reset TLB entries for BookE, spanning the 32bit addr space. */
|
||||
static void mmubooke_create_initial_mapping(CPUPPCState *env,
|
||||
target_ulong va,
|
||||
hwaddr pa)
|
||||
{
|
||||
ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
|
||||
|
||||
tlb->attr = 0;
|
||||
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
||||
tlb->size = 1 << 31; /* up to 0x80000000 */
|
||||
tlb->EPN = va & TARGET_PAGE_MASK;
|
||||
tlb->RPN = pa & TARGET_PAGE_MASK;
|
||||
tlb->PID = 0;
|
||||
|
||||
tlb = &env->tlb.tlbe[1];
|
||||
tlb->attr = 0;
|
||||
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
||||
tlb->size = 1 << 31; /* up to 0xffffffff */
|
||||
tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
|
||||
tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
|
||||
tlb->PID = 0;
|
||||
}
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
env->gpr[1] = (16<<20) - 8;
|
||||
env->gpr[3] = FDT_ADDR;
|
||||
env->nip = entry;
|
||||
|
||||
/* Create a mapping for the kernel. */
|
||||
mmubooke_create_initial_mapping(env, 0, 0);
|
||||
}
|
||||
|
||||
static void bamboo_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
ram_addr_t ram_size = args->ram_size;
|
||||
const char *cpu_model = args->cpu_model;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 };
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
MemoryRegion *ram_memories
|
||||
= g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories));
|
||||
hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS];
|
||||
hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS];
|
||||
qemu_irq *pic;
|
||||
qemu_irq *irqs;
|
||||
PCIBus *pcibus;
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
uint64_t elf_entry;
|
||||
uint64_t elf_lowaddr;
|
||||
hwaddr loadaddr = 0;
|
||||
target_long initrd_size = 0;
|
||||
DeviceState *dev;
|
||||
int success;
|
||||
int i;
|
||||
|
||||
/* Setup CPU. */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = "440EP";
|
||||
}
|
||||
cpu = cpu_ppc_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to initialize CPU!\n");
|
||||
exit(1);
|
||||
}
|
||||
env = &cpu->env;
|
||||
|
||||
qemu_register_reset(main_cpu_reset, cpu);
|
||||
ppc_booke_timers_init(cpu, 400000000, 0);
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
|
||||
/* interrupt controller */
|
||||
irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
|
||||
irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
|
||||
irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
|
||||
pic = ppcuic_init(env, irqs, 0x0C0, 0, 1);
|
||||
|
||||
/* SDRAM controller */
|
||||
memset(ram_bases, 0, sizeof(ram_bases));
|
||||
memset(ram_sizes, 0, sizeof(ram_sizes));
|
||||
ram_size = ppc4xx_sdram_adjust(ram_size, PPC440EP_SDRAM_NR_BANKS,
|
||||
ram_memories,
|
||||
ram_bases, ram_sizes,
|
||||
ppc440ep_sdram_bank_sizes);
|
||||
/* XXX 440EP's ECC interrupts are on UIC1, but we've only created UIC0. */
|
||||
ppc4xx_sdram_init(env, pic[14], PPC440EP_SDRAM_NR_BANKS, ram_memories,
|
||||
ram_bases, ram_sizes, 1);
|
||||
|
||||
/* PCI */
|
||||
dev = sysbus_create_varargs(TYPE_PPC4xx_PCI_HOST_BRIDGE,
|
||||
PPC440EP_PCI_CONFIG,
|
||||
pic[pci_irq_nrs[0]], pic[pci_irq_nrs[1]],
|
||||
pic[pci_irq_nrs[2]], pic[pci_irq_nrs[3]],
|
||||
NULL);
|
||||
pcibus = (PCIBus *)qdev_get_child_bus(dev, "pci.0");
|
||||
if (!pcibus) {
|
||||
fprintf(stderr, "couldn't create PCI controller!\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
isa_mmio_init(PPC440EP_PCI_IO, PPC440EP_PCI_IOLEN);
|
||||
|
||||
if (serial_hds[0] != NULL) {
|
||||
serial_mm_init(address_space_mem, 0xef600300, 0, pic[0],
|
||||
PPC_SERIAL_MM_BAUDBASE, serial_hds[0],
|
||||
DEVICE_BIG_ENDIAN);
|
||||
}
|
||||
if (serial_hds[1] != NULL) {
|
||||
serial_mm_init(address_space_mem, 0xef600400, 0, pic[1],
|
||||
PPC_SERIAL_MM_BAUDBASE, serial_hds[1],
|
||||
DEVICE_BIG_ENDIAN);
|
||||
}
|
||||
|
||||
if (pcibus) {
|
||||
/* Register network interfaces. */
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
/* There are no PCI NICs on the Bamboo board, but there are
|
||||
* PCI slots, so we can pick whatever default model we want. */
|
||||
pci_nic_init_nofail(&nd_table[i], "e1000", NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Load kernel. */
|
||||
if (kernel_filename) {
|
||||
success = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
|
||||
if (success < 0) {
|
||||
success = load_elf(kernel_filename, NULL, NULL, &elf_entry,
|
||||
&elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
|
||||
entry = elf_entry;
|
||||
loadaddr = elf_lowaddr;
|
||||
}
|
||||
/* XXX try again as binary */
|
||||
if (success < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Load initrd. */
|
||||
if (initrd_filename) {
|
||||
initrd_size = load_image_targphys(initrd_filename, RAMDISK_ADDR,
|
||||
ram_size - RAMDISK_ADDR);
|
||||
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load ram disk '%s' at %x\n",
|
||||
initrd_filename, RAMDISK_ADDR);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we're loading a kernel directly, we must load the device tree too. */
|
||||
if (kernel_filename) {
|
||||
if (bamboo_load_device_tree(FDT_ADDR, ram_size, RAMDISK_ADDR,
|
||||
initrd_size, kernel_cmdline) < 0) {
|
||||
fprintf(stderr, "couldn't load device tree\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (kvm_enabled())
|
||||
kvmppc_init();
|
||||
}
|
||||
|
||||
static QEMUMachine bamboo_machine = {
|
||||
.name = "bamboo",
|
||||
.desc = "bamboo",
|
||||
.init = bamboo_init,
|
||||
DEFAULT_MACHINE_OPTIONS,
|
||||
};
|
||||
|
||||
static void bamboo_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&bamboo_machine);
|
||||
}
|
||||
|
||||
machine_init(bamboo_machine_init);
|
273
hw/ppc/ppc_booke.c
Normal file
273
hw/ppc/ppc_booke.c
Normal file
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* QEMU PowerPC Booke hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2011 AdaCore
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/ppc.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/nvram.h"
|
||||
#include "qemu/log.h"
|
||||
#include "hw/loader.h"
|
||||
|
||||
|
||||
/* Timer Control Register */
|
||||
|
||||
#define TCR_WP_SHIFT 30 /* Watchdog Timer Period */
|
||||
#define TCR_WP_MASK (0x3 << TCR_WP_SHIFT)
|
||||
#define TCR_WRC_SHIFT 28 /* Watchdog Timer Reset Control */
|
||||
#define TCR_WRC_MASK (0x3 << TCR_WRC_SHIFT)
|
||||
#define TCR_WIE (1 << 27) /* Watchdog Timer Interrupt Enable */
|
||||
#define TCR_DIE (1 << 26) /* Decrementer Interrupt Enable */
|
||||
#define TCR_FP_SHIFT 24 /* Fixed-Interval Timer Period */
|
||||
#define TCR_FP_MASK (0x3 << TCR_FP_SHIFT)
|
||||
#define TCR_FIE (1 << 23) /* Fixed-Interval Timer Interrupt Enable */
|
||||
#define TCR_ARE (1 << 22) /* Auto-Reload Enable */
|
||||
|
||||
/* Timer Control Register (e500 specific fields) */
|
||||
|
||||
#define TCR_E500_FPEXT_SHIFT 13 /* Fixed-Interval Timer Period Extension */
|
||||
#define TCR_E500_FPEXT_MASK (0xf << TCR_E500_FPEXT_SHIFT)
|
||||
#define TCR_E500_WPEXT_SHIFT 17 /* Watchdog Timer Period Extension */
|
||||
#define TCR_E500_WPEXT_MASK (0xf << TCR_E500_WPEXT_SHIFT)
|
||||
|
||||
/* Timer Status Register */
|
||||
|
||||
#define TSR_FIS (1 << 26) /* Fixed-Interval Timer Interrupt Status */
|
||||
#define TSR_DIS (1 << 27) /* Decrementer Interrupt Status */
|
||||
#define TSR_WRS_SHIFT 28 /* Watchdog Timer Reset Status */
|
||||
#define TSR_WRS_MASK (0x3 << TSR_WRS_SHIFT)
|
||||
#define TSR_WIS (1 << 30) /* Watchdog Timer Interrupt Status */
|
||||
#define TSR_ENW (1 << 31) /* Enable Next Watchdog Timer */
|
||||
|
||||
typedef struct booke_timer_t booke_timer_t;
|
||||
struct booke_timer_t {
|
||||
|
||||
uint64_t fit_next;
|
||||
struct QEMUTimer *fit_timer;
|
||||
|
||||
uint64_t wdt_next;
|
||||
struct QEMUTimer *wdt_timer;
|
||||
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static void booke_update_irq(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_DECR,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_DIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_DIE));
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_WDT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_WIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_WIE));
|
||||
|
||||
ppc_set_irq(cpu, PPC_INTERRUPT_FIT,
|
||||
(env->spr[SPR_BOOKE_TSR] & TSR_FIS
|
||||
&& env->spr[SPR_BOOKE_TCR] & TCR_FIE));
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the FIT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_fit_target(CPUPPCState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t fp = (env->spr[SPR_BOOKE_TCR] & TCR_FP_MASK) >> TCR_FP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Fixed-interval timer period extension */
|
||||
uint32_t fpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_FPEXT_MASK)
|
||||
>> TCR_E500_FPEXT_SHIFT;
|
||||
fp = 63 - (fp | fpext << 2);
|
||||
} else {
|
||||
fp = env->fit_period[fp];
|
||||
}
|
||||
|
||||
return fp;
|
||||
}
|
||||
|
||||
/* Return the location of the bit of time base at which the WDT will raise an
|
||||
interrupt */
|
||||
static uint8_t booke_get_wdt_target(CPUPPCState *env, ppc_tb_t *tb_env)
|
||||
{
|
||||
uint8_t wp = (env->spr[SPR_BOOKE_TCR] & TCR_WP_MASK) >> TCR_WP_SHIFT;
|
||||
|
||||
if (tb_env->flags & PPC_TIMER_E500) {
|
||||
/* e500 Watchdog timer period extension */
|
||||
uint32_t wpext = (env->spr[SPR_BOOKE_TCR] & TCR_E500_WPEXT_MASK)
|
||||
>> TCR_E500_WPEXT_SHIFT;
|
||||
wp = 63 - (wp | wpext << 2);
|
||||
} else {
|
||||
wp = env->wdt_period[wp];
|
||||
}
|
||||
|
||||
return wp;
|
||||
}
|
||||
|
||||
static void booke_update_fixed_timer(CPUPPCState *env,
|
||||
uint8_t target_bit,
|
||||
uint64_t *next,
|
||||
struct QEMUTimer *timer)
|
||||
{
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
uint64_t lapse;
|
||||
uint64_t tb;
|
||||
uint64_t period = 1 << (target_bit + 1);
|
||||
uint64_t now;
|
||||
|
||||
now = qemu_get_clock_ns(vm_clock);
|
||||
tb = cpu_ppc_get_tb(tb_env, now, tb_env->tb_offset);
|
||||
|
||||
lapse = period - ((tb - (1 << target_bit)) & (period - 1));
|
||||
|
||||
*next = now + muldiv64(lapse, get_ticks_per_sec(), tb_env->tb_freq);
|
||||
|
||||
/* XXX: If expire time is now. We can't run the callback because we don't
|
||||
* have access to it. So we just set the timer one nanosecond later.
|
||||
*/
|
||||
|
||||
if (*next == now) {
|
||||
(*next)++;
|
||||
}
|
||||
|
||||
qemu_mod_timer(timer, *next);
|
||||
}
|
||||
|
||||
static void booke_decr_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_DIS;
|
||||
booke_update_irq(cpu);
|
||||
|
||||
if (env->spr[SPR_BOOKE_TCR] & TCR_ARE) {
|
||||
/* Auto Reload */
|
||||
cpu_ppc_store_decr(env, env->spr[SPR_BOOKE_DECAR]);
|
||||
}
|
||||
}
|
||||
|
||||
static void booke_fit_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
env->spr[SPR_BOOKE_TSR] |= TSR_FIS;
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer);
|
||||
}
|
||||
|
||||
static void booke_wdt_cb(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
booke_timer = tb_env->opaque;
|
||||
|
||||
/* TODO: There's lots of complicated stuff to do here */
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer);
|
||||
}
|
||||
|
||||
void store_booke_tsr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] &= ~val;
|
||||
booke_update_irq(cpu);
|
||||
}
|
||||
|
||||
void store_booke_tcr(CPUPPCState *env, target_ulong val)
|
||||
{
|
||||
PowerPCCPU *cpu = ppc_env_get_cpu(env);
|
||||
ppc_tb_t *tb_env = env->tb_env;
|
||||
booke_timer_t *booke_timer = tb_env->opaque;
|
||||
|
||||
tb_env = env->tb_env;
|
||||
env->spr[SPR_BOOKE_TCR] = val;
|
||||
|
||||
booke_update_irq(cpu);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_fit_target(env, tb_env),
|
||||
&booke_timer->fit_next,
|
||||
booke_timer->fit_timer);
|
||||
|
||||
booke_update_fixed_timer(env,
|
||||
booke_get_wdt_target(env, tb_env),
|
||||
&booke_timer->wdt_next,
|
||||
booke_timer->wdt_timer);
|
||||
|
||||
}
|
||||
|
||||
static void ppc_booke_timer_reset_handle(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
env->spr[SPR_BOOKE_TSR] = 0;
|
||||
env->spr[SPR_BOOKE_TCR] = 0;
|
||||
|
||||
booke_update_irq(cpu);
|
||||
}
|
||||
|
||||
void ppc_booke_timers_init(PowerPCCPU *cpu, uint32_t freq, uint32_t flags)
|
||||
{
|
||||
ppc_tb_t *tb_env;
|
||||
booke_timer_t *booke_timer;
|
||||
|
||||
tb_env = g_malloc0(sizeof(ppc_tb_t));
|
||||
booke_timer = g_malloc0(sizeof(booke_timer_t));
|
||||
|
||||
cpu->env.tb_env = tb_env;
|
||||
tb_env->flags = flags | PPC_TIMER_BOOKE | PPC_DECR_ZERO_TRIGGERED;
|
||||
|
||||
tb_env->tb_freq = freq;
|
||||
tb_env->decr_freq = freq;
|
||||
tb_env->opaque = booke_timer;
|
||||
tb_env->decr_timer = qemu_new_timer_ns(vm_clock, &booke_decr_cb, cpu);
|
||||
|
||||
booke_timer->fit_timer =
|
||||
qemu_new_timer_ns(vm_clock, &booke_fit_cb, cpu);
|
||||
booke_timer->wdt_timer =
|
||||
qemu_new_timer_ns(vm_clock, &booke_wdt_cb, cpu);
|
||||
|
||||
qemu_register_reset(ppc_booke_timer_reset_handle, cpu);
|
||||
}
|
963
hw/ppc/spapr.c
Normal file
963
hw/ppc/spapr.c
Normal file
|
@ -0,0 +1,963 @@
|
|||
/*
|
||||
* QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator
|
||||
*
|
||||
* Copyright (c) 2004-2007 Fabrice Bellard
|
||||
* Copyright (c) 2007 Jocelyn Mayer
|
||||
* Copyright (c) 2010 David Gibson, IBM Corporation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/hw.h"
|
||||
#include "elf.h"
|
||||
#include "net/net.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "sysemu/cpus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
|
||||
#include "hw/boards.h"
|
||||
#include "hw/ppc.h"
|
||||
#include "hw/loader.h"
|
||||
|
||||
#include "hw/spapr.h"
|
||||
#include "hw/spapr_vio.h"
|
||||
#include "hw/spapr_pci.h"
|
||||
#include "hw/xics.h"
|
||||
#include "hw/pci/msi.h"
|
||||
|
||||
#include "sysemu/kvm.h"
|
||||
#include "kvm_ppc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
|
||||
#include "exec/address-spaces.h"
|
||||
#include "hw/usb.h"
|
||||
#include "qemu/config-file.h"
|
||||
|
||||
#include <libfdt.h>
|
||||
|
||||
/* SLOF memory layout:
|
||||
*
|
||||
* SLOF raw image loaded at 0, copies its romfs right below the flat
|
||||
* device-tree, then position SLOF itself 31M below that
|
||||
*
|
||||
* So we set FW_OVERHEAD to 40MB which should account for all of that
|
||||
* and more
|
||||
*
|
||||
* We load our kernel at 4M, leaving space for SLOF initial image
|
||||
*/
|
||||
#define FDT_MAX_SIZE 0x10000
|
||||
#define RTAS_MAX_SIZE 0x10000
|
||||
#define FW_MAX_SIZE 0x400000
|
||||
#define FW_FILE_NAME "slof.bin"
|
||||
#define FW_OVERHEAD 0x2800000
|
||||
#define KERNEL_LOAD_ADDR FW_MAX_SIZE
|
||||
|
||||
#define MIN_RMA_SLOF 128UL
|
||||
|
||||
#define TIMEBASE_FREQ 512000000ULL
|
||||
|
||||
#define MAX_CPUS 256
|
||||
#define XICS_IRQS 1024
|
||||
|
||||
#define PHANDLE_XICP 0x00001111
|
||||
|
||||
#define HTAB_SIZE(spapr) (1ULL << ((spapr)->htab_shift))
|
||||
|
||||
sPAPREnvironment *spapr;
|
||||
|
||||
int spapr_allocate_irq(int hint, bool lsi)
|
||||
{
|
||||
int irq;
|
||||
|
||||
if (hint) {
|
||||
irq = hint;
|
||||
/* FIXME: we should probably check for collisions somehow */
|
||||
} else {
|
||||
irq = spapr->next_irq++;
|
||||
}
|
||||
|
||||
/* Configure irq type */
|
||||
if (!xics_get_qirq(spapr->icp, irq)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
xics_set_irq_type(spapr->icp, irq, lsi);
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
/* Allocate block of consequtive IRQs, returns a number of the first */
|
||||
int spapr_allocate_irq_block(int num, bool lsi)
|
||||
{
|
||||
int first = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; ++i) {
|
||||
int irq;
|
||||
|
||||
irq = spapr_allocate_irq(0, lsi);
|
||||
if (!irq) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (0 == i) {
|
||||
first = irq;
|
||||
}
|
||||
|
||||
/* If the above doesn't create a consecutive block then that's
|
||||
* an internal bug */
|
||||
assert(irq == (first + i));
|
||||
}
|
||||
|
||||
return first;
|
||||
}
|
||||
|
||||
static int spapr_fixup_cpu_dt(void *fdt, sPAPREnvironment *spapr)
|
||||
{
|
||||
int ret = 0, offset;
|
||||
CPUPPCState *env;
|
||||
CPUState *cpu;
|
||||
char cpu_model[32];
|
||||
int smt = kvmppc_smt_threads();
|
||||
uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)};
|
||||
|
||||
assert(spapr->cpu_model);
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
cpu = CPU(ppc_env_get_cpu(env));
|
||||
uint32_t associativity[] = {cpu_to_be32(0x5),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0),
|
||||
cpu_to_be32(cpu->numa_node),
|
||||
cpu_to_be32(cpu->cpu_index)};
|
||||
|
||||
if ((cpu->cpu_index % smt) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(cpu_model, 32, "/cpus/%s@%x", spapr->cpu_model,
|
||||
cpu->cpu_index);
|
||||
|
||||
offset = fdt_path_offset(fdt, cpu_model);
|
||||
if (offset < 0) {
|
||||
return offset;
|
||||
}
|
||||
|
||||
if (nb_numa_nodes > 1) {
|
||||
ret = fdt_setprop(fdt, offset, "ibm,associativity", associativity,
|
||||
sizeof(associativity));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = fdt_setprop(fdt, offset, "ibm,pft-size",
|
||||
pft_size_prop, sizeof(pft_size_prop));
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static size_t create_page_sizes_prop(CPUPPCState *env, uint32_t *prop,
|
||||
size_t maxsize)
|
||||
{
|
||||
size_t maxcells = maxsize / sizeof(uint32_t);
|
||||
int i, j, count;
|
||||
uint32_t *p = prop;
|
||||
|
||||
for (i = 0; i < PPC_PAGE_SIZES_MAX_SZ; i++) {
|
||||
struct ppc_one_seg_page_size *sps = &env->sps.sps[i];
|
||||
|
||||
if (!sps->page_shift) {
|
||||
break;
|
||||
}
|
||||
for (count = 0; count < PPC_PAGE_SIZES_MAX_SZ; count++) {
|
||||
if (sps->enc[count].page_shift == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((p - prop) >= (maxcells - 3 - count * 2)) {
|
||||
break;
|
||||
}
|
||||
*(p++) = cpu_to_be32(sps->page_shift);
|
||||
*(p++) = cpu_to_be32(sps->slb_enc);
|
||||
*(p++) = cpu_to_be32(count);
|
||||
for (j = 0; j < count; j++) {
|
||||
*(p++) = cpu_to_be32(sps->enc[j].page_shift);
|
||||
*(p++) = cpu_to_be32(sps->enc[j].pte_enc);
|
||||
}
|
||||
}
|
||||
|
||||
return (p - prop) * sizeof(uint32_t);
|
||||
}
|
||||
|
||||
#define _FDT(exp) \
|
||||
do { \
|
||||
int ret = (exp); \
|
||||
if (ret < 0) { \
|
||||
fprintf(stderr, "qemu: error creating device tree: %s: %s\n", \
|
||||
#exp, fdt_strerror(ret)); \
|
||||
exit(1); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
static void *spapr_create_fdt_skel(const char *cpu_model,
|
||||
hwaddr initrd_base,
|
||||
hwaddr initrd_size,
|
||||
hwaddr kernel_size,
|
||||
const char *boot_device,
|
||||
const char *kernel_cmdline,
|
||||
uint32_t epow_irq)
|
||||
{
|
||||
void *fdt;
|
||||
CPUPPCState *env;
|
||||
uint32_t start_prop = cpu_to_be32(initrd_base);
|
||||
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
|
||||
char hypertas_prop[] = "hcall-pft\0hcall-term\0hcall-dabr\0hcall-interrupt"
|
||||
"\0hcall-tce\0hcall-vio\0hcall-splpar\0hcall-bulk";
|
||||
char qemu_hypertas_prop[] = "hcall-memop1";
|
||||
uint32_t refpoints[] = {cpu_to_be32(0x4), cpu_to_be32(0x4)};
|
||||
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
|
||||
char *modelname;
|
||||
int i, smt = kvmppc_smt_threads();
|
||||
unsigned char vec5[] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x80};
|
||||
|
||||
fdt = g_malloc0(FDT_MAX_SIZE);
|
||||
_FDT((fdt_create(fdt, FDT_MAX_SIZE)));
|
||||
|
||||
if (kernel_size) {
|
||||
_FDT((fdt_add_reservemap_entry(fdt, KERNEL_LOAD_ADDR, kernel_size)));
|
||||
}
|
||||
if (initrd_size) {
|
||||
_FDT((fdt_add_reservemap_entry(fdt, initrd_base, initrd_size)));
|
||||
}
|
||||
_FDT((fdt_finish_reservemap(fdt)));
|
||||
|
||||
/* Root node */
|
||||
_FDT((fdt_begin_node(fdt, "")));
|
||||
_FDT((fdt_property_string(fdt, "device_type", "chrp")));
|
||||
_FDT((fdt_property_string(fdt, "model", "IBM pSeries (emulated by qemu)")));
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "#address-cells", 0x2)));
|
||||
_FDT((fdt_property_cell(fdt, "#size-cells", 0x2)));
|
||||
|
||||
/* /chosen */
|
||||
_FDT((fdt_begin_node(fdt, "chosen")));
|
||||
|
||||
/* Set Form1_affinity */
|
||||
_FDT((fdt_property(fdt, "ibm,architecture-vec-5", vec5, sizeof(vec5))));
|
||||
|
||||
_FDT((fdt_property_string(fdt, "bootargs", kernel_cmdline)));
|
||||
_FDT((fdt_property(fdt, "linux,initrd-start",
|
||||
&start_prop, sizeof(start_prop))));
|
||||
_FDT((fdt_property(fdt, "linux,initrd-end",
|
||||
&end_prop, sizeof(end_prop))));
|
||||
if (kernel_size) {
|
||||
uint64_t kprop[2] = { cpu_to_be64(KERNEL_LOAD_ADDR),
|
||||
cpu_to_be64(kernel_size) };
|
||||
|
||||
_FDT((fdt_property(fdt, "qemu,boot-kernel", &kprop, sizeof(kprop))));
|
||||
}
|
||||
if (boot_device) {
|
||||
_FDT((fdt_property_string(fdt, "qemu,boot-device", boot_device)));
|
||||
}
|
||||
_FDT((fdt_property_cell(fdt, "qemu,graphic-width", graphic_width)));
|
||||
_FDT((fdt_property_cell(fdt, "qemu,graphic-height", graphic_height)));
|
||||
_FDT((fdt_property_cell(fdt, "qemu,graphic-depth", graphic_depth)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* cpus */
|
||||
_FDT((fdt_begin_node(fdt, "cpus")));
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
|
||||
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
|
||||
|
||||
modelname = g_strdup(cpu_model);
|
||||
|
||||
for (i = 0; i < strlen(modelname); i++) {
|
||||
modelname[i] = toupper(modelname[i]);
|
||||
}
|
||||
|
||||
/* This is needed during FDT finalization */
|
||||
spapr->cpu_model = g_strdup(modelname);
|
||||
|
||||
for (env = first_cpu; env != NULL; env = env->next_cpu) {
|
||||
CPUState *cpu = CPU(ppc_env_get_cpu(env));
|
||||
int index = cpu->cpu_index;
|
||||
uint32_t servers_prop[smp_threads];
|
||||
uint32_t gservers_prop[smp_threads * 2];
|
||||
char *nodename;
|
||||
uint32_t segs[] = {cpu_to_be32(28), cpu_to_be32(40),
|
||||
0xffffffff, 0xffffffff};
|
||||
uint32_t tbfreq = kvm_enabled() ? kvmppc_get_tbfreq() : TIMEBASE_FREQ;
|
||||
uint32_t cpufreq = kvm_enabled() ? kvmppc_get_clockfreq() : 1000000000;
|
||||
uint32_t page_sizes_prop[64];
|
||||
size_t page_sizes_prop_size;
|
||||
|
||||
if ((index % smt) != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nodename = g_strdup_printf("%s@%x", modelname, index);
|
||||
|
||||
_FDT((fdt_begin_node(fdt, nodename)));
|
||||
|
||||
g_free(nodename);
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "reg", index)));
|
||||
_FDT((fdt_property_string(fdt, "device_type", "cpu")));
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "cpu-version", env->spr[SPR_PVR])));
|
||||
_FDT((fdt_property_cell(fdt, "dcache-block-size",
|
||||
env->dcache_line_size)));
|
||||
_FDT((fdt_property_cell(fdt, "icache-block-size",
|
||||
env->icache_line_size)));
|
||||
_FDT((fdt_property_cell(fdt, "timebase-frequency", tbfreq)));
|
||||
_FDT((fdt_property_cell(fdt, "clock-frequency", cpufreq)));
|
||||
_FDT((fdt_property_cell(fdt, "ibm,slb-size", env->slb_nr)));
|
||||
_FDT((fdt_property_string(fdt, "status", "okay")));
|
||||
_FDT((fdt_property(fdt, "64-bit", NULL, 0)));
|
||||
|
||||
/* Build interrupt servers and gservers properties */
|
||||
for (i = 0; i < smp_threads; i++) {
|
||||
servers_prop[i] = cpu_to_be32(index + i);
|
||||
/* Hack, direct the group queues back to cpu 0 */
|
||||
gservers_prop[i*2] = cpu_to_be32(index + i);
|
||||
gservers_prop[i*2 + 1] = 0;
|
||||
}
|
||||
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-server#s",
|
||||
servers_prop, sizeof(servers_prop))));
|
||||
_FDT((fdt_property(fdt, "ibm,ppc-interrupt-gserver#s",
|
||||
gservers_prop, sizeof(gservers_prop))));
|
||||
|
||||
if (env->mmu_model & POWERPC_MMU_1TSEG) {
|
||||
_FDT((fdt_property(fdt, "ibm,processor-segment-sizes",
|
||||
segs, sizeof(segs))));
|
||||
}
|
||||
|
||||
/* Advertise VMX/VSX (vector extensions) if available
|
||||
* 0 / no property == no vector extensions
|
||||
* 1 == VMX / Altivec available
|
||||
* 2 == VSX available */
|
||||
if (env->insns_flags & PPC_ALTIVEC) {
|
||||
uint32_t vmx = (env->insns_flags2 & PPC2_VSX) ? 2 : 1;
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "ibm,vmx", vmx)));
|
||||
}
|
||||
|
||||
/* Advertise DFP (Decimal Floating Point) if available
|
||||
* 0 / no property == no DFP
|
||||
* 1 == DFP available */
|
||||
if (env->insns_flags2 & PPC2_DFP) {
|
||||
_FDT((fdt_property_cell(fdt, "ibm,dfp", 1)));
|
||||
}
|
||||
|
||||
page_sizes_prop_size = create_page_sizes_prop(env, page_sizes_prop,
|
||||
sizeof(page_sizes_prop));
|
||||
if (page_sizes_prop_size) {
|
||||
_FDT((fdt_property(fdt, "ibm,segment-page-sizes",
|
||||
page_sizes_prop, page_sizes_prop_size)));
|
||||
}
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
}
|
||||
|
||||
g_free(modelname);
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* RTAS */
|
||||
_FDT((fdt_begin_node(fdt, "rtas")));
|
||||
|
||||
_FDT((fdt_property(fdt, "ibm,hypertas-functions", hypertas_prop,
|
||||
sizeof(hypertas_prop))));
|
||||
_FDT((fdt_property(fdt, "qemu,hypertas-functions", qemu_hypertas_prop,
|
||||
sizeof(qemu_hypertas_prop))));
|
||||
|
||||
_FDT((fdt_property(fdt, "ibm,associativity-reference-points",
|
||||
refpoints, sizeof(refpoints))));
|
||||
|
||||
_FDT((fdt_property_cell(fdt, "rtas-error-log-max", RTAS_ERROR_LOG_MAX)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* interrupt controller */
|
||||
_FDT((fdt_begin_node(fdt, "interrupt-controller")));
|
||||
|
||||
_FDT((fdt_property_string(fdt, "device_type",
|
||||
"PowerPC-External-Interrupt-Presentation")));
|
||||
_FDT((fdt_property_string(fdt, "compatible", "IBM,ppc-xicp")));
|
||||
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
|
||||
_FDT((fdt_property(fdt, "ibm,interrupt-server-ranges",
|
||||
interrupt_server_ranges_prop,
|
||||
sizeof(interrupt_server_ranges_prop))));
|
||||
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 2)));
|
||||
_FDT((fdt_property_cell(fdt, "linux,phandle", PHANDLE_XICP)));
|
||||
_FDT((fdt_property_cell(fdt, "phandle", PHANDLE_XICP)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* vdevice */
|
||||
_FDT((fdt_begin_node(fdt, "vdevice")));
|
||||
|
||||
_FDT((fdt_property_string(fdt, "device_type", "vdevice")));
|
||||
_FDT((fdt_property_string(fdt, "compatible", "IBM,vdevice")));
|
||||
_FDT((fdt_property_cell(fdt, "#address-cells", 0x1)));
|
||||
_FDT((fdt_property_cell(fdt, "#size-cells", 0x0)));
|
||||
_FDT((fdt_property_cell(fdt, "#interrupt-cells", 0x2)));
|
||||
_FDT((fdt_property(fdt, "interrupt-controller", NULL, 0)));
|
||||
|
||||
_FDT((fdt_end_node(fdt)));
|
||||
|
||||
/* event-sources */
|
||||
spapr_events_fdt_skel(fdt, epow_irq);
|
||||
|
||||
_FDT((fdt_end_node(fdt))); /* close root node */
|
||||
_FDT((fdt_finish(fdt)));
|
||||
|
||||
return fdt;
|
||||
}
|
||||
|
||||
static int spapr_populate_memory(sPAPREnvironment *spapr, void *fdt)
|
||||
{
|
||||
uint32_t associativity[] = {cpu_to_be32(0x4), cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0), cpu_to_be32(0x0),
|
||||
cpu_to_be32(0x0)};
|
||||
char mem_name[32];
|
||||
hwaddr node0_size, mem_start;
|
||||
uint64_t mem_reg_property[2];
|
||||
int i, off;
|
||||
|
||||
/* memory node(s) */
|
||||
node0_size = (nb_numa_nodes > 1) ? node_mem[0] : ram_size;
|
||||
if (spapr->rma_size > node0_size) {
|
||||
spapr->rma_size = node0_size;
|
||||
}
|
||||
|
||||
/* RMA */
|
||||
mem_reg_property[0] = 0;
|
||||
mem_reg_property[1] = cpu_to_be64(spapr->rma_size);
|
||||
off = fdt_add_subnode(fdt, 0, "memory@0");
|
||||
_FDT(off);
|
||||
_FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
|
||||
_FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property))));
|
||||
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
|
||||
sizeof(associativity))));
|
||||
|
||||
/* RAM: Node 0 */
|
||||
if (node0_size > spapr->rma_size) {
|
||||
mem_reg_property[0] = cpu_to_be64(spapr->rma_size);
|
||||
mem_reg_property[1] = cpu_to_be64(node0_size - spapr->rma_size);
|
||||
|
||||
sprintf(mem_name, "memory@" TARGET_FMT_lx, spapr->rma_size);
|
||||
off = fdt_add_subnode(fdt, 0, mem_name);
|
||||
_FDT(off);
|
||||
_FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
|
||||
_FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property))));
|
||||
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
|
||||
sizeof(associativity))));
|
||||
}
|
||||
|
||||
/* RAM: Node 1 and beyond */
|
||||
mem_start = node0_size;
|
||||
for (i = 1; i < nb_numa_nodes; i++) {
|
||||
mem_reg_property[0] = cpu_to_be64(mem_start);
|
||||
mem_reg_property[1] = cpu_to_be64(node_mem[i]);
|
||||
associativity[3] = associativity[4] = cpu_to_be32(i);
|
||||
sprintf(mem_name, "memory@" TARGET_FMT_lx, mem_start);
|
||||
off = fdt_add_subnode(fdt, 0, mem_name);
|
||||
_FDT(off);
|
||||
_FDT((fdt_setprop_string(fdt, off, "device_type", "memory")));
|
||||
_FDT((fdt_setprop(fdt, off, "reg", mem_reg_property,
|
||||
sizeof(mem_reg_property))));
|
||||
_FDT((fdt_setprop(fdt, off, "ibm,associativity", associativity,
|
||||
sizeof(associativity))));
|
||||
mem_start += node_mem[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void spapr_finalize_fdt(sPAPREnvironment *spapr,
|
||||
hwaddr fdt_addr,
|
||||
hwaddr rtas_addr,
|
||||
hwaddr rtas_size)
|
||||
{
|
||||
int ret;
|
||||
void *fdt;
|
||||
sPAPRPHBState *phb;
|
||||
|
||||
fdt = g_malloc(FDT_MAX_SIZE);
|
||||
|
||||
/* open out the base tree into a temp buffer for the final tweaks */
|
||||
_FDT((fdt_open_into(spapr->fdt_skel, fdt, FDT_MAX_SIZE)));
|
||||
|
||||
ret = spapr_populate_memory(spapr, fdt);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't setup memory nodes in fdt\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = spapr_populate_vdevice(spapr->vio_bus, fdt);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't setup vio devices in fdt\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
QLIST_FOREACH(phb, &spapr->phbs, list) {
|
||||
ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "couldn't setup PCI devices in fdt\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* RTAS */
|
||||
ret = spapr_rtas_device_tree_setup(fdt, rtas_addr, rtas_size);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Couldn't set up RTAS device tree properties\n");
|
||||
}
|
||||
|
||||
/* Advertise NUMA via ibm,associativity */
|
||||
ret = spapr_fixup_cpu_dt(fdt, spapr);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "Couldn't finalize CPU device tree properties\n");
|
||||
}
|
||||
|
||||
if (!spapr->has_graphics) {
|
||||
spapr_populate_chosen_stdout(fdt, spapr->vio_bus);
|
||||
}
|
||||
|
||||
_FDT((fdt_pack(fdt)));
|
||||
|
||||
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
|
||||
hw_error("FDT too big ! 0x%x bytes (max is 0x%x)\n",
|
||||
fdt_totalsize(fdt), FDT_MAX_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
|
||||
|
||||
g_free(fdt);
|
||||
}
|
||||
|
||||
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
|
||||
{
|
||||
return (addr & 0x0fffffff) + KERNEL_LOAD_ADDR;
|
||||
}
|
||||
|
||||
static void emulate_spapr_hypercall(PowerPCCPU *cpu)
|
||||
{
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
if (msr_pr) {
|
||||
hcall_dprintf("Hypercall made with MSR[PR]=1\n");
|
||||
env->gpr[3] = H_PRIVILEGE;
|
||||
} else {
|
||||
env->gpr[3] = spapr_hypercall(cpu, env->gpr[3], &env->gpr[4]);
|
||||
}
|
||||
}
|
||||
|
||||
static void spapr_reset_htab(sPAPREnvironment *spapr)
|
||||
{
|
||||
long shift;
|
||||
|
||||
/* allocate hash page table. For now we always make this 16mb,
|
||||
* later we should probably make it scale to the size of guest
|
||||
* RAM */
|
||||
|
||||
shift = kvmppc_reset_htab(spapr->htab_shift);
|
||||
|
||||
if (shift > 0) {
|
||||
/* Kernel handles htab, we don't need to allocate one */
|
||||
spapr->htab_shift = shift;
|
||||
} else {
|
||||
if (!spapr->htab) {
|
||||
/* Allocate an htab if we don't yet have one */
|
||||
spapr->htab = qemu_memalign(HTAB_SIZE(spapr), HTAB_SIZE(spapr));
|
||||
}
|
||||
|
||||
/* And clear it */
|
||||
memset(spapr->htab, 0, HTAB_SIZE(spapr));
|
||||
}
|
||||
|
||||
/* Update the RMA size if necessary */
|
||||
if (spapr->vrma_adjust) {
|
||||
spapr->rma_size = kvmppc_rma_size(ram_size, spapr->htab_shift);
|
||||
}
|
||||
}
|
||||
|
||||
static void ppc_spapr_reset(void)
|
||||
{
|
||||
/* Reset the hash table & recalc the RMA */
|
||||
spapr_reset_htab(spapr);
|
||||
|
||||
qemu_devices_reset();
|
||||
|
||||
/* Load the fdt */
|
||||
spapr_finalize_fdt(spapr, spapr->fdt_addr, spapr->rtas_addr,
|
||||
spapr->rtas_size);
|
||||
|
||||
/* Set up the entry state */
|
||||
first_cpu->gpr[3] = spapr->fdt_addr;
|
||||
first_cpu->gpr[5] = 0;
|
||||
first_cpu->halted = 0;
|
||||
first_cpu->nip = spapr->entry_point;
|
||||
|
||||
}
|
||||
|
||||
static void spapr_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
|
||||
/* All CPUs start halted. CPU0 is unhalted from the machine level
|
||||
* reset code and the rest are explicitly started up by the guest
|
||||
* using an RTAS call */
|
||||
env->halted = 1;
|
||||
|
||||
env->spr[SPR_HIOR] = 0;
|
||||
|
||||
env->external_htab = spapr->htab;
|
||||
env->htab_base = -1;
|
||||
env->htab_mask = HTAB_SIZE(spapr) - 1;
|
||||
env->spr[SPR_SDR1] = (unsigned long)spapr->htab |
|
||||
(spapr->htab_shift - 18);
|
||||
}
|
||||
|
||||
static void spapr_create_nvram(sPAPREnvironment *spapr)
|
||||
{
|
||||
QemuOpts *machine_opts;
|
||||
DeviceState *dev;
|
||||
|
||||
dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram");
|
||||
|
||||
machine_opts = qemu_opts_find(qemu_find_opts("machine"), 0);
|
||||
if (machine_opts) {
|
||||
const char *drivename;
|
||||
|
||||
drivename = qemu_opt_get(machine_opts, "nvram");
|
||||
if (drivename) {
|
||||
BlockDriverState *bs;
|
||||
|
||||
bs = bdrv_find(drivename);
|
||||
if (!bs) {
|
||||
fprintf(stderr, "No such block device \"%s\" for nvram\n",
|
||||
drivename);
|
||||
exit(1);
|
||||
}
|
||||
qdev_prop_set_drive_nofail(dev, "drive", bs);
|
||||
}
|
||||
}
|
||||
|
||||
qdev_init_nofail(dev);
|
||||
|
||||
spapr->nvram = (struct sPAPRNVRAM *)dev;
|
||||
}
|
||||
|
||||
/* Returns whether we want to use VGA or not */
|
||||
static int spapr_vga_init(PCIBus *pci_bus)
|
||||
{
|
||||
switch (vga_interface_type) {
|
||||
case VGA_NONE:
|
||||
case VGA_STD:
|
||||
return pci_vga_init(pci_bus) != NULL;
|
||||
default:
|
||||
fprintf(stderr, "This vga model is not supported,"
|
||||
"currently it only supports -vga std\n");
|
||||
exit(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* pSeries LPAR / sPAPR hardware init */
|
||||
static void ppc_spapr_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
ram_addr_t ram_size = args->ram_size;
|
||||
const char *cpu_model = args->cpu_model;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
const char *initrd_filename = args->initrd_filename;
|
||||
const char *boot_device = args->boot_device;
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
PCIHostState *phb;
|
||||
int i;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *ram = g_new(MemoryRegion, 1);
|
||||
hwaddr rma_alloc_size;
|
||||
uint32_t initrd_base = 0;
|
||||
long kernel_size = 0, initrd_size = 0;
|
||||
long load_limit, rtas_limit, fw_size;
|
||||
char *filename;
|
||||
|
||||
msi_supported = true;
|
||||
|
||||
spapr = g_malloc0(sizeof(*spapr));
|
||||
QLIST_INIT(&spapr->phbs);
|
||||
|
||||
cpu_ppc_hypercall = emulate_spapr_hypercall;
|
||||
|
||||
/* Allocate RMA if necessary */
|
||||
rma_alloc_size = kvmppc_alloc_rma("ppc_spapr.rma", sysmem);
|
||||
|
||||
if (rma_alloc_size == -1) {
|
||||
hw_error("qemu: Unable to create RMA\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (rma_alloc_size && (rma_alloc_size < ram_size)) {
|
||||
spapr->rma_size = rma_alloc_size;
|
||||
} else {
|
||||
spapr->rma_size = ram_size;
|
||||
|
||||
/* With KVM, we don't actually know whether KVM supports an
|
||||
* unbounded RMA (PR KVM) or is limited by the hash table size
|
||||
* (HV KVM using VRMA), so we always assume the latter
|
||||
*
|
||||
* In that case, we also limit the initial allocations for RTAS
|
||||
* etc... to 256M since we have no way to know what the VRMA size
|
||||
* is going to be as it depends on the size of the hash table
|
||||
* isn't determined yet.
|
||||
*/
|
||||
if (kvm_enabled()) {
|
||||
spapr->vrma_adjust = 1;
|
||||
spapr->rma_size = MIN(spapr->rma_size, 0x10000000);
|
||||
}
|
||||
}
|
||||
|
||||
/* We place the device tree and RTAS just below either the top of the RMA,
|
||||
* or just below 2GB, whichever is lowere, so that it can be
|
||||
* processed with 32-bit real mode code if necessary */
|
||||
rtas_limit = MIN(spapr->rma_size, 0x80000000);
|
||||
spapr->rtas_addr = rtas_limit - RTAS_MAX_SIZE;
|
||||
spapr->fdt_addr = spapr->rtas_addr - FDT_MAX_SIZE;
|
||||
load_limit = spapr->fdt_addr - FW_OVERHEAD;
|
||||
|
||||
/* We aim for a hash table of size 1/128 the size of RAM. The
|
||||
* normal rule of thumb is 1/64 the size of RAM, but that's much
|
||||
* more than needed for the Linux guests we support. */
|
||||
spapr->htab_shift = 18; /* Minimum architected size */
|
||||
while (spapr->htab_shift <= 46) {
|
||||
if ((1ULL << (spapr->htab_shift + 7)) >= ram_size) {
|
||||
break;
|
||||
}
|
||||
spapr->htab_shift++;
|
||||
}
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = kvm_enabled() ? "host" : "POWER7";
|
||||
}
|
||||
for (i = 0; i < smp_cpus; i++) {
|
||||
cpu = cpu_ppc_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to find PowerPC CPU definition\n");
|
||||
exit(1);
|
||||
}
|
||||
env = &cpu->env;
|
||||
|
||||
/* Set time-base frequency to 512 MHz */
|
||||
cpu_ppc_tb_init(env, TIMEBASE_FREQ);
|
||||
|
||||
/* PAPR always has exception vectors in RAM not ROM */
|
||||
env->hreset_excp_prefix = 0;
|
||||
|
||||
/* Tell KVM that we're in PAPR mode */
|
||||
if (kvm_enabled()) {
|
||||
kvmppc_set_papr(cpu);
|
||||
}
|
||||
|
||||
qemu_register_reset(spapr_cpu_reset, cpu);
|
||||
}
|
||||
|
||||
/* allocate RAM */
|
||||
spapr->ram_limit = ram_size;
|
||||
if (spapr->ram_limit > rma_alloc_size) {
|
||||
ram_addr_t nonrma_base = rma_alloc_size;
|
||||
ram_addr_t nonrma_size = spapr->ram_limit - rma_alloc_size;
|
||||
|
||||
memory_region_init_ram(ram, "ppc_spapr.ram", nonrma_size);
|
||||
vmstate_register_ram_global(ram);
|
||||
memory_region_add_subregion(sysmem, nonrma_base, ram);
|
||||
}
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
|
||||
spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr,
|
||||
rtas_limit - spapr->rtas_addr);
|
||||
if (spapr->rtas_size < 0) {
|
||||
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
if (spapr->rtas_size > RTAS_MAX_SIZE) {
|
||||
hw_error("RTAS too big ! 0x%lx bytes (max is 0x%x)\n",
|
||||
spapr->rtas_size, RTAS_MAX_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
g_free(filename);
|
||||
|
||||
|
||||
/* Set up Interrupt Controller */
|
||||
spapr->icp = xics_system_init(XICS_IRQS);
|
||||
spapr->next_irq = XICS_IRQ_BASE;
|
||||
|
||||
/* Set up EPOW events infrastructure */
|
||||
spapr_events_init(spapr);
|
||||
|
||||
/* Set up IOMMU */
|
||||
spapr_iommu_init();
|
||||
|
||||
/* Set up VIO bus */
|
||||
spapr->vio_bus = spapr_vio_bus_init();
|
||||
|
||||
for (i = 0; i < MAX_SERIAL_PORTS; i++) {
|
||||
if (serial_hds[i]) {
|
||||
spapr_vty_create(spapr->vio_bus, serial_hds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* We always have at least the nvram device on VIO */
|
||||
spapr_create_nvram(spapr);
|
||||
|
||||
/* Set up PCI */
|
||||
spapr_pci_rtas_init();
|
||||
|
||||
phb = spapr_create_phb(spapr, 0, "pci");
|
||||
|
||||
for (i = 0; i < nb_nics; i++) {
|
||||
NICInfo *nd = &nd_table[i];
|
||||
|
||||
if (!nd->model) {
|
||||
nd->model = g_strdup("ibmveth");
|
||||
}
|
||||
|
||||
if (strcmp(nd->model, "ibmveth") == 0) {
|
||||
spapr_vlan_create(spapr->vio_bus, nd);
|
||||
} else {
|
||||
pci_nic_init_nofail(&nd_table[i], nd->model, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i <= drive_get_max_bus(IF_SCSI); i++) {
|
||||
spapr_vscsi_create(spapr->vio_bus);
|
||||
}
|
||||
|
||||
/* Graphics */
|
||||
if (spapr_vga_init(phb->bus)) {
|
||||
spapr->has_graphics = true;
|
||||
}
|
||||
|
||||
if (usb_enabled(spapr->has_graphics)) {
|
||||
pci_create_simple(phb->bus, -1, "pci-ohci");
|
||||
if (spapr->has_graphics) {
|
||||
usbdevice_create("keyboard");
|
||||
usbdevice_create("mouse");
|
||||
}
|
||||
}
|
||||
|
||||
if (spapr->rma_size < (MIN_RMA_SLOF << 20)) {
|
||||
fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
|
||||
"%ldM guest RMA (Real Mode Area memory)\n", MIN_RMA_SLOF);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (kernel_filename) {
|
||||
uint64_t lowaddr = 0;
|
||||
|
||||
kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
|
||||
NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
|
||||
if (kernel_size < 0) {
|
||||
kernel_size = load_image_targphys(kernel_filename,
|
||||
KERNEL_LOAD_ADDR,
|
||||
load_limit - KERNEL_LOAD_ADDR);
|
||||
}
|
||||
if (kernel_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||
kernel_filename);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* load initrd */
|
||||
if (initrd_filename) {
|
||||
/* Try to locate the initrd in the gap between the kernel
|
||||
* and the firmware. Add a bit of space just in case
|
||||
*/
|
||||
initrd_base = (KERNEL_LOAD_ADDR + kernel_size + 0x1ffff) & ~0xffff;
|
||||
initrd_size = load_image_targphys(initrd_filename, initrd_base,
|
||||
load_limit - initrd_base);
|
||||
if (initrd_size < 0) {
|
||||
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||
initrd_filename);
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
initrd_base = 0;
|
||||
initrd_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, FW_FILE_NAME);
|
||||
fw_size = load_image_targphys(filename, 0, FW_MAX_SIZE);
|
||||
if (fw_size < 0) {
|
||||
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
|
||||
exit(1);
|
||||
}
|
||||
g_free(filename);
|
||||
|
||||
spapr->entry_point = 0x100;
|
||||
|
||||
/* Prepare the device tree */
|
||||
spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
|
||||
initrd_base, initrd_size,
|
||||
kernel_size,
|
||||
boot_device, kernel_cmdline,
|
||||
spapr->epow_irq);
|
||||
assert(spapr->fdt_skel != NULL);
|
||||
}
|
||||
|
||||
static QEMUMachine spapr_machine = {
|
||||
.name = "pseries",
|
||||
.desc = "pSeries Logical Partition (PAPR compliant)",
|
||||
.init = ppc_spapr_init,
|
||||
.reset = ppc_spapr_reset,
|
||||
.block_default_type = IF_SCSI,
|
||||
.max_cpus = MAX_CPUS,
|
||||
.no_parallel = 1,
|
||||
.boot_order = NULL,
|
||||
};
|
||||
|
||||
static void spapr_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&spapr_machine);
|
||||
}
|
||||
|
||||
machine_init(spapr_machine_init);
|
274
hw/ppc/virtex_ml507.c
Normal file
274
hw/ppc/virtex_ml507.c
Normal file
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
* Model of Xilinx Virtex5 ML507 PPC-440 refdesign.
|
||||
*
|
||||
* Copyright (c) 2010 Edgar E. Iglesias.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "hw/sysbus.h"
|
||||
#include "hw/hw.h"
|
||||
#include "hw/serial.h"
|
||||
#include "hw/flash.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/devices.h"
|
||||
#include "hw/boards.h"
|
||||
#include "sysemu/device_tree.h"
|
||||
#include "hw/loader.h"
|
||||
#include "elf.h"
|
||||
#include "qemu/log.h"
|
||||
#include "exec/address-spaces.h"
|
||||
|
||||
#include "hw/ppc.h"
|
||||
#include "hw/ppc4xx.h"
|
||||
#include "hw/ppc405.h"
|
||||
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "hw/xilinx.h"
|
||||
|
||||
#define EPAPR_MAGIC (0x45504150)
|
||||
#define FLASH_SIZE (16 * 1024 * 1024)
|
||||
|
||||
static struct boot_info
|
||||
{
|
||||
uint32_t bootstrap_pc;
|
||||
uint32_t cmdline;
|
||||
uint32_t fdt;
|
||||
uint32_t ima_size;
|
||||
void *vfdt;
|
||||
} boot_info;
|
||||
|
||||
/* Create reset TLB entries for BookE, spanning the 32bit addr space. */
|
||||
static void mmubooke_create_initial_mapping(CPUPPCState *env,
|
||||
target_ulong va,
|
||||
hwaddr pa)
|
||||
{
|
||||
ppcemb_tlb_t *tlb = &env->tlb.tlbe[0];
|
||||
|
||||
tlb->attr = 0;
|
||||
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
||||
tlb->size = 1 << 31; /* up to 0x80000000 */
|
||||
tlb->EPN = va & TARGET_PAGE_MASK;
|
||||
tlb->RPN = pa & TARGET_PAGE_MASK;
|
||||
tlb->PID = 0;
|
||||
|
||||
tlb = &env->tlb.tlbe[1];
|
||||
tlb->attr = 0;
|
||||
tlb->prot = PAGE_VALID | ((PAGE_READ | PAGE_WRITE | PAGE_EXEC) << 4);
|
||||
tlb->size = 1 << 31; /* up to 0xffffffff */
|
||||
tlb->EPN = 0x80000000 & TARGET_PAGE_MASK;
|
||||
tlb->RPN = 0x80000000 & TARGET_PAGE_MASK;
|
||||
tlb->PID = 0;
|
||||
}
|
||||
|
||||
static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size,
|
||||
int do_init,
|
||||
const char *cpu_model,
|
||||
uint32_t sysclk)
|
||||
{
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
qemu_irq *irqs;
|
||||
|
||||
cpu = cpu_ppc_init(cpu_model);
|
||||
if (cpu == NULL) {
|
||||
fprintf(stderr, "Unable to initialize CPU!\n");
|
||||
exit(1);
|
||||
}
|
||||
env = &cpu->env;
|
||||
|
||||
ppc_booke_timers_init(cpu, sysclk, 0/* no flags */);
|
||||
|
||||
ppc_dcr_init(env, NULL, NULL);
|
||||
|
||||
/* interrupt controller */
|
||||
irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB);
|
||||
irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT];
|
||||
irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT];
|
||||
ppcuic_init(env, irqs, 0x0C0, 0, 1);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
static void main_cpu_reset(void *opaque)
|
||||
{
|
||||
PowerPCCPU *cpu = opaque;
|
||||
CPUPPCState *env = &cpu->env;
|
||||
struct boot_info *bi = env->load_info;
|
||||
|
||||
cpu_reset(CPU(cpu));
|
||||
/* Linux Kernel Parameters (passing device tree):
|
||||
* r3: pointer to the fdt
|
||||
* r4: 0
|
||||
* r5: 0
|
||||
* r6: epapr magic
|
||||
* r7: size of IMA in bytes
|
||||
* r8: 0
|
||||
* r9: 0
|
||||
*/
|
||||
env->gpr[1] = (16<<20) - 8;
|
||||
/* Provide a device-tree. */
|
||||
env->gpr[3] = bi->fdt;
|
||||
env->nip = bi->bootstrap_pc;
|
||||
|
||||
/* Create a mapping for the kernel. */
|
||||
mmubooke_create_initial_mapping(env, 0, 0);
|
||||
env->gpr[6] = tswap32(EPAPR_MAGIC);
|
||||
env->gpr[7] = bi->ima_size;
|
||||
}
|
||||
|
||||
#define BINARY_DEVICE_TREE_FILE "virtex-ml507.dtb"
|
||||
static int xilinx_load_device_tree(hwaddr addr,
|
||||
uint32_t ramsize,
|
||||
hwaddr initrd_base,
|
||||
hwaddr initrd_size,
|
||||
const char *kernel_cmdline)
|
||||
{
|
||||
char *path;
|
||||
int fdt_size;
|
||||
#ifdef CONFIG_FDT
|
||||
void *fdt;
|
||||
int r;
|
||||
|
||||
/* Try the local "ppc.dtb" override. */
|
||||
fdt = load_device_tree("ppc.dtb", &fdt_size);
|
||||
if (!fdt) {
|
||||
path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
|
||||
if (path) {
|
||||
fdt = load_device_tree(path, &fdt_size);
|
||||
g_free(path);
|
||||
}
|
||||
if (!fdt) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = qemu_devtree_setprop_string(fdt, "/chosen", "bootargs", kernel_cmdline);
|
||||
if (r < 0)
|
||||
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
||||
cpu_physical_memory_write (addr, (void *)fdt, fdt_size);
|
||||
#else
|
||||
/* We lack libfdt so we cannot manipulate the fdt. Just pass on the blob
|
||||
to the kernel. */
|
||||
fdt_size = load_image_targphys("ppc.dtb", addr, 0x10000);
|
||||
if (fdt_size < 0) {
|
||||
path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE);
|
||||
if (path) {
|
||||
fdt_size = load_image_targphys(path, addr, 0x10000);
|
||||
g_free(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (kernel_cmdline) {
|
||||
fprintf(stderr,
|
||||
"Warning: missing libfdt, cannot pass cmdline to kernel!\n");
|
||||
}
|
||||
#endif
|
||||
return fdt_size;
|
||||
}
|
||||
|
||||
static void virtex_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
ram_addr_t ram_size = args->ram_size;
|
||||
const char *cpu_model = args->cpu_model;
|
||||
const char *kernel_filename = args->kernel_filename;
|
||||
const char *kernel_cmdline = args->kernel_cmdline;
|
||||
MemoryRegion *address_space_mem = get_system_memory();
|
||||
DeviceState *dev;
|
||||
PowerPCCPU *cpu;
|
||||
CPUPPCState *env;
|
||||
hwaddr ram_base = 0;
|
||||
DriveInfo *dinfo;
|
||||
MemoryRegion *phys_ram = g_new(MemoryRegion, 1);
|
||||
qemu_irq irq[32], *cpu_irq;
|
||||
int kernel_size;
|
||||
int i;
|
||||
|
||||
/* init CPUs */
|
||||
if (cpu_model == NULL) {
|
||||
cpu_model = "440-Xilinx";
|
||||
}
|
||||
|
||||
cpu = ppc440_init_xilinx(&ram_size, 1, cpu_model, 400000000);
|
||||
env = &cpu->env;
|
||||
qemu_register_reset(main_cpu_reset, cpu);
|
||||
|
||||
memory_region_init_ram(phys_ram, "ram", ram_size);
|
||||
vmstate_register_ram_global(phys_ram);
|
||||
memory_region_add_subregion(address_space_mem, ram_base, phys_ram);
|
||||
|
||||
dinfo = drive_get(IF_PFLASH, 0, 0);
|
||||
pflash_cfi01_register(0xfc000000, NULL, "virtex.flash", FLASH_SIZE,
|
||||
dinfo ? dinfo->bdrv : NULL, (64 * 1024),
|
||||
FLASH_SIZE >> 16,
|
||||
1, 0x89, 0x18, 0x0000, 0x0, 1);
|
||||
|
||||
cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT];
|
||||
dev = xilinx_intc_create(0x81800000, cpu_irq[0], 0);
|
||||
for (i = 0; i < 32; i++) {
|
||||
irq[i] = qdev_get_gpio_in(dev, i);
|
||||
}
|
||||
|
||||
serial_mm_init(address_space_mem, 0x83e01003ULL, 2, irq[9], 115200,
|
||||
serial_hds[0], DEVICE_LITTLE_ENDIAN);
|
||||
|
||||
/* 2 timers at irq 2 @ 62 Mhz. */
|
||||
xilinx_timer_create(0x83c00000, irq[3], 0, 62 * 1000000);
|
||||
|
||||
if (kernel_filename) {
|
||||
uint64_t entry, low, high;
|
||||
hwaddr boot_offset;
|
||||
|
||||
/* Boots a kernel elf binary. */
|
||||
kernel_size = load_elf(kernel_filename, NULL, NULL,
|
||||
&entry, &low, &high, 1, ELF_MACHINE, 0);
|
||||
boot_info.bootstrap_pc = entry & 0x00ffffff;
|
||||
|
||||
if (kernel_size < 0) {
|
||||
boot_offset = 0x1200000;
|
||||
/* If we failed loading ELF's try a raw image. */
|
||||
kernel_size = load_image_targphys(kernel_filename,
|
||||
boot_offset,
|
||||
ram_size);
|
||||
boot_info.bootstrap_pc = boot_offset;
|
||||
high = boot_info.bootstrap_pc + kernel_size + 8192;
|
||||
}
|
||||
|
||||
boot_info.ima_size = kernel_size;
|
||||
|
||||
/* Provide a device-tree. */
|
||||
boot_info.fdt = high + (8192 * 2);
|
||||
boot_info.fdt &= ~8191;
|
||||
xilinx_load_device_tree(boot_info.fdt, ram_size, 0, 0, kernel_cmdline);
|
||||
}
|
||||
env->load_info = &boot_info;
|
||||
}
|
||||
|
||||
static QEMUMachine virtex_machine = {
|
||||
.name = "virtex-ml507",
|
||||
.desc = "Xilinx Virtex ML507 reference design",
|
||||
.init = virtex_init,
|
||||
DEFAULT_MACHINE_OPTIONS,
|
||||
};
|
||||
|
||||
static void virtex_machine_init(void)
|
||||
{
|
||||
qemu_register_machine(&virtex_machine);
|
||||
}
|
||||
|
||||
machine_init(virtex_machine_init);
|
Loading…
Add table
Add a link
Reference in a new issue