aboutsummaryrefslogtreecommitdiff
path: root/hw/ppc/spapr_hcall.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/ppc/spapr_hcall.c')
-rw-r--r--hw/ppc/spapr_hcall.c55
1 files changed, 52 insertions, 3 deletions
diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c
index 05a7ca275b..0f19be794c 100644
--- a/hw/ppc/spapr_hcall.c
+++ b/hw/ppc/spapr_hcall.c
@@ -1,4 +1,5 @@
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "qapi/error.h"
#include "sysemu/hw_accel.h"
#include "sysemu/runstate.h"
@@ -15,6 +16,7 @@
#include "cpu-models.h"
#include "trace.h"
#include "kvm_ppc.h"
+#include "hw/ppc/fdt.h"
#include "hw/ppc/spapr_ovec.h"
#include "mmu-book3s-v3.h"
#include "hw/mem/memory-device.h"
@@ -1638,6 +1640,26 @@ static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu,
return best_compat;
}
+static bool spapr_hotplugged_dev_before_cas(void)
+{
+ Object *drc_container, *obj;
+ ObjectProperty *prop;
+ ObjectPropertyIterator iter;
+
+ drc_container = container_get(object_get_root(), "/dr-connector");
+ object_property_iter_init(&iter, drc_container);
+ while ((prop = object_property_iter_next(&iter))) {
+ if (!strstart(prop->type, "link<", NULL)) {
+ continue;
+ }
+ obj = object_property_get_link(drc_container, prop->name, NULL);
+ if (spapr_drc_needed(obj)) {
+ return true;
+ }
+ }
+ return false;
+}
+
static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
SpaprMachineState *spapr,
target_ulong opcode,
@@ -1645,6 +1667,8 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
{
/* Working address in data buffer */
target_ulong addr = ppc64_phys_to_real(args[0]);
+ target_ulong fdt_buf = args[1];
+ target_ulong fdt_bufsize = args[2];
target_ulong ov_table;
uint32_t cas_pvr;
SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates;
@@ -1788,16 +1812,41 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu,
spapr_irq_update_active_intc(spapr);
+ if (spapr_hotplugged_dev_before_cas()) {
+ spapr->cas_reboot = true;
+ }
+
if (!spapr->cas_reboot) {
+ void *fdt;
+ SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 };
+
/* If spapr_machine_reset() did not set up a HPT but one is necessary
* (because the guest isn't going to use radix) then set it up here. */
if ((spapr->patb_entry & PATE1_GR) && !guest_radix) {
/* legacy hash or new hash: */
spapr_setup_hpt_and_vrma(spapr);
}
- spapr->cas_reboot =
- (spapr_h_cas_compose_response(spapr, args[1], args[2],
- ov5_updates) != 0);
+
+ if (fdt_bufsize < sizeof(hdr)) {
+ error_report("SLOF provided insufficient CAS buffer "
+ TARGET_FMT_lu " (min: %zu)", fdt_bufsize, sizeof(hdr));
+ exit(EXIT_FAILURE);
+ }
+
+ fdt_bufsize -= sizeof(hdr);
+
+ fdt = spapr_build_fdt(spapr, false, fdt_bufsize);
+ _FDT((fdt_pack(fdt)));
+
+ cpu_physical_memory_write(fdt_buf, &hdr, sizeof(hdr));
+ cpu_physical_memory_write(fdt_buf + sizeof(hdr), fdt,
+ fdt_totalsize(fdt));
+ trace_spapr_cas_continue(fdt_totalsize(fdt) + sizeof(hdr));
+
+ g_free(spapr->fdt_blob);
+ spapr->fdt_size = fdt_totalsize(fdt);
+ spapr->fdt_initial_size = spapr->fdt_size;
+ spapr->fdt_blob = fdt;
}
spapr_ovec_cleanup(ov5_updates);