spapr: Improve handling of fdt buffer size

Previously, spapr_build_fdt() constructed the device tree in a fixed
buffer of size FDT_MAX_SIZE.  This is a bit inflexible, but more
importantly it's awkward for the case where we use it during CAS.  In
that case the guest firmware supplies a buffer and we have to
awkwardly check that what we generated fits into it afterwards, after
doing a lot of size checks during spapr_build_fdt().

Simplify this by having spapr_build_fdt() take a 'space' parameter.
For the CAS case, we pass in the buffer size provided by SLOF, for the
machine init case, we continue to pass FDT_MAX_SIZE.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Reviewed-by: Cedric Le Goater <clg@fr.ibm.com>
Reviewed-by: Greg Kurz <groug@kaod.org>
This commit is contained in:
David Gibson 2019-10-18 21:25:49 +11:00
parent 8deb8019d6
commit 97b32a6afa

View File

@ -917,7 +917,8 @@ static bool spapr_hotplugged_dev_before_cas(void)
return false; return false;
} }
static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset); static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset,
size_t space);
int spapr_h_cas_compose_response(SpaprMachineState *spapr, int spapr_h_cas_compose_response(SpaprMachineState *spapr,
target_ulong addr, target_ulong size, target_ulong addr, target_ulong size,
@ -930,24 +931,17 @@ int spapr_h_cas_compose_response(SpaprMachineState *spapr,
return 1; return 1;
} }
if (size < sizeof(hdr) || size > FW_MAX_SIZE) { if (size < sizeof(hdr)) {
error_report("SLOF provided an unexpected CAS buffer size " error_report("SLOF provided insufficient CAS buffer "
TARGET_FMT_lu " (min: %zu, max: %u)", TARGET_FMT_lu " (min: %zu)", size, sizeof(hdr));
size, sizeof(hdr), FW_MAX_SIZE);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
size -= sizeof(hdr); size -= sizeof(hdr);
fdt = spapr_build_fdt(spapr, false); fdt = spapr_build_fdt(spapr, false, size);
_FDT((fdt_pack(fdt))); _FDT((fdt_pack(fdt)));
if (fdt_totalsize(fdt) + sizeof(hdr) > size) {
g_free(fdt);
trace_spapr_cas_failed(size);
return -1;
}
cpu_physical_memory_write(addr, &hdr, sizeof(hdr)); cpu_physical_memory_write(addr, &hdr, sizeof(hdr));
cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(addr + sizeof(hdr), fdt, fdt_totalsize(fdt));
trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr)); trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
@ -1197,7 +1191,8 @@ static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt)
} }
} }
static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset) static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset,
size_t space)
{ {
MachineState *machine = MACHINE(spapr); MachineState *machine = MACHINE(spapr);
MachineClass *mc = MACHINE_GET_CLASS(machine); MachineClass *mc = MACHINE_GET_CLASS(machine);
@ -1207,8 +1202,8 @@ static void *spapr_build_fdt(SpaprMachineState *spapr, bool reset)
SpaprPhbState *phb; SpaprPhbState *phb;
char *buf; char *buf;
fdt = g_malloc0(FDT_MAX_SIZE); fdt = g_malloc0(space);
_FDT((fdt_create_empty_tree(fdt, FDT_MAX_SIZE))); _FDT((fdt_create_empty_tree(fdt, space)));
/* Root node */ /* Root node */
_FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp")); _FDT(fdt_setprop_string(fdt, 0, "device_type", "chrp"));
@ -1723,19 +1718,13 @@ static void spapr_machine_reset(MachineState *machine)
*/ */
fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE; fdt_addr = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FDT_MAX_SIZE;
fdt = spapr_build_fdt(spapr, true); fdt = spapr_build_fdt(spapr, true, FDT_MAX_SIZE);
rc = fdt_pack(fdt); rc = fdt_pack(fdt);
/* Should only fail if we've built a corrupted tree */ /* Should only fail if we've built a corrupted tree */
assert(rc == 0); assert(rc == 0);
if (fdt_totalsize(fdt) > FDT_MAX_SIZE) {
error_report("FDT too big ! 0x%x bytes (max is 0x%x)",
fdt_totalsize(fdt), FDT_MAX_SIZE);
exit(1);
}
/* Load the fdt */ /* Load the fdt */
qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt));
cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt));