| 1 | /* |
| 2 | * Emulation of MPIPL (Memory Preserving Initial Program Load), aka fadump |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 5 | */ |
| 6 | |
| 7 | #include "qemu/osdep.h" |
| 8 | #include "qemu/log.h" |
| 9 | #include "qemu/units.h" |
| 10 | #include "system/address-spaces.h" |
| 11 | #include "system/cpus.h" |
| 12 | #include "system/hw_accel.h" |
| 13 | #include "system/memory.h" |
| 14 | #include "system/runstate.h" |
| 15 | #include "hw/ppc/pnv.h" |
| 16 | #include "hw/ppc/pnv_mpipl.h" |
| 17 | #include <math.h> |
| 18 | |
| 19 | #define MDST_TABLE_RELOCATED \ |
| 20 | (pnv->mpipl_state.skiboot_base + MDST_TABLE_OFF) |
| 21 | #define MDDT_TABLE_RELOCATED \ |
| 22 | (pnv->mpipl_state.skiboot_base + MDDT_TABLE_OFF) |
| 23 | #define MDRT_TABLE_RELOCATED \ |
| 24 | (pnv->mpipl_state.skiboot_base + MDRT_TABLE_OFF) |
| 25 | #define PROC_DUMP_RELOCATED \ |
| 26 | (pnv->mpipl_state.skiboot_base + PROC_DUMP_AREA_OFF) |
| 27 | |
| 28 | /* |
| 29 | * Preserve the memory regions as pointed by MDST table |
| 30 | * |
| 31 | * During this, the memory region pointed by entries in MDST, are 'copied' |
| 32 | * as it is to the memory region pointed by corresponding entry in MDDT |
| 33 | * |
| 34 | * Notes: All reads should consider data coming from skiboot as big-endian, |
| 35 | * and data written should also be in big-endian |
| 36 | */ |
| 37 | static bool pnv_mpipl_preserve_mem(PnvMachineState *pnv) |
| 38 | { |
| 39 | g_autofree MdstTableEntry *mdst = g_malloc(MDST_TABLE_SIZE); |
| 40 | g_autofree MddtTableEntry *mddt = g_malloc(MDDT_TABLE_SIZE); |
| 41 | g_autofree MdrtTableEntry *mdrt = g_malloc0(MDRT_TABLE_SIZE); |
| 42 | AddressSpace *default_as = &address_space_memory; |
| 43 | MemTxResult io_result; |
| 44 | MemTxAttrs attrs; |
| 45 | uint64_t src_addr, dest_addr; |
| 46 | uint32_t data_len; |
| 47 | uint64_t num_chunks, chunk_id = 0; |
| 48 | int mdrt_idx = 0; |
| 49 | |
| 50 | /* Mark the memory transactions as privileged memory access */ |
| 51 | attrs.user = 0; |
| 52 | attrs.memory = 1; |
| 53 | |
| 54 | if (pnv->mpipl_state.mdrt_table) { |
| 55 | /* |
| 56 | * MDRT table allocated from some past crash, free the memory to |
| 57 | * prevent memory leak |
| 58 | */ |
| 59 | g_free(pnv->mpipl_state.mdrt_table); |
| 60 | pnv->mpipl_state.num_mdrt_entries = 0; |
| 61 | } |
| 62 | |
| 63 | io_result = address_space_read(default_as, MDST_TABLE_RELOCATED, attrs, |
| 64 | mdst, MDST_TABLE_SIZE); |
| 65 | if (io_result != MEMTX_OK) { |
| 66 | qemu_log_mask(LOG_GUEST_ERROR, |
| 67 | "MPIPL: Failed to read MDST table at: 0x" TARGET_FMT_lx "\n", |
| 68 | MDST_TABLE_RELOCATED); |
| 69 | |
| 70 | return false; |
| 71 | } |
| 72 | |
| 73 | io_result = address_space_read(default_as, MDDT_TABLE_RELOCATED, attrs, |
| 74 | mddt, MDDT_TABLE_SIZE); |
| 75 | if (io_result != MEMTX_OK) { |
| 76 | qemu_log_mask(LOG_GUEST_ERROR, |
| 77 | "MPIPL: Failed to read MDDT table at: 0x" TARGET_FMT_lx "\n", |
| 78 | MDDT_TABLE_RELOCATED); |
| 79 | |
| 80 | return false; |
| 81 | } |
| 82 | |
| 83 | /* Try to read all entries */ |
| 84 | for (int i = 0; i < MDST_MAX_ENTRIES; ++i) { |
| 85 | g_autofree uint8_t *copy_buffer = NULL; |
| 86 | bool is_copy_failed = false; |
| 87 | |
| 88 | /* Considering entry with address and size as 0, as end of table */ |
| 89 | if ((mdst[i].addr == 0) && (mdst[i].size == 0)) { |
| 90 | break; |
| 91 | } |
| 92 | |
| 93 | if (mdst[i].size != mddt[i].size) { |
| 94 | qemu_log_mask(LOG_TRACE, |
| 95 | "Warning: Invalid entry, size mismatch in MDST & MDDT\n"); |
| 96 | continue; |
| 97 | } |
| 98 | |
| 99 | if (mdst[i].data_region != mddt[i].data_region) { |
| 100 | qemu_log_mask(LOG_TRACE, |
| 101 | "Warning: Invalid entry, region mismatch in MDST & MDDT\n"); |
| 102 | continue; |
| 103 | } |
| 104 | |
| 105 | src_addr = be64_to_cpu(mdst[i].addr) & ~HRMOR_BIT; |
| 106 | dest_addr = be64_to_cpu(mddt[i].addr) & ~HRMOR_BIT; |
| 107 | data_len = be32_to_cpu(mddt[i].size); |
| 108 | |
| 109 | #define COPY_CHUNK_SIZE ((size_t)(32 * MiB)) |
| 110 | copy_buffer = g_try_malloc(COPY_CHUNK_SIZE); |
| 111 | if (copy_buffer == NULL) { |
| 112 | qemu_log_mask(LOG_GUEST_ERROR, |
| 113 | "MPIPL: Failed allocating memory (size: %zu) for copying" |
| 114 | " reserved memory regions\n", COPY_CHUNK_SIZE); |
| 115 | is_copy_failed = true; |
| 116 | continue; |
| 117 | } |
| 118 | |
| 119 | chunk_id = 0; |
| 120 | num_chunks = ceil((data_len * 1.0f) / COPY_CHUNK_SIZE); |
| 121 | while (chunk_id < num_chunks) { |
| 122 | /* Take minimum of bytes left to copy, and chunk size */ |
| 123 | uint64_t copy_len = MIN( |
| 124 | data_len - (chunk_id * COPY_CHUNK_SIZE), |
| 125 | COPY_CHUNK_SIZE |
| 126 | ); |
| 127 | |
| 128 | /* Copy the source region to destination */ |
| 129 | io_result = address_space_read(default_as, src_addr, attrs, |
| 130 | copy_buffer, copy_len); |
| 131 | if (io_result != MEMTX_OK) { |
| 132 | qemu_log_mask(LOG_GUEST_ERROR, |
| 133 | "MPIPL: Failed to read region at: 0x%" PRIx64 "\n", |
| 134 | src_addr); |
| 135 | is_copy_failed = true; |
| 136 | break; |
| 137 | } |
| 138 | |
| 139 | io_result = address_space_write(default_as, dest_addr, attrs, |
| 140 | copy_buffer, copy_len); |
| 141 | if (io_result != MEMTX_OK) { |
| 142 | qemu_log_mask(LOG_GUEST_ERROR, |
| 143 | "MPIPL: Failed to write region at: 0x%" PRIx64 "\n", |
| 144 | dest_addr); |
| 145 | is_copy_failed = true; |
| 146 | break; |
| 147 | } |
| 148 | |
| 149 | src_addr += COPY_CHUNK_SIZE; |
| 150 | dest_addr += COPY_CHUNK_SIZE; |
| 151 | ++chunk_id; |
| 152 | } |
| 153 | #undef COPY_CHUNK_SIZE |
| 154 | |
| 155 | if (is_copy_failed) { |
| 156 | /* |
| 157 | * HDAT doesn't specify an error code in MDRT for failed copy, |
| 158 | * and doesn't specify how this is to be handled |
| 159 | * Hence just skip adding an entry in MDRT, as done for size |
| 160 | * mismatch or other inconsistency between MDST/MDDT |
| 161 | */ |
| 162 | continue; |
| 163 | } |
| 164 | |
| 165 | /* Populate entry in MDRT table if preserving successful */ |
| 166 | mdrt[mdrt_idx].src_addr = cpu_to_be64(src_addr); |
| 167 | mdrt[mdrt_idx].dest_addr = cpu_to_be64(dest_addr); |
| 168 | mdrt[mdrt_idx].size = cpu_to_be32(data_len); |
| 169 | mdrt[mdrt_idx].data_region = mdst[i].data_region; |
| 170 | ++mdrt_idx; |
| 171 | } |
| 172 | |
| 173 | pnv->mpipl_state.mdrt_table = g_steal_pointer(&mdrt); |
| 174 | pnv->mpipl_state.num_mdrt_entries = mdrt_idx; |
| 175 | |
| 176 | return true; |
| 177 | } |
| 178 | |
| 179 | static void do_store_cpu_regs(CPUState *cpu, MpiplPreservedCPUState *state) |
| 180 | { |
| 181 | CPUPPCState *env = cpu_env(cpu); |
| 182 | MpiplRegDataHdr *regs_hdr = &state->hdr; |
| 183 | MpiplRegEntry *reg_entries = state->reg_entries; |
| 184 | MpiplRegEntry *curr_reg_entry; |
| 185 | uint32_t num_saved_regs = 0; |
| 186 | |
| 187 | cpu_synchronize_state(cpu); |
| 188 | |
| 189 | regs_hdr->pir = cpu_to_be32(env->spr[SPR_PIR]); |
| 190 | |
| 191 | /* QEMU CPUs are not in Power Saving Mode */ |
| 192 | regs_hdr->core_state = 0xff; |
| 193 | |
| 194 | regs_hdr->off_regentries = 0; |
| 195 | regs_hdr->num_regentries = cpu_to_be32(NUM_REGS_PER_CPU); |
| 196 | |
| 197 | regs_hdr->alloc_size = cpu_to_be32(sizeof(MpiplRegEntry)); |
| 198 | regs_hdr->act_size = cpu_to_be32(sizeof(MpiplRegEntry)); |
| 199 | |
| 200 | #define REG_TYPE_GPR 0x1 |
| 201 | #define REG_TYPE_SPR 0x2 |
| 202 | #define REG_TYPE_TIMA 0x3 |
| 203 | |
| 204 | /* |
| 205 | * ID numbers used by f/w while populating certain registers |
| 206 | * |
| 207 | * Copied these defines from the linux kernel |
| 208 | */ |
| 209 | #define REG_ID_NIP 0x7D0 |
| 210 | #define REG_ID_MSR 0x7D1 |
| 211 | #define REG_ID_CCR 0x7D2 |
| 212 | |
| 213 | curr_reg_entry = reg_entries; |
| 214 | |
| 215 | #define REG_ENTRY(type, num, val) \ |
| 216 | do { \ |
| 217 | curr_reg_entry->reg_type = cpu_to_be32(type); \ |
| 218 | curr_reg_entry->reg_num = cpu_to_be32(num); \ |
| 219 | curr_reg_entry->reg_val = cpu_to_be64(val); \ |
| 220 | ++curr_reg_entry; \ |
| 221 | ++num_saved_regs; \ |
| 222 | } while (0) |
| 223 | |
| 224 | /* Save the GPRs */ |
| 225 | for (int gpr_id = 0; gpr_id < 32; ++gpr_id) { |
| 226 | REG_ENTRY(REG_TYPE_GPR, gpr_id, env->gpr[gpr_id]); |
| 227 | } |
| 228 | |
| 229 | REG_ENTRY(REG_TYPE_SPR, SPR_ACOP, env->spr[SPR_ACOP]); |
| 230 | REG_ENTRY(REG_TYPE_SPR, SPR_AMR, env->spr[SPR_AMR]); |
| 231 | REG_ENTRY(REG_TYPE_SPR, SPR_BESCR, env->spr[SPR_BESCR]); |
| 232 | REG_ENTRY(REG_TYPE_SPR, SPR_CFAR, env->spr[SPR_CFAR]); |
| 233 | REG_ENTRY(REG_TYPE_SPR, SPR_CIABR, env->spr[SPR_CIABR]); |
| 234 | |
| 235 | REG_ENTRY(REG_TYPE_SPR, SPR_CTR, env->spr[SPR_CTR]); |
| 236 | REG_ENTRY(REG_TYPE_SPR, SPR_CTRL, env->spr[SPR_CTRL]); |
| 237 | REG_ENTRY(REG_TYPE_SPR, SPR_DABR, env->spr[SPR_DABR]); |
| 238 | REG_ENTRY(REG_TYPE_SPR, SPR_DABRX, env->spr[SPR_DABRX]); |
| 239 | REG_ENTRY(REG_TYPE_SPR, SPR_DAR, env->spr[SPR_DAR]); |
| 240 | REG_ENTRY(REG_TYPE_SPR, SPR_DAWR0, env->spr[SPR_DAWR0]); |
| 241 | REG_ENTRY(REG_TYPE_SPR, SPR_DAWR1, env->spr[SPR_DAWR1]); |
| 242 | REG_ENTRY(REG_TYPE_SPR, SPR_DAWRX0, env->spr[SPR_DAWRX0]); |
| 243 | REG_ENTRY(REG_TYPE_SPR, SPR_DAWRX1, env->spr[SPR_DAWRX1]); |
| 244 | REG_ENTRY(REG_TYPE_SPR, SPR_DPDES, env->spr[SPR_DPDES]); |
| 245 | REG_ENTRY(REG_TYPE_SPR, SPR_DSCR, env->spr[SPR_DSCR]); |
| 246 | REG_ENTRY(REG_TYPE_SPR, SPR_DSISR, env->spr[SPR_DSISR]); |
| 247 | REG_ENTRY(REG_TYPE_SPR, SPR_EBBHR, env->spr[SPR_EBBHR]); |
| 248 | REG_ENTRY(REG_TYPE_SPR, SPR_EBBRR, env->spr[SPR_EBBRR]); |
| 249 | |
| 250 | REG_ENTRY(REG_TYPE_SPR, SPR_FSCR, env->spr[SPR_FSCR]); |
| 251 | |
| 252 | REG_ENTRY(REG_TYPE_SPR, SPR_CTR, env->ctr); |
| 253 | REG_ENTRY(REG_TYPE_SPR, SPR_DAR, env->spr[SPR_DAR]); |
| 254 | REG_ENTRY(REG_TYPE_SPR, SPR_DSISR, env->spr[SPR_DSISR]); |
| 255 | REG_ENTRY(REG_TYPE_SPR, SPR_LR, env->lr); |
| 256 | REG_ENTRY(REG_TYPE_SPR, REG_ID_MSR, env->msr); |
| 257 | REG_ENTRY(REG_TYPE_SPR, REG_ID_NIP, env->nip); |
| 258 | REG_ENTRY(REG_TYPE_SPR, SPR_XER, env->xer); |
| 259 | REG_ENTRY(REG_TYPE_SPR, SPR_SRR0, env->spr[SPR_SRR0]); |
| 260 | REG_ENTRY(REG_TYPE_SPR, SPR_SRR1, env->spr[SPR_SRR1]); |
| 261 | REG_ENTRY(REG_TYPE_SPR, SPR_HSRR0, env->spr[SPR_HSRR0]); |
| 262 | REG_ENTRY(REG_TYPE_SPR, SPR_HSRR1, env->spr[SPR_HSRR1]); |
| 263 | REG_ENTRY(REG_TYPE_SPR, SPR_CFAR, env->spr[SPR_CFAR]); |
| 264 | REG_ENTRY(REG_TYPE_SPR, SPR_HMER, env->spr[SPR_HMER]); |
| 265 | REG_ENTRY(REG_TYPE_SPR, SPR_HMEER, env->spr[SPR_HMEER]); |
| 266 | |
| 267 | /* |
| 268 | * Ensure the number of registers saved match the number of |
| 269 | * registers per cpu |
| 270 | * |
| 271 | * This will help catch an error if in future a new register entry |
| 272 | * is added/removed while not modifying NUM_PER_CPU_REGS |
| 273 | */ |
| 274 | assert(num_saved_regs == NUM_REGS_PER_CPU); |
| 275 | } |
| 276 | |
| 277 | static bool pnv_mpipl_preserve_cpu_state(PnvMachineState *pnv) |
| 278 | { |
| 279 | MachineState *machine = MACHINE(pnv); |
| 280 | uint32_t num_cpus = machine->smp.cpus; |
| 281 | MpiplPreservedCPUState *state; |
| 282 | CPUState *cpu; |
| 283 | AddressSpace *default_as = &address_space_memory; |
| 284 | MemTxResult io_result; |
| 285 | MemTxAttrs attrs; |
| 286 | |
| 287 | /* Mark the memory transactions as privileged memory access */ |
| 288 | attrs.user = 0; |
| 289 | attrs.memory = 1; |
| 290 | |
| 291 | if (pnv->mpipl_state.cpu_states) { |
| 292 | /* |
| 293 | * CPU States might have been allocated from some past crash, free the |
| 294 | * memory to preven memory leak |
| 295 | */ |
| 296 | g_free(pnv->mpipl_state.cpu_states); |
| 297 | pnv->mpipl_state.num_cpu_states = 0; |
| 298 | } |
| 299 | |
| 300 | pnv->mpipl_state.cpu_states = g_malloc_n(num_cpus, |
| 301 | sizeof(MpiplPreservedCPUState)); |
| 302 | pnv->mpipl_state.num_cpu_states = num_cpus; |
| 303 | |
| 304 | state = pnv->mpipl_state.cpu_states; |
| 305 | |
| 306 | /* Preserve the Processor Dump Area */ |
| 307 | io_result = address_space_read(default_as, PROC_DUMP_RELOCATED, attrs, |
| 308 | &pnv->mpipl_state.proc_area, sizeof(MpiplProcDumpArea)); |
| 309 | if (io_result != MEMTX_OK) { |
| 310 | qemu_log_mask(LOG_GUEST_ERROR, |
| 311 | "MPIPL: Failed to read Proc Dump Area at: 0x" TARGET_FMT_lx "\n", |
| 312 | PROC_DUMP_RELOCATED); |
| 313 | |
| 314 | return false; |
| 315 | } |
| 316 | |
| 317 | CPU_FOREACH(cpu) { |
| 318 | do_store_cpu_regs(cpu, state); |
| 319 | ++state; |
| 320 | } |
| 321 | |
| 322 | return true; |
| 323 | } |
| 324 | |
| 325 | /* |
| 326 | * Write the preserved CPU state data in Processor Dump Area (PROC_DUMP_AREA) |
| 327 | * |
| 328 | * Returns true if everything went fine, else false for any error |
| 329 | */ |
| 330 | static bool pnv_mpipl_write_cpu_state(PnvMachineState *pnv) |
| 331 | { |
| 332 | MpiplProcDumpArea *proc_area = &pnv->mpipl_state.proc_area; |
| 333 | MpiplPreservedCPUState *cpu_state = pnv->mpipl_state.cpu_states; |
| 334 | const uint32_t num_cpu_states = pnv->mpipl_state.num_cpu_states; |
| 335 | hwaddr next_regentries_hdr; |
| 336 | AddressSpace *default_as = &address_space_memory; |
| 337 | MemTxResult io_result; |
| 338 | MemTxAttrs attrs; |
| 339 | |
| 340 | /* Mark the memory transactions as privileged memory access */ |
| 341 | attrs.user = 0; |
| 342 | attrs.memory = 1; |
| 343 | |
| 344 | if (be32_to_cpu(proc_area->alloc_size) < |
| 345 | (num_cpu_states * sizeof(MpiplPreservedCPUState))) { |
| 346 | qemu_log_mask(LOG_GUEST_ERROR, |
| 347 | "MPIPL: Size of buffer allocate by skiboot (%u bytes) is not" |
| 348 | "enough to save all CPUs registers needed (%zu bytes)", |
| 349 | be32_to_cpu(proc_area->alloc_size), |
| 350 | num_cpu_states * sizeof(MpiplPreservedCPUState)); |
| 351 | |
| 352 | return false; |
| 353 | } |
| 354 | |
| 355 | proc_area->version = PROC_DUMP_AREA_VERSION_P9; |
| 356 | |
| 357 | /* |
| 358 | * This is the stride kernel/firmware should use to jump from a |
| 359 | * register entries header to next CPU's header |
| 360 | */ |
| 361 | proc_area->thread_size = cpu_to_be32(sizeof(MpiplPreservedCPUState)); |
| 362 | |
| 363 | /* Write the header and register entries for each CPU */ |
| 364 | next_regentries_hdr = be64_to_cpu(proc_area->alloc_addr) & (~HRMOR_BIT); |
| 365 | for (int i = 0; i < num_cpu_states; ++i) { |
| 366 | io_result = address_space_write(default_as, next_regentries_hdr, attrs, |
| 367 | &cpu_state->hdr, sizeof(MpiplRegDataHdr)); |
| 368 | if (io_result != MEMTX_OK) { |
| 369 | qemu_log_mask(LOG_GUEST_ERROR, |
| 370 | "MPIPL: Failed to write RegEntries Header\n"); |
| 371 | return false; |
| 372 | } |
| 373 | |
| 374 | io_result = address_space_write(default_as, |
| 375 | next_regentries_hdr + sizeof(MpiplRegDataHdr), attrs, |
| 376 | &cpu_state->reg_entries, |
| 377 | NUM_REGS_PER_CPU * (sizeof(MpiplRegEntry))); |
| 378 | if (io_result != MEMTX_OK) { |
| 379 | qemu_log_mask(LOG_GUEST_ERROR, |
| 380 | "MPIPL: Failed to write Register Entries\n"); |
| 381 | return false; |
| 382 | } |
| 383 | |
| 384 | /* |
| 385 | * According to HDAT section: |
| 386 | * "15.3.1.5 Architected Register Data content": |
| 387 | * |
| 388 | * The next register entries header will be at current header + |
| 389 | * "Thread Register State Entry size" |
| 390 | * |
| 391 | * Note: proc_area.thread_size == sizeof(MpiplPreservedCPUState) |
| 392 | */ |
| 393 | next_regentries_hdr += sizeof(MpiplPreservedCPUState); |
| 394 | ++cpu_state; |
| 395 | } |
| 396 | |
| 397 | /* Point the destination address to the preserved memory region */ |
| 398 | proc_area->dest_addr = proc_area->alloc_addr; |
| 399 | proc_area->act_size = cpu_to_be32(num_cpu_states * |
| 400 | sizeof(MpiplPreservedCPUState)); |
| 401 | |
| 402 | io_result = address_space_write(default_as, PROC_DUMP_AREA_OFF, attrs, |
| 403 | proc_area, sizeof(MpiplProcDumpArea)); |
| 404 | if (io_result != MEMTX_OK) { |
| 405 | qemu_log_mask(LOG_GUEST_ERROR, |
| 406 | "MPIPL: Failed to write Register Entries\n"); |
| 407 | return false; |
| 408 | } |
| 409 | |
| 410 | return true; |
| 411 | } |
| 412 | |
| 413 | /* |
| 414 | * Write the preserved MDRT table, representing preserved memory regions |
| 415 | * |
| 416 | * Returns true if everything went fine, else false for any error |
| 417 | */ |
| 418 | static bool pnv_mpipl_write_mdrt(PnvMachineState *pnv) |
| 419 | { |
| 420 | MpiplPreservedState *state = &pnv->mpipl_state; |
| 421 | AddressSpace *default_as = &address_space_memory; |
| 422 | MemTxResult io_result; |
| 423 | MemTxAttrs attrs; |
| 424 | |
| 425 | /* Mark the memory transactions as privileged memory access */ |
| 426 | attrs.user = 0; |
| 427 | attrs.memory = 1; |
| 428 | |
| 429 | /* |
| 430 | * Generally writes from platform during MPIPL don't go to a relocated |
| 431 | * skiboot address |
| 432 | * |
| 433 | * Though for MDRT we are doing so, as this is the address skiboot |
| 434 | * considers by default for MDRT |
| 435 | * |
| 436 | * MDRT/MDST/MDDT base addresses are actually meant to be shared by |
| 437 | * platform in SPIRA structures. |
| 438 | * |
| 439 | * Not implementing SPIRA as it increases complexity for no gains. |
| 440 | * Using the default address skiboot expects for MDRT, which is the |
| 441 | * relocated MDRT, hence writing to it |
| 442 | * |
| 443 | * Other tables like MDST/MDDT should not be written to relocated |
| 444 | * addresses, as skiboot will overwrite anything from SKIBOOT_BASE till |
| 445 | * SKIBOOT_BASE+SKIBOOT_SIZE (which is 0x30000000-0x31c00000 by default) |
| 446 | */ |
| 447 | io_result = address_space_write(default_as, MDRT_TABLE_RELOCATED, attrs, |
| 448 | state->mdrt_table, |
| 449 | state->num_mdrt_entries * sizeof(MdrtTableEntry)); |
| 450 | if (io_result != MEMTX_OK) { |
| 451 | qemu_log_mask(LOG_GUEST_ERROR, "MPIPL: Failed to write MDRT table\n"); |
| 452 | return false; |
| 453 | } |
| 454 | |
| 455 | return true; |
| 456 | } |
| 457 | |
| 458 | void do_mpipl_preserve(PnvMachineState *pnv) |
| 459 | { |
| 460 | pause_all_vcpus(); |
| 461 | |
| 462 | pnv_mpipl_preserve_mem(pnv); |
| 463 | pnv_mpipl_preserve_cpu_state(pnv); |
| 464 | |
| 465 | /* Mark next boot as Memory-preserving boot */ |
| 466 | pnv->mpipl_state.is_next_boot_mpipl = true; |
| 467 | |
| 468 | /* |
| 469 | * Do a guest reset. |
| 470 | * Next reset will see 'is_next_boot_mpipl' as true, and trigger MPIPL |
| 471 | * |
| 472 | * Requirement: |
| 473 | * GUEST_RESET is expected to NOT clear the memory, as is the case when |
| 474 | * this is merged |
| 475 | */ |
| 476 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 477 | } |
| 478 | |
| 479 | bool do_mpipl_write(PnvMachineState *pnv) |
| 480 | { |
| 481 | return pnv_mpipl_write_mdrt(pnv) && pnv_mpipl_write_cpu_state(pnv); |
| 482 | } |