From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=209.85.215.226 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1794377302; b=3DxagcciHwbUMraNEx4LOBPiC5IBbpCYu/OaDsu2SgUpJL59MnKdlQAFL7LHKUPX4PnV kvpbggbExvjNInw4fVYV9MoaMXo8hyiQ1X0c4OXBH3HfrTHJ4B0XiWC0t82SbVKCFtybL lhXbITuExwtHiXI+czkfYrQOrJycxuq5HRLQ1pC/dObkKzGEN08+FR1eaNvsYzd0Z4E2X 0Xr1yvbhPv6C6sSnVt9d4qO/45rGYXGqDqozw0MtR4ODmKkBUj1lMiyY1zX2jrXcs4sr8 IfY0TACiapzvChJBjowGsFoiPZyg020VFZ1/uw0UIwacG3AcK2NGZP1wlzl+nmvnL3Q== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1794377302; h=DMARC-Filter:Received:X-Google-DKIM-Signature:X-Forwarded-Encrypted: X-Gm-Message-State:X-Gm-Gg:X-Received:Received:X-Relaying-Domain: X-CFilter-Loop:Received:DKIM-Signature:X-Forwarded-Encrypted: X-Received:X-Received:Received:From:To:Cc:Subject:Date:Message-ID: X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding:X-DetectorID-Processed; bh=Vgb5KbfLXjqL8iV/MCTqSzX96RhTYAbLrdtarSFXk0A=; b=tBceGwcoB4sWjar7C6At1M/jV52w7fC9kA8ulMY1gsQUyvVDQtayLHAjLBZ5s+P5gpiV OcE7Jjq3I62tryJ2oVi7bmKicEAZIJ+OV7aGmesNSla0AWITs+nDQcRsUbDBL+szb1J9t FXnXclM3AegG7RiEmUlAplcxDcf6njgYrQ481tLnntcM0O45OqC1Y1lE0xAgeInHQYkdr 5CmKFmnEfbYU1hT7KpJkkiqtvKShCwWcYTzJgJW5s0Mocr4PUrBWn3YEEwIom2Km1YJ07 68PirP6iup3Zf7BJ85iY1KyJA5lPC5jzaFPlIvz3U3kbOfQztIw6lap0xAmNEs2px3w== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=broadcom.com; spf=fail smtp.mailfrom=broadcom.com; dkim=pass header.d=broadcom.com header.i=@broadcom.com header.a=rsa-sha256 header.s=google header.b=dnJ5Kcq6; arc=none smtp.remote-ip=209.85.215.226 Received: from mail-pg1-f226.google.com (mail-pg1-f226.google.com [209.85.215.226]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 851C7606A330 for ; Wed, 11 Nov 2026 06:08:22 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 851C7606A330 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=broadcom.com Authentication-Results: smtp.subspace.kernel.org; spf=fail smtp.mailfrom=broadcom.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=broadcom.com header.i=@broadcom.com header.a=rsa-sha256 header.s=google header.b=dnJ5Kcq6 Received: by mail-pg1-f226.google.com with SMTP id 41be03b00d2f7-cdb681de50eso979924a12.2 for ; Tue, 10 Nov 2026 22:08:22 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1794377302; x=1794982102; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:dkim-signature:x-gm-gg :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to :content-type; bh=Vgb5KbfLXjqL8iV/MCTqSzX96RhTYAbLrdtarSFXk0A=; b=N8rsMUXvtSOqOSvzPSHjxfEQTbcG2dLOH+bF5eO3anzrE/CLeoy6XlnhRjH22TTo4C +JVStV4JQqta/mjSNN6osJ8zHvamfpVq0jFX8mP40PjoolOcDB/3WP8z2KvWnmkqSr6P kT7V21Gqlr7fTQbLiJIsjqhHgeUXdj/0+dLeM+m8avy7fxpAUy8WrMaVXlmmahmB3lxA f8ekp8KcXhu47l9r0Je+AHHXzk58bCs1MFwh95eModDpHgAqVyBmrI2C9ppN/5LJ+9RK JljldHNwl9neGWE9GMP8FeKF4mnhTYrTJVWtVF9PQLJ8lZimJq996sUVYnp32cU6/j09 QyrQ== X-Forwarded-Encrypted: i=1; AL+hErE+fPYDxeRmCQpG+UKwSrVFwuuGGwp8XI8Joc/WIa71ML6csDy4CzYpmFI26VsI6puHC6TtuW7o4tnyQS8=@vger.kernel.org X-Gm-Message-State: APuU4Z/l+G4QW0U7FgVAZ7Ar2v2/Ok2UPVkD4yFONAi+wvqqPjsS8oNE QZaJTK6FHybxDnk4sqEpR4wiqBysERTTelm4czb+rsgUUrlSeR8yuvVgnCB2VPriFmRHGPUbcbZ DzX42/W46qvXQc0+fjUvq5i6Qp2Z3r3Zqc70iLS54ciTRwyNf6JmSO3uhhVHkYd305KOnEOWyFd 6t2p14v6hpfANB+R462i4l7lbSM3jDIm226x2c3PLhPTVhli9STm7B2YqMAMCrMy/CrlcQ9zq3a gfmmdR2AuLir0vQ X-Gm-Gg: AYBFou0YK+42LnRtXnUpBpmK11UEDfzf6RkB7t6hr+EahHidoA2EBhOIJm8IIrOKfWx nWwJl0Q2n0KojtgDXMYKLaiIODWdv4NsxTCsXTphA2YYbptq1HYg+oDHU7/fkA0No3B7rvOkrYt JAJkZYcvwuNcurSc2tM4psweCJGvQr/Nvwe875+IRJziTL8+azYr/8mme4gZ+bTk2NVnTdMSdKC 1TsnwttMUWSNXCRBtnyYxub3DYSElfa/+MI6U6KYi3Ur+mg0CUr3qb64gv3/OPUSW2ZUZMhOI9y BimeORnMndRt5ifVayqLlWOOQgz6em4azBiGR1sgTv4FpmkHEBqwcWpqu4s0+BqwFuWWBboOWdY yXE7zKmzFl+UcutTaKN3OgEnU1m1iFFX42ht2HaNeggdLMXmVt3u4Mrh4GIE/Um6yt2DJPH2eB+ lQjgEB9RhMrr8gTbWxemmS2wF/fW7pzZWZ X-Received: by 2002:a17:90b:4a88:b0:3ac:9ed8:f30d with SMTP id 98e67ed59e1d1-3b182fe6587mr1510108a91.39.1794377301823; Tue, 10 Nov 2026 22:08:21 -0800 (PST) Received: from smtp-us-east1-p01-i01-si01.dlp.protect.broadcom.com (address-144-49-247-22.dlp.protect.broadcom.com. [144.49.247.22]) by smtp-relay.gmail.com with ESMTPS id 98e67ed59e1d1-3b17db39f76sm1445574a91.3.2026.11.10.22.08.21 for (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Tue, 10 Nov 2026 22:08:21 -0800 (PST) X-Relaying-Domain: broadcom.com X-CFilter-Loop: Reflected Received: by mail-ua1-f69.google.com with SMTP id a1e0cc1a2514c-998c9679e95so1965819241.3 for ; Tue, 10 Nov 2026 22:08:20 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; t=1794377300; x=1794982100; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Vgb5KbfLXjqL8iV/MCTqSzX96RhTYAbLrdtarSFXk0A=; b=dnJ5Kcq6/ZRs5d7QSsYykTwq0q9fiNMOVyY8u+eiTkz5MPiUlh7pb5Ykijl9of6o2R mIxuQ2Had4gtoipr/lI5/CltDDqPXKFUfiTHCL0jEXc4mlhquhQZFAXd8HtWHev7siMY AZpglG5tKhccXyoubVeMieUKhjyriv+ZsvLyk= X-Forwarded-Encrypted: i=1; AL+hErG4leYzxQ1fCo+4ysEkhD8xKGr7ZoPIm42AWtZMwF9zMMHa5sKYqe9NZgquniFhOqqBwBk6JtKaP9KgRNQ=@vger.kernel.org X-Received: by 2002:a05:620a:1a25:b0:93f:5f83:2ca3 with SMTP id af79cd13be357-947b007e993mr212499885a.54.1794376839295; Tue, 10 Nov 2026 22:00:39 -0800 (PST) X-Received: by 2002:a05:620a:1a25:b0:93f:5f83:2ca3 with SMTP id af79cd13be357-947b007e993mr212492885a.54.1794376838652; Tue, 10 Nov 2026 22:00:38 -0800 (PST) Received: from vertex.localdomain (pool-173-49-113-140.phlapa.fios.verizon.net. [173.49.113.140]) by smtp.gmail.com with ESMTPSA id af79cd13be357-9479e7e7f3bsm411506885a.37.2026.11.10.22.00.36 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 10 Nov 2026 22:00:37 -0800 (PST) From: Zack Rusin To: Sean Christopherson , Paolo Bonzini , kvm@vger.kernel.org Cc: Doug Covelli , Thomas Gleixner , Ingo Molnar , Borislav Petkov , Dave Hansen , x86@kernel.org, "H. Peter Anvin" , Jonathan Corbet , Randy Dunlap , Shuah Khan , Vitaly Kuznetsov , David Woodhouse , Paul Durrant , Xin Li , Francesco Lavra , =?UTF-8?q?Radim=20Kr=C4=8Dm=C3=A1=C5=99?= , Liran Alon , Arbel Moshe , linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org, bcm-kernel-feedback-list@broadcom.com, linux-kernel@vger.kernel.org, Zack Rusin Subject: [PATCH v3 09/12] KVM: selftests: Port VMware backdoor coverage Date: Wed, 11 Nov 2026 00:59:58 -0500 Message-ID: <4dca09deb85d2d5db157888ae4a2de7cbf6421d2.1794374897.git.zack.rusin@broadcom.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: References: X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-DetectorID-Processed: b00c1d49-9d2e-4205-b15f-d015386d3d5e Add vmware_backdoor_test, adapted from KVM-unit-tests' vmware_backdoors test. From CPL3, with CR4.PCE clear and no I/O permission bitmap in the TSS, read the VMware pseudo-PMCs with RDPMC and issue backdoor commands on the backdoor port with IN. Verify that the pseudo-PMC reads succeed and the port accesses exit to userspace only when the backdoor is enabled and raise #GP otherwise, and that other PMC indices and ports always raise #GP. Repeat the pseudo-PMC checks at CPL3 with CR4.PCE set and at CPL0, where RDPMC doesn't fault before KVM handles it. Skip the pseudo-PMC checks if KVM doesn't virtualize a PMU, as KVM then injects #GP for every RDPMC. Run the checks for a VM that relies on the enable_vmware_backdoor default and for a VM that enables KVM_VMWARE_ENABLE_BACKDOOR, and verify that KVM_CAP_X86_VMWARE rejects every flag it doesn't report. KVM-unit-tests can only cover the module parameter, which applies to every VM, while the selftest also covers the per-VM capability. Suggested-by: Sean Christopherson Link: https://lore.kernel.org/r/aIEoNpe-LFoH6LBm@google.com Signed-off-by: Zack Rusin --- MAINTAINERS | 1 + tools/testing/selftests/kvm/Makefile.kvm | 1 + .../selftests/kvm/x86/vmware_backdoor_test.c | 272 ++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 tools/testing/selftests/kvm/x86/vmware_backdoor_test.c diff --git a/MAINTAINERS b/MAINTAINERS index 017053e8f76e..39fa67a180f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15050,6 +15050,7 @@ S: Supported T: git git://git.kernel.org/pub/scm/virt/kvm/kvm.git T: git https://github.com/kvm-x86/linux.git F: arch/x86/kvm/vmware.* +F: tools/testing/selftests/kvm/x86/vmware_backdoor_test.c KVM X86 Xen (KVM/Xen) M: David Woodhouse diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm index 95867977beb3..0ef08d7783ae 100644 --- a/tools/testing/selftests/kvm/Makefile.kvm +++ b/tools/testing/selftests/kvm/Makefile.kvm @@ -140,6 +140,7 @@ TEST_GEN_PROGS_x86 += x86/sync_regs_test TEST_GEN_PROGS_x86 += x86/ucna_injection_test TEST_GEN_PROGS_x86 += x86/userspace_io_test TEST_GEN_PROGS_x86 += x86/userspace_msr_exit_test +TEST_GEN_PROGS_x86 += x86/vmware_backdoor_test TEST_GEN_PROGS_x86 += x86/vmx_apic_access_test TEST_GEN_PROGS_x86 += x86/vmx_apicv_updates_test TEST_GEN_PROGS_x86 += x86/vmx_exception_with_invalid_guest_state diff --git a/tools/testing/selftests/kvm/x86/vmware_backdoor_test.c b/tools/testing/selftests/kvm/x86/vmware_backdoor_test.c new file mode 100644 index 000000000000..e4e87d2ce3c3 --- /dev/null +++ b/tools/testing/selftests/kvm/x86/vmware_backdoor_test.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2025-2026 Broadcom. All Rights Reserved. The term + * "Broadcom" refers to Broadcom Inc. and/or its subsidiaries. + * + * Adapted from kvm-unit-tests' x86/vmware_backdoors.c. + */ + +#include "kvm_util.h" +#include "processor.h" +#include "test_util.h" + +#define VMWARE_BACKDOOR_PMC_HOST_TSC 0x10000 +#define VMWARE_BACKDOOR_PMC_REAL_TIME 0x10001 +#define VMWARE_BACKDOOR_PMC_APPARENT_TIME 0x10002 + +#define VMWARE_BACKDOOR_PORT 0x5658 +#define VMWARE_MAGIC 0x564d5868 +#define VMPORT_CMD_GETVERSION 0x0a +#define VMPORT_CMD_ILLEGAL 0xfff +#define VMPORT_DEFAULT_RETVAL 0xdeadbeef +#define RANDOM_IO_PORT 0x1234 +#define RANDOM_PMC 0xfff + +struct insn_result { + u64 value; + u8 vector; +}; + +struct backdoor_results { + struct insn_result pmc[4]; + struct insn_result port[3]; +}; + +enum backdoor_mode { + USER_MODE, /* CPL3, CR4.PCE clear */ + USER_MODE_PCE, /* CPL3, CR4.PCE set */ + KERNEL_MODE, /* CPL0 */ +}; + +static struct backdoor_results results; +static bool expect_backdoor; +static bool test_pmcs; + +static struct insn_result guest_rdpmc(u32 pmc) +{ + struct insn_result result; + u32 eax; + u32 edx; + u64 error_code; + + asm volatile(KVM_ASM_SAFE("rdpmc") + : "=a"(eax), "=d"(edx), + KVM_ASM_SAFE_OUTPUTS(result.vector, error_code) + : "c"(pmc) + : KVM_ASM_SAFE_CLOBBERS); + result.value = ((u64)edx << 32) | eax; + return result; +} + +static struct insn_result guest_inl(u16 port, u32 command) +{ + struct insn_result result; + unsigned long rax = VMPORT_DEFAULT_RETVAL; + unsigned long rbx = VMPORT_DEFAULT_RETVAL; + unsigned long rcx = command; + unsigned long rdx = port; + u64 error_code; + + asm volatile(KVM_ASM_SAFE("inl %%dx, %%eax") + : "+a"(rax), "+b"(rbx), "+c"(rcx), "+d"(rdx), + KVM_ASM_SAFE_OUTPUTS(result.vector, error_code) + : + : KVM_ASM_SAFE_CLOBBERS); + result.value = rbx; + return result; +} + +static void guest_read_pmcs(void) +{ + results.pmc[0] = guest_rdpmc(VMWARE_BACKDOOR_PMC_HOST_TSC); + results.pmc[1] = guest_rdpmc(VMWARE_BACKDOOR_PMC_REAL_TIME); + results.pmc[2] = guest_rdpmc(VMWARE_BACKDOOR_PMC_APPARENT_TIME); + results.pmc[3] = guest_rdpmc(RANDOM_PMC); +} + +static void guest_check_pmcs(void) +{ + int i; + + for (i = 0; i < 3; i++) { + GUEST_ASSERT_EQ(results.pmc[i].vector, + expect_backdoor ? 0 : GP_VECTOR); + if (expect_backdoor) + GUEST_ASSERT_NE(results.pmc[i].value, 0); + } + GUEST_ASSERT_EQ(results.pmc[3].vector, GP_VECTOR); +} + +static void guest_done_handler(struct ex_regs *regs) +{ + if (test_pmcs) + guest_check_pmcs(); + + GUEST_ASSERT_EQ(results.port[0].vector, + expect_backdoor ? 0 : GP_VECTOR); + GUEST_ASSERT_EQ(results.port[1].vector, + expect_backdoor ? 0 : GP_VECTOR); + if (expect_backdoor) + GUEST_ASSERT_EQ(results.port[0].value, VMWARE_MAGIC); + GUEST_ASSERT_EQ(results.port[2].vector, GP_VECTOR); + + GUEST_DONE(); +} + +static void guest_user_code(void) +{ + if (test_pmcs) + guest_read_pmcs(); + + results.port[0] = guest_inl(VMWARE_BACKDOOR_PORT, + VMPORT_CMD_GETVERSION); + results.port[1] = guest_inl(VMWARE_BACKDOOR_PORT, + VMPORT_CMD_ILLEGAL); + results.port[2] = guest_inl(RANDOM_IO_PORT, VMPORT_CMD_ILLEGAL); + + asm volatile("int3"); +} + +/* At CPL0, only the pseudo-PMCs depend on the backdoor. */ +static void guest_kernel_code(void) +{ + guest_read_pmcs(); + guest_check_pmcs(); + GUEST_DONE(); +} + +static void handle_vmware_port(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + struct kvm_regs regs; + u32 *data; + + TEST_ASSERT_EQ(run->io.direction, KVM_EXIT_IO_IN); + TEST_ASSERT_EQ(run->io.size, 4); + TEST_ASSERT_EQ(run->io.count, 1); + TEST_ASSERT_EQ(run->io.port, VMWARE_BACKDOOR_PORT); + + data = (void *)run + run->io.data_offset; + *data = 0; + + vcpu_regs_get(vcpu, ®s); + regs.rbx = VMWARE_MAGIC; + vcpu_regs_set(vcpu, ®s); +} + +static void test_unsupported_flags(u64 supported) +{ + struct kvm_vm *vm = vm_create_barebones(); + int bit, ret; + + for (bit = 0; bit < 64; bit++) { + if (supported & BIT_ULL(bit)) + continue; + + errno = 0; + ret = __vm_enable_cap(vm, KVM_CAP_X86_VMWARE, BIT_ULL(bit)); + TEST_ASSERT(ret == -1 && errno == EINVAL, + "Unsupported VMware flag %d: ret=%d errno=%d", + bit, ret, errno); + } + kvm_vm_free(vm); +} + +static void run_backdoor_test(bool enable_cap, bool expected, + enum backdoor_mode mode) +{ + struct kvm_sregs sregs; + struct idt_entry *idt; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int port_exits = 0; + + expect_backdoor = expected; + memset(&results, 0, sizeof(results)); + + vm = vm_create(1); + sync_global_to_guest(vm, expect_backdoor); + sync_global_to_guest(vm, test_pmcs); + sync_global_to_guest(vm, results); + if (enable_cap) + vm_enable_cap(vm, KVM_CAP_X86_VMWARE, + KVM_VMWARE_ENABLE_BACKDOOR); + if (mode == KERNEL_MODE) { + vcpu = vm_vcpu_add(vm, 0, guest_kernel_code); + } else { + vcpu = vm_vcpu_add(vm, 0, guest_user_code); + + idt = addr_gva2hva(vm, vm->arch.idt); + idt[BP_VECTOR].dpl = 3; + vm_install_exception_handler(vm, BP_VECTOR, guest_done_handler); + vm_init_user_mode(vm); + vcpu_enter_user_mode(vcpu); + } + + if (mode == USER_MODE_PCE) { + vcpu_sregs_get(vcpu, &sregs); + sregs.cr4 |= X86_CR4_PCE; + vcpu_sregs_set(vcpu, &sregs); + } + + for (;;) { + struct kvm_run *run = vcpu->run; + struct ucall uc; + + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + if (run->io.port == VMWARE_BACKDOOR_PORT) { + TEST_ASSERT(expected, + "Disabled VMware backdoor reached userspace"); + handle_vmware_port(vcpu); + port_exits++; + continue; + } + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + case UCALL_DONE: + TEST_ASSERT_EQ(port_exits, + expected && mode != KERNEL_MODE ? 2 : 0); + kvm_vm_free(vm); + return; + default: + TEST_FAIL("Unexpected ucall 0x%lx", uc.cmd); + } + } +} + +int main(int argc, char *argv[]) +{ + unsigned int supported = kvm_check_cap(KVM_CAP_X86_VMWARE); + enum backdoor_mode mode; + bool module_default; + + TEST_REQUIRE(supported & KVM_VMWARE_ENABLE_BACKDOOR); + + test_unsupported_flags(supported); + + /* KVM serves the pseudo-PMCs only if it virtualizes a PMU. */ + test_pmcs = kvm_is_pmu_enabled(); + if (!test_pmcs) + ksft_print_msg("vPMU disabled, skipping pseudo-PMC checks\n"); + + module_default = get_kvm_param_bool("enable_vmware_backdoor"); + run_backdoor_test(false, module_default, USER_MODE); + run_backdoor_test(true, true, USER_MODE); + + /* + * RDPMC at CPL0, or at CPL3 with CR4.PCE set, doesn't raise the #GP + * that the emulator handles, so check the pseudo-PMCs there as well. + */ + if (test_pmcs) { + for (mode = USER_MODE_PCE; mode <= KERNEL_MODE; mode++) { + run_backdoor_test(false, module_default, mode); + run_backdoor_test(true, true, mode); + } + } + return 0; +} -- 2.53.0