1 /*
2 * SPAPR machine hooks to Virtual Open Firmware,
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 #include "qemu/osdep.h"
7 #include "qapi/error.h"
8 #include "hw/ppc/spapr.h"
9 #include "hw/ppc/spapr_vio.h"
10 #include "hw/ppc/spapr_cpu_core.h"
11 #include "hw/ppc/fdt.h"
12 #include "hw/ppc/vof.h"
13 #include "sysemu/sysemu.h"
14 #include "qom/qom-qobject.h"
15 #include "trace.h"
16
spapr_h_vof_client(PowerPCCPU * cpu,SpaprMachineState * spapr,target_ulong opcode,target_ulong * _args)17 target_ulong spapr_h_vof_client(PowerPCCPU *cpu, SpaprMachineState *spapr,
18 target_ulong opcode, target_ulong *_args)
19 {
20 int ret = vof_client_call(MACHINE(spapr), spapr->vof, spapr->fdt_blob,
21 ppc64_phys_to_real(_args[0]));
22
23 if (ret) {
24 return H_PARAMETER;
25 }
26 return H_SUCCESS;
27 }
28
spapr_vof_client_dt_finalize(SpaprMachineState * spapr,void * fdt)29 void spapr_vof_client_dt_finalize(SpaprMachineState *spapr, void *fdt)
30 {
31 g_autofree char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus);
32
33 vof_build_dt(fdt, spapr->vof);
34
35 if (spapr->vof->bootargs) {
36 int chosen;
37
38 _FDT(chosen = fdt_path_offset(fdt, "/chosen"));
39 /*
40 * If the client did not change "bootargs", spapr_dt_chosen() must have
41 * stored machine->kernel_cmdline in it before getting here.
42 */
43 _FDT(fdt_setprop_string(fdt, chosen, "bootargs", spapr->vof->bootargs));
44 }
45
46 /*
47 * SLOF-less setup requires an open instance of stdout for early
48 * kernel printk. By now all phandles are settled so we can open
49 * the default serial console.
50 */
51 if (stdout_path) {
52 _FDT(vof_client_open_store(fdt, spapr->vof, "/chosen", "stdout",
53 stdout_path));
54 }
55 }
56
spapr_vof_reset(SpaprMachineState * spapr,void * fdt,Error ** errp)57 void spapr_vof_reset(SpaprMachineState *spapr, void *fdt, Error **errp)
58 {
59 target_ulong stack_ptr;
60 Vof *vof = spapr->vof;
61 PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu);
62
63 vof_init(vof, spapr->rma_size, errp);
64
65 stack_ptr = vof_claim(vof, 0, VOF_STACK_SIZE, VOF_STACK_SIZE);
66 if (stack_ptr == -1) {
67 error_setg(errp, "Memory allocation for stack failed");
68 return;
69 }
70 /* Stack grows downwards plus reserve space for the minimum stack frame */
71 stack_ptr += VOF_STACK_SIZE - 0x20;
72
73 if (spapr->kernel_size &&
74 vof_claim(vof, spapr->kernel_addr, spapr->kernel_size, 0) == -1) {
75 error_setg(errp, "Memory for kernel is in use");
76 return;
77 }
78
79 if (spapr->initrd_size &&
80 vof_claim(vof, spapr->initrd_base, spapr->initrd_size, 0) == -1) {
81 error_setg(errp, "Memory for initramdisk is in use");
82 return;
83 }
84
85 spapr_vof_client_dt_finalize(spapr, fdt);
86
87 spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT,
88 stack_ptr, spapr->initrd_base,
89 spapr->initrd_size);
90
91 /*
92 * At this point the expected allocation map is:
93 *
94 * 0..c38 - the initial firmware
95 * 8000..10000 - stack
96 * 400000.. - kernel
97 * 3ea0000.. - initramdisk
98 *
99 * We skip writing FDT as nothing expects it; OF client interface is
100 * going to be used for reading the device tree.
101 */
102 }
103
spapr_vof_quiesce(MachineState * ms)104 void spapr_vof_quiesce(MachineState *ms)
105 {
106 SpaprMachineState *spapr = SPAPR_MACHINE(ms);
107
108 spapr->fdt_size = fdt_totalsize(spapr->fdt_blob);
109 spapr->fdt_initial_size = spapr->fdt_size;
110 }
111
spapr_vof_setprop(MachineState * ms,const char * path,const char * propname,void * val,int vallen)112 bool spapr_vof_setprop(MachineState *ms, const char *path, const char *propname,
113 void *val, int vallen)
114 {
115 SpaprMachineState *spapr = SPAPR_MACHINE(ms);
116
117 /*
118 * We only allow changing properties which we know how to update in QEMU
119 * OR
120 * the ones which we know that they need to survive during "quiesce".
121 */
122
123 if (strcmp(path, "/rtas") == 0) {
124 if (strcmp(propname, "linux,rtas-base") == 0 ||
125 strcmp(propname, "linux,rtas-entry") == 0) {
126 /* These need to survive quiesce so let them store in the FDT */
127 return true;
128 }
129 }
130
131 if (strcmp(path, "/chosen") == 0) {
132 if (strcmp(propname, "bootargs") == 0) {
133 Vof *vof = spapr->vof;
134
135 g_free(vof->bootargs);
136 vof->bootargs = g_strndup(val, vallen);
137 return true;
138 }
139 if (strcmp(propname, "linux,initrd-start") == 0) {
140 if (vallen == sizeof(uint32_t)) {
141 spapr->initrd_base = ldl_be_p(val);
142 return true;
143 }
144 if (vallen == sizeof(uint64_t)) {
145 spapr->initrd_base = ldq_be_p(val);
146 return true;
147 }
148 return false;
149 }
150 if (strcmp(propname, "linux,initrd-end") == 0) {
151 if (vallen == sizeof(uint32_t)) {
152 spapr->initrd_size = ldl_be_p(val) - spapr->initrd_base;
153 return true;
154 }
155 if (vallen == sizeof(uint64_t)) {
156 spapr->initrd_size = ldq_be_p(val) - spapr->initrd_base;
157 return true;
158 }
159 return false;
160 }
161 }
162
163 return true;
164 }
165