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>
This commit is contained in:
David Gibson 2011-04-05 15:12:10 +10:00 committed by Alexander Graf
parent c7a5c0c928
commit a3467baa88
2 changed files with 75 additions and 48 deletions

View File

@ -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 = {

View File

@ -7,6 +7,13 @@ struct icp_state;
typedef struct sPAPREnvironment {
struct VIOsPAPRBus *vio_bus;
struct icp_state *icp;
void *htab;
long htab_size;
target_phys_addr_t fdt_addr, rtas_addr;
long rtas_size;
void *fdt_skel;
target_ulong entry_point;
} sPAPREnvironment;
#define H_SUCCESS 0