From nobody Wed Apr 2 14:11:37 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=reject dis=none) header.from=linux.ibm.com ARC-Seal: i=1; a=rsa-sha256; t=1742751947; cv=none; d=zohomail.com; s=zohoarc; b=XH8OGtBGUNkbXo04KuUK/Dbv3kmhmQdC8+2OGM2ARUaZYitv1OwvtSEZWnyiYRCA/9tDtS/fL/+1IZpf32JPzM6nMmvb7bEnMK3U5SADRoCia8ngx3smlcsEXeFjvVK/UsJWMC7rpIQUMmr5QTq1RWH86Zrbjk/ZNEWa8TctOE0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1742751947; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=AyZxE2LT0Z01zBqX6uhoXVSAR1Sh/o0BK9cs9uYJJ7s=; b=BZFx79dh6FkIcjmlQfJB2rIbEQWnPykjTSWuKMiNEZlp37QOPkDyJr5fc0IzeElB6e6FOsPt0MVDnDu1sIgumitt3o+ojXfTu14kmHSzb4jUFW4MqFGzcE1WaVUr/IhoMdGay/1/uYo81YwqHsMIjMTE4fmgnMGTlkTlj7lm/C4= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1742751947819889.214805676533; Sun, 23 Mar 2025 10:45:47 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1twPKd-0003Qj-6p; Sun, 23 Mar 2025 13:41:55 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1twPJM-0001Jm-Pw; Sun, 23 Mar 2025 13:40:50 -0400 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1twPJJ-0003RW-2H; Sun, 23 Mar 2025 13:40:35 -0400 Received: from pps.filterd (m0356516.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.18.1.2/8.18.1.2) with ESMTP id 52N1dBTe024020; Sun, 23 Mar 2025 17:40:31 GMT Received: from pps.reinject (localhost [127.0.0.1]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 45j4cp314j-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sun, 23 Mar 2025 17:40:30 +0000 (GMT) Received: from m0356516.ppops.net (m0356516.ppops.net [127.0.0.1]) by pps.reinject (8.18.0.8/8.18.0.8) with ESMTP id 52NHaQK2028901; Sun, 23 Mar 2025 17:40:30 GMT Received: from ppma12.dal12v.mail.ibm.com (dc.9e.1632.ip4.static.sl-reverse.com [50.22.158.220]) by mx0a-001b2d01.pphosted.com (PPS) with ESMTPS id 45j4cp314e-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sun, 23 Mar 2025 17:40:30 +0000 (GMT) Received: from pps.filterd (ppma12.dal12v.mail.ibm.com [127.0.0.1]) by ppma12.dal12v.mail.ibm.com (8.18.1.2/8.18.1.2) with ESMTP id 52NG8txN030299; Sun, 23 Mar 2025 17:40:29 GMT Received: from smtprelay03.fra02v.mail.ibm.com ([9.218.2.224]) by ppma12.dal12v.mail.ibm.com (PPS) with ESMTPS id 45j7ht2vaa-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Sun, 23 Mar 2025 17:40:29 +0000 Received: from smtpav04.fra02v.mail.ibm.com (smtpav04.fra02v.mail.ibm.com [10.20.54.103]) by smtprelay03.fra02v.mail.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id 52NHeQo349217964 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Sun, 23 Mar 2025 17:40:26 GMT Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 0D60420043; Sun, 23 Mar 2025 17:40:26 +0000 (GMT) Received: from smtpav04.fra02v.mail.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5E0E020040; Sun, 23 Mar 2025 17:40:23 +0000 (GMT) Received: from li-3c92a0cc-27cf-11b2-a85c-b804d9ca68fa.ibm.com (unknown [9.124.214.162]) by smtpav04.fra02v.mail.ibm.com (Postfix) with ESMTP; Sun, 23 Mar 2025 17:40:23 +0000 (GMT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=ibm.com; h=cc :content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=pp1; bh=AyZxE2LT0Z01zBqX6 uhoXVSAR1Sh/o0BK9cs9uYJJ7s=; b=bLiw9DB5Sl1z5r+PUhdvvb1oZWNpuVezn orXMhjVKodQbbnVp7Zbtkf2agxGNaGtkbYP/MjI10jHBMteiO7OavZ7vUihCdeMs jwhiyssTQZNXVrHxUQ/dvNtOfzglXjh8IvaDV6+9slPcMtRVcqHzAs465yWS70dR w0oCt7zfjEva64AajT4uBTTavNH3/JibmRS4gDMByticFQ1FKx9Ai9edGqgo0xuu N4Ud5d9HpCwyZkrQKWWAnbg6hUkGS9P6Y13n/uM/IPo16hPWATceFD/fjMrpc4JO KMmPVYMS3dtrhKfAWBTkJZjnjgB3Kb1Tg06Y+6GXOqMJmQpfqQeHQ== From: Aditya Gupta To: Cc: , Nicholas Piggin , Daniel Henrique Barboza , Harsh Prateek Bora , Sourabh Jain , Mahesh J Salgaonkar , Hari Bathini Subject: [PATCH v4 5/8] hw/ppc: Implement saving CPU state in Fadump Date: Sun, 23 Mar 2025 23:10:04 +0530 Message-ID: <20250323174007.221116-6-adityag@linux.ibm.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20250323174007.221116-1-adityag@linux.ibm.com> References: <20250323174007.221116-1-adityag@linux.ibm.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-TM-AS-GCONF: 00 X-Proofpoint-ORIG-GUID: ZS_iFJdW9KaULZDOuU5nM8GvGJ_XBvrw X-Proofpoint-GUID: K8_zKEGTIbdoa4df1LLEQFKIaxh0fk3Y X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1093,Hydra:6.0.680,FMLib:17.12.68.34 definitions=2025-03-23_08,2025-03-21_01,2024-11-22_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 bulkscore=0 mlxlogscore=999 impostorscore=0 priorityscore=1501 malwarescore=0 suspectscore=0 spamscore=0 adultscore=0 clxscore=1015 mlxscore=0 lowpriorityscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2502280000 definitions=main-2503230123 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=148.163.158.5; envelope-from=adityag@linux.ibm.com; helo=mx0b-001b2d01.pphosted.com X-Spam_score_int: -26 X-Spam_score: -2.7 X-Spam_bar: -- X-Spam_report: (-2.7 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H2=-0.01, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, RCVD_IN_VALIDITY_SAFE_BLOCKED=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @ibm.com) X-ZM-MESSAGEID: 1742751948137116600 Content-Type: text/plain; charset="utf-8" Kernel expects CPU states/register states in the format mentioned in "Register Save Area" in PAPR. The platform (in our case, QEMU) saves each CPU register in the form of an array of "register entries", the start and end of this array is signified by "CPUSTRT" and "CPUEND" register entries respectively. The CPUSTRT and CPUEND register entry also has 4-byte logical CPU ID, thus storing the CPU ID corresponding to the array of register entries. Each register, and CPUSTRT, CPUEND has a predefined identifier. Implement calculating identifier for a given register in 'fadump_str_to_u64', which has been taken from the linux kernel Similarly GPRs also have predefined identifiers, and a corresponding 64-bit resiter value (split into two 32-bit cells). Implement calculation of GPR identifiers with 'fadump_gpr_id_to_u64' PAPR has restrictions on particular order of few registers, and is free to be in any order for other registers. Some registers mentioned in PAPR have not been exported as they are not implemented in QEMU / don't make sense in QEMU. Implement saving of CPU state according to the PAPR document Note: As of this patch, the "kernel-dump" device tree entry is still not added for the second boot, so after crash, the second kernel will boot assuming fadump dump is "NOT" active, and try to register for fadump, but since we already have fadump registered in QEMU internal state, the register rtas call will fail with: "DUMP ACTIVE" Signed-off-by: Aditya Gupta --- hw/ppc/spapr_fadump.c | 336 +++++++++++++++++++++++++++++++++- include/hw/ppc/spapr_fadump.h | 28 +++ 2 files changed, 363 insertions(+), 1 deletion(-) diff --git a/hw/ppc/spapr_fadump.c b/hw/ppc/spapr_fadump.c index c105b8d21da5..fc764b46e726 100644 --- a/hw/ppc/spapr_fadump.c +++ b/hw/ppc/spapr_fadump.c @@ -8,6 +8,71 @@ #include "qemu/log.h" #include "hw/ppc/spapr.h" #include "system/cpus.h" +#include "system/hw_accel.h" + +/* + * Copy the ascii values for first 8 characters from a string into u64 + * variable at their respective indexes. + * e.g. + * The string "FADMPINF" will be converted into 0x4641444d50494e46 + */ +static uint64_t fadump_str_to_u64(const char *str) +{ + uint64_t val =3D 0; + int i; + + for (i =3D 0; i < sizeof(val); i++) { + val =3D (*str) ? (val << 8) | *str++ : val << 8; + } + return val; +} + +/** + * Get the identifier id for register entries of GPRs + * + * It gives the same id as 'fadump_str_to_u64' when the complete string id + * of the GPR is given, ie. + * + * fadump_str_to_u64("GPR05") =3D=3D fadump_gpr_id_to_u64(5); + * fadump_str_to_u64("GPR12") =3D=3D fadump_gpr_id_to_u64(12); + * + * And so on. Hence this can be implemented by creating a dynamic + * string for each GPR, such as "GPR00", "GPR01", ... "GPR31" + * Instead of allocating a string, an observation from the math of + * 'fadump_str_to_u64' or from PAPR tells us that there's a pattern + * in the identifier IDs, such that the first 4 bytes are affected only by + * whether it is GPR0*, GPR1*, GPR2*, GPR3*. + * Upper half of 5th byte is always 0x3. Lower half (nibble) of 5th byte + * is the tens digit of the GPR id, ie. GPR ID / 10. + * Upper half of 6th byte is always 0x3. Lower half (nibble) of 5th byte + * is the ones digit of the GPR id, ie. GPR ID % 10 + * + * For example, for GPR 29, the 5th and 6th byte will be 0x32 and 0x39 + */ +static uint64_t fadump_gpr_id_to_u64(uint32_t gpr_id) +{ + uint64_t val =3D 0; + + /* Valid range of GPR id is only GPR0 to GPR31 */ + assert(gpr_id < 32); + + /* Below calculations set the 0th to 5th byte */ + if (gpr_id <=3D 9) { + val =3D fadump_str_to_u64("GPR0"); + } else if (gpr_id <=3D 19) { + val =3D fadump_str_to_u64("GPR1"); + } else if (gpr_id <=3D 29) { + val =3D fadump_str_to_u64("GPR2"); + } else { + val =3D fadump_str_to_u64("GPR3"); + } + + /* Set the 6th byte */ + val |=3D 0x30000000; + val |=3D ((gpr_id % 10) << 24); + + return val; +} =20 /* * Handle the "FADUMP_CMD_REGISTER" command in 'ibm,configure-kernel-dump' @@ -221,14 +286,221 @@ static bool do_preserve_region(FadumpSection *region) return true; } =20 +/* + * Populate the passed CPUs register entries, in the buffer starting at + * the argument 'curr_reg_entry' + * + * The register entries is an array of pair of register id and register + * value, as described in Table 591/592 in section "H.1 Register Save Area" + * in PAPR v2.13 + * + * Returns pointer just past this CPU's register entries, which can be used + * as the start address for next CPU's register entries + */ +static FadumpRegEntry *populate_cpu_reg_entries(CPUState *cpu, + FadumpRegEntry *curr_reg_entry) +{ + CPUPPCState *env; + PowerPCCPU *ppc_cpu; + uint32_t num_regs_per_cpu =3D 0; + + ppc_cpu =3D POWERPC_CPU(cpu); + env =3D cpu_env(cpu); + num_regs_per_cpu =3D 0; + + curr_reg_entry->reg_id =3D + cpu_to_be64(fadump_str_to_u64("CPUSTRT")); + curr_reg_entry->reg_value =3D ppc_cpu->vcpu_id; + ++curr_reg_entry; + +#define REG_ENTRY(id, val) \ + do { \ + curr_reg_entry->reg_id =3D \ + cpu_to_be64(fadump_str_to_u64(#id)); \ + curr_reg_entry->reg_value =3D cpu_to_be64(val); \ + ++curr_reg_entry; \ + ++num_regs_per_cpu; \ + } while (0) + + REG_ENTRY(ACOP, env->spr[SPR_ACOP]); + REG_ENTRY(AMR, env->spr[SPR_AMR]); + REG_ENTRY(BESCR, env->spr[SPR_BESCR]); + REG_ENTRY(CFAR, env->spr[SPR_CFAR]); + REG_ENTRY(CIABR, env->spr[SPR_CIABR]); + + /* Save the condition register */ + REG_ENTRY(CR, ppc_get_cr(env)); + + REG_ENTRY(CTR, env->spr[SPR_CTR]); + REG_ENTRY(CTRL, env->spr[SPR_CTRL]); + REG_ENTRY(DABR, env->spr[SPR_DABR]); + REG_ENTRY(DABRX, env->spr[SPR_DABRX]); + REG_ENTRY(DAR, env->spr[SPR_DAR]); + REG_ENTRY(DAWR0, env->spr[SPR_DAWR0]); + REG_ENTRY(DAWR1, env->spr[SPR_DAWR1]); + REG_ENTRY(DAWRX0, env->spr[SPR_DAWRX0]); + REG_ENTRY(DAWRX1, env->spr[SPR_DAWRX1]); + REG_ENTRY(DPDES, env->spr[SPR_DPDES]); + REG_ENTRY(DSCR, env->spr[SPR_DSCR]); + REG_ENTRY(DSISR, env->spr[SPR_DSISR]); + REG_ENTRY(EBBHR, env->spr[SPR_EBBHR]); + REG_ENTRY(EBBRR, env->spr[SPR_EBBRR]); + + REG_ENTRY(FPSCR, env->fpscr); + REG_ENTRY(FSCR, env->spr[SPR_FSCR]); + + /* Save the GPRs */ + for (int gpr_id =3D 0; gpr_id < 32; ++gpr_id) { + curr_reg_entry->reg_id =3D + cpu_to_be64(fadump_gpr_id_to_u64(gpr_id)); + curr_reg_entry->reg_value =3D + cpu_to_be64(env->gpr[gpr_id]); + ++curr_reg_entry; + ++num_regs_per_cpu; + } + + REG_ENTRY(IAMR, env->spr[SPR_IAMR]); + REG_ENTRY(IC, env->spr[SPR_IC]); + REG_ENTRY(LR, env->spr[SPR_LR]); + + REG_ENTRY(MSR, env->msr); + REG_ENTRY(NIA, env->nip); /* NIA */ + REG_ENTRY(PIR, env->spr[SPR_PIR]); + REG_ENTRY(PSPB, env->spr[SPR_PSPB]); + REG_ENTRY(PVR, env->spr[SPR_PVR]); + REG_ENTRY(RPR, env->spr[SPR_RPR]); + REG_ENTRY(SPURR, env->spr[SPR_SPURR]); + REG_ENTRY(SRR0, env->spr[SPR_SRR0]); + REG_ENTRY(SRR1, env->spr[SPR_SRR1]); + REG_ENTRY(TAR, env->spr[SPR_TAR]); + REG_ENTRY(TEXASR, env->spr[SPR_TEXASR]); + REG_ENTRY(TFHAR, env->spr[SPR_TFHAR]); + REG_ENTRY(TFIAR, env->spr[SPR_TFIAR]); + REG_ENTRY(TIR, env->spr[SPR_TIR]); + REG_ENTRY(UAMOR, env->spr[SPR_UAMOR]); + REG_ENTRY(VRSAVE, env->spr[SPR_VRSAVE]); + REG_ENTRY(VSCR, env->vscr); + REG_ENTRY(VTB, env->spr[SPR_VTB]); + REG_ENTRY(WORT, env->spr[SPR_WORT]); + REG_ENTRY(XER, env->spr[SPR_XER]); + + /* + * Ignoring transaction checkpoint and few other registers + * mentioned in PAPR as not supported in QEMU + */ +#undef REG_ENTRY + + /* End the registers for this CPU with "CPUEND" reg entry */ + curr_reg_entry->reg_id =3D + cpu_to_be64(fadump_str_to_u64("CPUEND")); + + /* + * Ensure the number of registers match (+2 for STRT & END) + * + * This will help catch an error if in future a new register entry + * is added/removed while not modifying FADUMP_NUM_PER_CPU_REGS + */ + assert(FADUMP_NUM_PER_CPU_REGS =3D=3D num_regs_per_cpu + 2 /*CPUSTRT+C= PUEND*/); + + ++curr_reg_entry; + + return curr_reg_entry; +} + +/* + * Populate the "Register Save Area"/CPU State as mentioned in section "H.1 + * Register Save Area" in PAPR v2.13 + * + * It allocates the buffer for this region, then populates the register + * entries + * + * Returns the pointer to the buffer (which should be deallocated by the + * callers), and sets the size of this buffer in the argument + * 'cpu_state_len' + */ +static void *populate_cpu_state_data(uint64_t *cpu_state_len) +{ + FadumpRegSaveAreaHeader reg_save_hdr; + FadumpRegEntry *reg_entries; + FadumpRegEntry *curr_reg_entry; + CPUState *cpu; + + uint32_t num_reg_entries; + uint32_t reg_entries_size; + uint32_t num_cpus =3D 0; + + void *cpu_state_buffer =3D NULL; + uint64_t offset =3D 0; + + CPU_FOREACH(cpu) { + ++num_cpus; + } + + reg_save_hdr.version =3D cpu_to_be32(0); + reg_save_hdr.magic_number =3D + cpu_to_be64(fadump_str_to_u64("REGSAVE")); + + /* Reg save area header is immediately followed by num cpus */ + reg_save_hdr.num_cpu_offset =3D + cpu_to_be32(sizeof(FadumpRegSaveAreaHeader)); + + num_reg_entries =3D num_cpus * FADUMP_NUM_PER_CPU_REGS; + reg_entries_size =3D num_reg_entries * sizeof(FadumpRegEntry); + + reg_entries =3D g_new(FadumpRegEntry, num_reg_entries); + + /* Pointer to current CPU's registers */ + curr_reg_entry =3D reg_entries; + + /* Populate register entries for all CPUs */ + CPU_FOREACH(cpu) { + cpu_synchronize_state(cpu); + curr_reg_entry =3D populate_cpu_reg_entries(cpu, curr_reg_entry); + } + + *cpu_state_len =3D 0; + *cpu_state_len +=3D sizeof(reg_save_hdr); /* reg save header */ + *cpu_state_len +=3D 0xc; /* padding as in PAPR */ + *cpu_state_len +=3D sizeof(__be32); /* num_cpus */ + *cpu_state_len +=3D reg_entries_size; /* reg entries */ + + cpu_state_buffer =3D g_malloc(*cpu_state_len); + + memcpy(cpu_state_buffer + offset, + ®_save_hdr, sizeof(reg_save_hdr)); + offset +=3D sizeof(reg_save_hdr); + + /* Write num_cpus */ + num_cpus =3D cpu_to_be32(num_cpus); + memcpy(cpu_state_buffer + offset, &num_cpus, sizeof(__be32)); + offset +=3D sizeof(__be32); + + /* Write the register entries */ + memcpy(cpu_state_buffer + offset, reg_entries, reg_entries_size); + offset +=3D reg_entries_size; + + return cpu_state_buffer; +} + /* Preserve the memory locations registered for fadump */ static bool fadump_preserve_mem(SpaprMachineState *spapr) { FadumpMemStruct *fdm =3D &spapr->registered_fdm; + FadumpSection *cpu_state_region =3D NULL; + AddressSpace *default_as =3D &address_space_memory; + MemTxResult io_result; + MemTxAttrs attrs; uint16_t dump_num_sections, data_type; + uint64_t dest_addr; + uint64_t cpu_state_addr, cpu_state_len =3D 0; + g_autofree void *cpu_state_buffer =3D NULL; =20 assert(spapr->fadump_registered); =20 + /* Mark the memory transaction as privileged memory access */ + attrs.user =3D 0; + attrs.memory =3D 1; + /* * Handle all sections * @@ -241,6 +513,7 @@ static bool fadump_preserve_mem(SpaprMachineState *spap= r) dump_num_sections =3D be16_to_cpu(fdm->header.dump_num_sections); for (int i =3D 0; i < dump_num_sections; ++i) { data_type =3D be16_to_cpu(fdm->rgn[i].source_data_type); + dest_addr =3D be64_to_cpu(fdm->rgn[i].destination_address); =20 /* Reset error_flags & bytes_dumped for now */ fdm->rgn[i].error_flags =3D 0; @@ -255,7 +528,15 @@ static bool fadump_preserve_mem(SpaprMachineState *spa= pr) =20 switch (data_type) { case FADUMP_CPU_STATE_DATA: - /* TODO: Add CPU state data */ + cpu_state_region =3D &fdm->rgn[i]; + cpu_state_addr =3D dest_addr; + cpu_state_buffer =3D populate_cpu_state_data(&cpu_state_len); + + /* + * We will write the cpu state data later, as otherwise it + * might get overwritten by other fadump regions + */ + break; case FADUMP_HPTE_REGION: /* TODO: Add hpte state data */ @@ -281,6 +562,59 @@ static bool fadump_preserve_mem(SpaprMachineState *spa= pr) } } =20 + /* CPU State Region has not been requested by kernel */ + if (!cpu_state_region) { + return true; + } + + /* + * Write the Register Save Area + * + * CPU State/Register Save Area should be written after dumping the + * memory to prevent overwriting while saving other memory regions + * + * eg. If boot memory region is 1G, then both the first 1GB memory, and + * the Register Save Area needs to be saved at 1GB. + * And as the CPU_STATE_DATA region comes first than the + * REAL_MODE_REGION region to be copied, the CPU_STATE_DATA will get + * overwritten if saved before the 0GB - 1GB region is copied after + * saving CPU state data + */ + io_result =3D address_space_write(default_as, cpu_state_addr, attrs, + cpu_state_buffer, cpu_state_len); + if ((io_result & MEMTX_DECODE_ERROR) || + (io_result & MEMTX_ACCESS_ERROR)) { + /* + * Invalid destination address is not an hardware error, instead + * wrong parameter from the kernel. + * Return true to let caller know to continue reading other + * sections + */ + cpu_state_region->error_flags =3D FADUMP_ERROR_INVALID_DEST_ADDR; + cpu_state_region->bytes_dumped =3D 0; + return true; + } else if (io_result !=3D MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, + "FADump: Failed to write CPU state region\n"); + cpu_state_region->bytes_dumped =3D 0; + + return false; + } + + /* + * Set bytes_dumped in cpu state region, so kernel knows platform have + * exported it + */ + cpu_state_region->bytes_dumped =3D cpu_to_be64(cpu_state_len); + + if (cpu_state_region->source_len !=3D cpu_state_region->bytes_dumped) { + qemu_log_mask(LOG_GUEST_ERROR, + "CPU State region's length passed by kernel (0x%lx) !=3D" + " CPU State region's length exported by QEMU (0x%lx)\n", + be64_to_cpu(cpu_state_region->source_len), + be64_to_cpu(cpu_state_region->bytes_dumped)); + } + return true; } =20 diff --git a/include/hw/ppc/spapr_fadump.h b/include/hw/ppc/spapr_fadump.h index d56ca1d6d651..13bdd39a9ec1 100644 --- a/include/hw/ppc/spapr_fadump.h +++ b/include/hw/ppc/spapr_fadump.h @@ -48,9 +48,14 @@ #define FADUMP_MAX_SECTIONS 10 #define RTAS_FADUMP_MAX_BOOT_MEM_REGS 7 =20 +/* Number of registers stored per cpu */ +#define FADUMP_NUM_PER_CPU_REGS (32 /*GPR*/ + 45 /*others*/ + 2 /*STRT & E= ND*/) + typedef struct FadumpSection FadumpSection; typedef struct FadumpSectionHeader FadumpSectionHeader; typedef struct FadumpMemStruct FadumpMemStruct; +typedef struct FadumpRegSaveAreaHeader FadumpRegSaveAreaHeader; +typedef struct FadumpRegEntry FadumpRegEntry; =20 struct SpaprMachineState; =20 @@ -88,6 +93,29 @@ struct FadumpMemStruct { FadumpSection rgn[FADUMP_MAX_SECTIONS]; }; =20 +/* + * The firmware-assisted dump format. + * + * The register save area is an area in the partition's memory used to pre= serve + * the register contents (CPU state data) for the active CPUs during a fir= mware + * assisted dump. The dump format contains register save area header follo= wed + * by register entries. Each list of registers for a CPU starts with "CPUS= TRT" + * and ends with "CPUEND". + */ + +/* Register save area header. */ +struct FadumpRegSaveAreaHeader { + __be64 magic_number; + __be32 version; + __be32 num_cpu_offset; +}; + +/* Register entry. */ +struct FadumpRegEntry { + __be64 reg_id; + __be64 reg_value; +}; + uint32_t do_fadump_register(struct SpaprMachineState *, target_ulong); void trigger_fadump_boot(struct SpaprMachineState *, target_ulong); #endif /* PPC_SPAPR_FADUMP_H */ --=20 2.49.0