Commit a3467baa authored by David Gibson's avatar David Gibson Committed by Alexander Graf
Browse files

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: default avatarDavid Gibson <dwg@au1.ibm.com>
Signed-off-by: default avatarAlexander Graf <agraf@suse.de>
parent c7a5c0c9
Loading
Loading
Loading
Loading
+68 −48
Original line number Diff line number Diff line
@@ -56,20 +56,16 @@

sPAPREnvironment *spapr;

static void *spapr_create_fdt(int *fdt_size, ram_addr_t ramsize,
                              const char *cpu_model,
                              sPAPREnvironment *spapr,
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,
                              target_phys_addr_t rtas_addr,
                              target_phys_addr_t rtas_size,
                                   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,
    spapr->fdt_skel = spapr_create_fdt_skel(cpu_model,
                                            initrd_base, initrd_size,
                                            boot_device, kernel_cmdline,
                           rtas_addr, rtas_size, pteg_shift + 7);
    assert(fdt != NULL);
                                            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 = {
+7 −0
Original line number Diff line number Diff line
@@ -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