summaryrefslogtreecommitdiff
path: root/hw/spapr.c
diff options
context:
space:
mode:
authorDavid Gibson <david@gibson.dropbear.id.au>2011-04-05 15:12:10 +1000
committerAlexander Graf <agraf@suse.de>2011-04-08 11:32:21 +0200
commita3467baa88c5bbb58834952980d2f2206aab4445 (patch)
treed7b253cb8228c81343753cfa169fb702e6a025db /hw/spapr.c
parentc7a5c0c9280c2ddaa875e3cafb3df16a96af809c (diff)
downloadqemu-a3467baa88c5bbb58834952980d2f2206aab4445.zip
Delay creation of pseries device tree until reset
At present, the 'pseries' machine creates a flattened device tree in the machine->init function to pass to either the guest kernel or to firmware. However, the machine->init function runs before processing of -device command line options, which means that the device tree so created will be (incorrectly) missing devices specified that way. Supplying a correct device tree is, in any case, part of the required platform entry conditions. Therefore, this patch moves the creation and loading of the device tree from machine->init to a reset callback. The setup of entry point address and initial register state moves with it, which leads to a slight cleanup. This is not, alas, quite enough to make a fully working reset for pseries. For that we would need to reload the firmware images, which on this machine are loaded into RAM. It's a step in the right direction, though. Signed-off-by: David Gibson <dwg@au1.ibm.com> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'hw/spapr.c')
-rw-r--r--hw/spapr.c116
1 files changed, 68 insertions, 48 deletions
diff --git a/hw/spapr.c b/hw/spapr.c
index f80873cfd2..1782cc0a94 100644
--- a/hw/spapr.c
+++ b/hw/spapr.c
@@ -56,20 +56,16 @@
sPAPREnvironment *spapr;
-static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
- const char *cpu_model,
- sPAPREnvironment *spapr,
- target_phys_addr_t initrd_base,
- target_phys_addr_t initrd_size,
- const char *boot_device,
- const char *kernel_cmdline,
- target_phys_addr_t rtas_addr,
- target_phys_addr_t rtas_size,
- long hash_shift)
+static void *spapr_create_fdt_skel(const char *cpu_model,
+ target_phys_addr_t initrd_base,
+ target_phys_addr_t initrd_size,
+ const char *boot_device,
+ const char *kernel_cmdline,
+ long hash_shift)
{
void *fdt;
CPUState *env;
- uint64_t mem_reg_property[] = { 0, cpu_to_be64(ramsize) };
+ uint64_t mem_reg_property[] = { 0, cpu_to_be64(ram_size) };
uint32_t start_prop = cpu_to_be32(initrd_base);
uint32_t end_prop = cpu_to_be32(initrd_base + initrd_size);
uint32_t pft_size_prop[] = {0, cpu_to_be32(hash_shift)};
@@ -78,7 +74,6 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
uint32_t interrupt_server_ranges_prop[] = {0, cpu_to_be32(smp_cpus)};
int i;
char *modelname;
- int ret;
#define _FDT(exp) \
do { \
@@ -222,8 +217,21 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
_FDT((fdt_end_node(fdt))); /* close root node */
_FDT((fdt_finish(fdt)));
- /* re-expand to allow for further tweaks */
- _FDT((fdt_open_into(fdt, fdt, FDT_MAX_SIZE)));
+ return fdt;
+}
+
+static void spapr_finalize_fdt(sPAPREnvironment *spapr,
+ target_phys_addr_t fdt_addr,
+ target_phys_addr_t rtas_addr,
+ target_phys_addr_t rtas_size)
+{
+ int ret;
+ void *fdt;
+
+ fdt = qemu_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_vdevice(spapr->vio_bus, fdt);
if (ret < 0) {
@@ -239,9 +247,9 @@ static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
_FDT((fdt_pack(fdt)));
- *fdt_size = fdt_totalsize(fdt);
+ cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));
- return fdt;
+ qemu_free(fdt);
}
static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
@@ -254,6 +262,27 @@ static void emulate_spapr_hypercall(CPUState *env)
env->gpr[3] = spapr_hypercall(env, env->gpr[3], &env->gpr[4]);
}
+static void spapr_reset(void *opaque)
+{
+ sPAPREnvironment *spapr = (sPAPREnvironment *)opaque;
+
+ fprintf(stderr, "sPAPR reset\n");
+
+ /* flush out the hash table */
+ memset(spapr->htab, 0, spapr->htab_size);
+
+ /* 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;
+
+}
+
/* pSeries LPAR / sPAPR hardware init */
static void ppc_spapr_init(ram_addr_t ram_size,
const char *boot_device,
@@ -262,15 +291,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
const char *initrd_filename,
const char *cpu_model)
{
- void *fdt, *htab;
CPUState *env;
int i;
ram_addr_t ram_offset;
- target_phys_addr_t fdt_addr, rtas_addr;
- uint32_t kernel_base, initrd_base;
- long kernel_size, initrd_size, htab_size, rtas_size, fw_size;
+ uint32_t initrd_base;
+ long kernel_size, initrd_size, fw_size;
long pteg_shift = 17;
- int fdt_size;
char *filename;
int irq = 16;
@@ -280,9 +306,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* We place the device tree just below either the top of RAM, or
* 2GB, so that it can be processed with 32-bit code if
* necessary */
- fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
- /* RTAS goes just below that */
- rtas_addr = fdt_addr - RTAS_MAX_SIZE;
+ spapr->fdt_addr = MIN(ram_size, 0x80000000) - FDT_MAX_SIZE;
+ spapr->rtas_addr = spapr->fdt_addr - RTAS_MAX_SIZE;
/* init CPUs */
if (cpu_model == NULL) {
@@ -311,18 +336,19 @@ static void ppc_spapr_init(ram_addr_t ram_size,
/* allocate hash page table. For now we always make this 16mb,
* later we should probably make it scale to the size of guest
* RAM */
- htab_size = 1ULL << (pteg_shift + 7);
- htab = qemu_mallocz(htab_size);
+ spapr->htab_size = 1ULL << (pteg_shift + 7);
+ spapr->htab = qemu_malloc(spapr->htab_size);
for (env = first_cpu; env != NULL; env = env->next_cpu) {
- env->external_htab = htab;
+ env->external_htab = spapr->htab;
env->htab_base = -1;
- env->htab_mask = htab_size - 1;
+ env->htab_mask = spapr->htab_size - 1;
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "spapr-rtas.bin");
- rtas_size = load_image_targphys(filename, rtas_addr, ram_size - rtas_addr);
- if (rtas_size < 0) {
+ spapr->rtas_size = load_image_targphys(filename, spapr->rtas_addr,
+ ram_size - spapr->rtas_addr);
+ if (spapr->rtas_size < 0) {
hw_error("qemu: could not load LPAR rtas '%s'\n", filename);
exit(1);
}
@@ -368,13 +394,12 @@ static void ppc_spapr_init(ram_addr_t ram_size,
if (kernel_filename) {
uint64_t lowaddr = 0;
- kernel_base = KERNEL_LOAD_ADDR;
-
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_base,
- ram_size - kernel_base);
+ kernel_size = load_image_targphys(kernel_filename,
+ KERNEL_LOAD_ADDR,
+ ram_size - KERNEL_LOAD_ADDR);
}
if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n",
@@ -396,6 +421,8 @@ static void ppc_spapr_init(ram_addr_t ram_size,
initrd_base = 0;
initrd_size = 0;
}
+
+ spapr->entry_point = KERNEL_LOAD_ADDR;
} else {
if (ram_size < (MIN_RAM_SLOF << 20)) {
fprintf(stderr, "qemu: pSeries SLOF firmware requires >= "
@@ -409,7 +436,7 @@ static void ppc_spapr_init(ram_addr_t ram_size,
exit(1);
}
qemu_free(filename);
- kernel_base = 0x100;
+ spapr->entry_point = 0x100;
initrd_base = 0;
initrd_size = 0;
@@ -421,20 +448,13 @@ static void ppc_spapr_init(ram_addr_t ram_size,
}
/* Prepare the device tree */
- fdt = spapr_create_fdt(&fdt_size, ram_size, cpu_model, spapr,
- initrd_base, initrd_size,
- boot_device, kernel_cmdline,
- rtas_addr, rtas_size, pteg_shift + 7);
- assert(fdt != NULL);
+ spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
+ initrd_base, initrd_size,
+ boot_device, kernel_cmdline,
+ pteg_shift + 7);
+ assert(spapr->fdt_skel != NULL);
- cpu_physical_memory_write(fdt_addr, fdt, fdt_size);
-
- qemu_free(fdt);
-
- first_cpu->gpr[3] = fdt_addr;
- first_cpu->gpr[5] = 0;
- first_cpu->hreset_vector = kernel_base;
- first_cpu->halted = 0;
+ qemu_register_reset(spapr_reset, spapr);
}
static QEMUMachine spapr_machine = {