target-arm queue:

* Implement FEAT_SCTLR2
  * Implement FEAT_TCR2
  * Implement FEAT_CSSC
  * Implement FEAT_LSE128
  * Clean up of register field definitions
  * Trap PMCR when MDCR_EL2.TPMCR is set
  * tests/functional: update aarch64 RME test images
  * hw/intc/arm_gicv3_kvm: preserve pending interrupts during cpr
  * hw/arm: add static NVDIMMs in device tree
  * hw/arm/stm32f205_soc: Don't leak TYPE_OR_IRQ objects
  * scripts/kernel-doc: Avoid new Perl precedence warning
  * scripts/kernel-doc: Update to kernel's new Python implementation
 -----BEGIN PGP SIGNATURE-----
 
 iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmizIcAZHHBldGVyLm1h
 eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3tmCD/9Pe4Evw/I2e3Nqr4X87+KC
 JtX3s9U8Gly1ttnWd5a+fRubBqIvpxRsJYf0PJQi7otPGDq4E3TZ5UCRnInArpRh
 hJqyNxi2ELgDU0Z917UYMnxBwpv7+V/635V1/svSOWDf9RPHnf6GwrmlCvu4Llgf
 mVtDlQd+Ta5hoICM0VzrMZfTYevxGqi/cr/oVzCObKmh1YMpPTtSNlfYPMFcY7py
 JLu5e7YNN2krh19nCXieS3iqXMsFoLp31kXcCmKE1BgIKeVPNxTRMfOWa4uNDtUN
 17iLfHLatNfSWUA1gvUHxv2maCdm4xJZdGowP/uYvzaemquFSjfM/8qaBxxFqZ1v
 7jdZEzdnn1CX4Kmu3cPvhcuACyYRprlrKZYvCrTH4yCKbJsm0Uo7M66ia3EIF5EQ
 kehnGGwu3rv3qrliTXiXoAr7fC0OOiN0afAkS6a5lAi13s6M+Se2VElnRvIoXR2W
 0Xw21/05p/WuXLoMNFjEpAaQgWYEc0kQhFAQczcZH+pyGlaU2QxCTTnaeuHUWcke
 y7OtpVBk4Fukaqd4gn0SQtYQLxeFq6vPOL4b1VKR5FuGDSucBUjuVl0dG4gkdbII
 yvCBaTb+IEY4fJ1E8IMTI3Lcydv9yblLyGXLr42e22x/l51SCZs1WvIx2i6u6VST
 lYnoOObEknvf25YAu3rDTw==
 =VItP
 -----END PGP SIGNATURE-----

Merge tag 'pull-target-arm-20250830' of https://gitlab.com/pm215/qemu into staging

target-arm queue:
 * Implement FEAT_SCTLR2
 * Implement FEAT_TCR2
 * Implement FEAT_CSSC
 * Implement FEAT_LSE128
 * Clean up of register field definitions
 * Trap PMCR when MDCR_EL2.TPMCR is set
 * tests/functional: update aarch64 RME test images
 * hw/intc/arm_gicv3_kvm: preserve pending interrupts during cpr
 * hw/arm: add static NVDIMMs in device tree
 * hw/arm/stm32f205_soc: Don't leak TYPE_OR_IRQ objects
 * scripts/kernel-doc: Avoid new Perl precedence warning
 * scripts/kernel-doc: Update to kernel's new Python implementation

# -----BEGIN PGP SIGNATURE-----
#
# iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmizIcAZHHBldGVyLm1h
# eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3tmCD/9Pe4Evw/I2e3Nqr4X87+KC
# JtX3s9U8Gly1ttnWd5a+fRubBqIvpxRsJYf0PJQi7otPGDq4E3TZ5UCRnInArpRh
# hJqyNxi2ELgDU0Z917UYMnxBwpv7+V/635V1/svSOWDf9RPHnf6GwrmlCvu4Llgf
# mVtDlQd+Ta5hoICM0VzrMZfTYevxGqi/cr/oVzCObKmh1YMpPTtSNlfYPMFcY7py
# JLu5e7YNN2krh19nCXieS3iqXMsFoLp31kXcCmKE1BgIKeVPNxTRMfOWa4uNDtUN
# 17iLfHLatNfSWUA1gvUHxv2maCdm4xJZdGowP/uYvzaemquFSjfM/8qaBxxFqZ1v
# 7jdZEzdnn1CX4Kmu3cPvhcuACyYRprlrKZYvCrTH4yCKbJsm0Uo7M66ia3EIF5EQ
# kehnGGwu3rv3qrliTXiXoAr7fC0OOiN0afAkS6a5lAi13s6M+Se2VElnRvIoXR2W
# 0Xw21/05p/WuXLoMNFjEpAaQgWYEc0kQhFAQczcZH+pyGlaU2QxCTTnaeuHUWcke
# y7OtpVBk4Fukaqd4gn0SQtYQLxeFq6vPOL4b1VKR5FuGDSucBUjuVl0dG4gkdbII
# yvCBaTb+IEY4fJ1E8IMTI3Lcydv9yblLyGXLr42e22x/l51SCZs1WvIx2i6u6VST
# lYnoOObEknvf25YAu3rDTw==
# =VItP
# -----END PGP SIGNATURE-----
# gpg: Signature made Sun 31 Aug 2025 02:07:28 AM AEST
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [unknown]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [unknown]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [unknown]
# gpg:                 aka "Peter Maydell <peter@archaic.org.uk>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* tag 'pull-target-arm-20250830' of https://gitlab.com/pm215/qemu: (32 commits)
  hw/arm/stm32f205_soc: Don't leak TYPE_OR_IRQ objects
  target/arm: Enable FEAT_LSE128 for -cpu max
  target/arm: Implement FEAT_LSE128
  target/arm: Rename isar_feature_aa64_atomics
  tcg: Add tcg_gen_atomic_{xchg,fetch_and,fetch_or}_i128
  accel/tcg: Add cpu_atomic_*_mmu for 16-byte xchg, fetch_and, fetch_or
  qemu/atomic: Add atomic16 primitives for xchg, fetch_and, fetch_or
  qemu/atomic: Finish renaming atomic128-cas.h headers
  target/arm: Correct condition of aa64_atomics feature function
  MAINTAINERS: Put kernel-doc under the "docs build machinery" section
  scripts/kernel-doc: Delete the old Perl kernel-doc script
  scripts/kerneldoc: Switch to the Python kernel-doc script
  scripts/kernel-doc: tweak for QEMU coding standards
  scripts/kernel-doc: strip QEMU_ from function definitions
  scripts: Import Python kerneldoc from Linux kernel
  tests/qtest/libqtest.h: Remove stray space from doc comment
  docs/sphinx/kerneldoc.py: Handle new LINENO syntax
  scripts/kernel-doc: Avoid new Perl precedence warning
  hw/arm: add static NVDIMMs in device tree
  target/arm: Enable FEAT_CSSC for -cpu max
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2025-08-31 07:37:34 +10:00
commit e101d33792
44 changed files with 4389 additions and 2663 deletions

View file

@ -55,7 +55,7 @@ indent_size = 4
emacs_mode = perl
# but user kernel "style" for imported scripts
[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}]
[scripts/{get_maintainer.pl,checkpatch.pl}]
indent_style = tab
indent_size = 8
emacs_mode = perl

View file

@ -4441,6 +4441,7 @@ F: po/*.po
Sphinx documentation configuration and build machinery
M: John Snow <jsnow@redhat.com>
M: Peter Maydell <peter.maydell@linaro.org>
M: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
S: Maintained
F: docs/conf.py
F: docs/*/conf.py
@ -4449,6 +4450,8 @@ F: docs/sphinx/
F: docs/_templates/
F: docs/devel/docs.rst
F: docs/devel/qapi-domain.rst
F: scripts/kernel-doc
F: scripts/lib/kdoc/
Rust build system integration
M: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>

View file

@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch)
GEN_ATOMIC_HELPERS(xchg)
#if HAVE_CMPXCHG128
ATOMIC_HELPER(xchgo_be, Int128)
ATOMIC_HELPER(xchgo_le, Int128)
ATOMIC_HELPER(fetch_ando_be, Int128)
ATOMIC_HELPER(fetch_ando_le, Int128)
ATOMIC_HELPER(fetch_oro_be, Int128)
ATOMIC_HELPER(fetch_oro_le, Int128)
#endif
#undef ATOMIC_HELPER
#undef GEN_ATOMIC_HELPERS

View file

@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return ret;
}
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
@ -108,7 +107,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
DATA_SIZE, retaddr);
DATA_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, val);
#else
ret = qatomic_xchg__nocheck(haddr, val);
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@ -119,6 +122,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return ret;
}
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, val);
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return ret;
}
#else
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new)
GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new)
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA SIZE < 16 */
#endif /* DATA SIZE == 16 */
#undef END
@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr,
return BSWAP(ret);
}
#if DATA_SIZE < 16
ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
@ -233,7 +268,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
DATA_SIZE, retaddr);
ABI_TYPE ret;
#if DATA_SIZE == 16
ret = atomic16_xchg(haddr, BSWAP(val));
#else
ret = qatomic_xchg__nocheck(haddr, BSWAP(val));
#endif
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
@ -244,6 +283,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val,
return BSWAP(ret);
}
#if DATA_SIZE == 16
ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val,
MemOpIdx oi, uintptr_t retaddr)
{
DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi,
DATA_SIZE, retaddr);
DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val));
ATOMIC_MMU_CLEANUP;
atomic_trace_rmw_post(env, addr,
VALUE_LOW(ret),
VALUE_HIGH(ret),
VALUE_LOW(val),
VALUE_HIGH(val),
oi);
return BSWAP(ret);
}
#else
#define GEN_ATOMIC_HELPER(X) \
ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \
ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \
@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new)
#undef ADD
#undef GEN_ATOMIC_HELPER_FN
#endif /* DATA_SIZE < 16 */
#endif /* DATA_SIZE == 16 */
#undef END
#endif /* DATA_SIZE > 1 */

View file

@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG,
i128, env, i64, i128, i32)
#endif
DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG,

View file

@ -114,7 +114,7 @@ static uint32_t get_elf_hwcap(void)
GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3);
GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4);
GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP);
GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS);
GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS);
GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM);
GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP);
GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA);

View file

@ -341,7 +341,9 @@ man_make_section_directory = False
# We use paths starting from qemu_docdir here so that you can run
# sphinx-build from anywhere and the kerneldoc extension can still
# find everything.
kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')]
# Since kernel-doc is now a Python script, we should run it with whatever
# Python this sphinx is using (rather than letting it find one via env)
kerneldoc_bin = [sys.executable, os.path.join(qemu_docdir, '../scripts/kernel-doc.py')]
kerneldoc_srctree = os.path.join(qemu_docdir, '..')
hxtool_srctree = os.path.join(qemu_docdir, '..')
qapidoc_srctree = os.path.join(qemu_docdir, '..')

View file

@ -63,11 +63,6 @@ class KernelDocDirective(Directive):
env = self.state.document.settings.env
cmd = env.config.kerneldoc_bin + ['-rst', '-enable-lineno']
# Pass the version string to kernel-doc, as it needs to use a different
# dialect, depending what the C domain supports for each specific
# Sphinx versions
cmd += ['-sphinx-version', sphinx.__version__]
# Pass through the warnings-as-errors flag
if env.config.kerneldoc_werror:
cmd += ['-Werror']
@ -127,7 +122,7 @@ class KernelDocDirective(Directive):
result = ViewList()
lineoffset = 0;
line_regex = re.compile("^#define LINENO ([0-9]+)$")
line_regex = re.compile(r"^(?:\.\.|#define) LINENO ([0-9]+)$")
for line in lines:
match = line_regex.search(line)
if match:

View file

@ -30,6 +30,7 @@ the following architecture extensions:
- FEAT_CMOW (Control for cache maintenance permission)
- FEAT_CRC32 (CRC32 instructions)
- FEAT_Crypto (Cryptographic Extension)
- FEAT_CSSC (Common Short Sequence Compression instructions)
- FEAT_CSV2 (Cache speculation variant 2)
- FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1)
- FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2)
@ -88,6 +89,7 @@ the following architecture extensions:
- FEAT_LRCPC2 (Load-acquire RCpc instructions v2)
- FEAT_LSE (Large System Extensions)
- FEAT_LSE2 (Large System Extensions v2)
- FEAT_LSE128 (128-bit Atomics)
- FEAT_LVA (Large Virtual Address space)
- FEAT_MixedEnd (Mixed-endian support)
- FEAT_MixedEndEL0 (Mixed-endian support at EL0)
@ -121,6 +123,7 @@ the following architecture extensions:
- FEAT_RPRES (Increased precision of FRECPE and FRSQRTE)
- FEAT_S2FWB (Stage 2 forced Write-Back)
- FEAT_SB (Speculation Barrier)
- FEAT_SCTLR2 (Extension to SCTLR_ELx)
- FEAT_SEL2 (Secure EL2)
- FEAT_SHA1 (SHA1 instructions)
- FEAT_SHA256 (SHA256 instructions)
@ -148,6 +151,7 @@ the following architecture extensions:
- FEAT_SPECRES (Speculation restriction instructions)
- FEAT_SSBS (Speculative Store Bypass Safe)
- FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2)
- FEAT_TCR2 (Support for TCR2_ELx)
- FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1)
- FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1)
- FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1)

View file

@ -1,45 +0,0 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Compare-and-swap for 128-bit atomic operations, AArch64 version.
*
* Copyright (C) 2018, 2023 Linaro, Ltd.
*
* See docs/devel/atomics.rst for discussion about the guarantees each
* atomic primitive is meant to provide.
*/
#ifndef AARCH64_ATOMIC128_CAS_H
#define AARCH64_ATOMIC128_CAS_H
/* Through gcc 10, aarch64 has no support for 128-bit atomics. */
#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128)
#include "host/include/generic/host/atomic128-cas.h.inc"
#else
static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
{
uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp);
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
uint64_t oldl, oldh;
uint32_t tmp;
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
"cmp %[oldl], %[cmpl]\n\t"
"ccmp %[oldh], %[cmph], #0, eq\n\t"
"b.ne 1f\n\t"
"stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t"
"cbnz %w[tmp], 0b\n"
"1:"
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
[oldl] "=&r"(oldl), [oldh] "=&r"(oldh)
: [cmpl] "r"(cmpl), [cmph] "r"(cmph),
[newl] "r"(newl), [newh] "r"(newh)
: "memory", "cc");
return int128_make128(oldl, oldh);
}
# define CONFIG_CMPXCHG128 1
# define HAVE_CMPXCHG128 1
#endif
#endif /* AARCH64_ATOMIC128_CAS_H */

View file

@ -0,0 +1,102 @@
/*
* SPDX-License-Identifier: GPL-2.0-or-later
* Compare-and-swap for 128-bit atomic operations, AArch64 version.
*
* Copyright (C) 2018, 2023 Linaro, Ltd.
*
* See docs/devel/atomics.rst for discussion about the guarantees each
* atomic primitive is meant to provide.
*/
#ifndef AARCH64_ATOMIC128_CAS_H
#define AARCH64_ATOMIC128_CAS_H
/* Through gcc 10, aarch64 has no support for 128-bit atomics. */
#if defined(CONFIG_ATOMIC128) || defined(CONFIG_CMPXCHG128)
#include "host/include/generic/host/atomic128-cas.h.inc"
#else
static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
{
uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp);
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
uint64_t oldl, oldh;
uint32_t tmp;
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
"cmp %[oldl], %[cmpl]\n\t"
"ccmp %[oldh], %[cmph], #0, eq\n\t"
"b.ne 1f\n\t"
"stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t"
"cbnz %w[tmp], 0b\n"
"1:"
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
[oldl] "=&r"(oldl), [oldh] "=&r"(oldh)
: [cmpl] "r"(cmpl), [cmph] "r"(cmph),
[newl] "r"(newl), [newh] "r"(newh)
: "memory", "cc");
return int128_make128(oldl, oldh);
}
static inline Int128 atomic16_xchg(Int128 *ptr, Int128 new)
{
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
uint64_t oldl, oldh;
uint32_t tmp;
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
"stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t"
"cbnz %w[tmp], 0b"
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
[oldl] "=&r"(oldl), [oldh] "=&r"(oldh)
: [newl] "r"(newl), [newh] "r"(newh)
: "memory");
return int128_make128(oldl, oldh);
}
static inline Int128 atomic16_fetch_and(Int128 *ptr, Int128 new)
{
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
uint64_t oldl, oldh, tmpl, tmph;
uint32_t tmp;
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
"and %[tmpl], %[oldl], %[newl]\n\t"
"and %[tmph], %[oldh], %[newh]\n\t"
"stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t"
"cbnz %w[tmp], 0b"
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
[oldl] "=&r"(oldl), [oldh] "=&r"(oldh)
: [newl] "r"(newl), [newh] "r"(newh),
[tmpl] "r"(tmpl), [tmph] "r"(tmph)
: "memory");
return int128_make128(oldl, oldh);
}
static inline Int128 atomic16_fetch_or(Int128 *ptr, Int128 new)
{
uint64_t newl = int128_getlo(new), newh = int128_gethi(new);
uint64_t oldl, oldh, tmpl, tmph;
uint32_t tmp;
asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t"
"orr %[tmpl], %[oldl], %[newl]\n\t"
"orr %[tmph], %[oldh], %[newh]\n\t"
"stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t"
"cbnz %w[tmp], 0b"
: [mem] "+m"(*ptr), [tmp] "=&r"(tmp),
[oldl] "=&r"(oldl), [oldh] "=&r"(oldh)
: [newl] "r"(newl), [newh] "r"(newh),
[tmpl] "r"(tmpl), [tmph] "r"(tmph)
: "memory");
return int128_make128(oldl, oldh);
}
# define CONFIG_CMPXCHG128 1
# define HAVE_CMPXCHG128 1
#endif
#endif /* AARCH64_ATOMIC128_CAS_H */

View file

@ -23,6 +23,51 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i);
return r.s;
}
/*
* Since we're looping anyway, use weak compare and swap.
* If the host supports weak, this will eliminate a second loop hidden
* within the atomic operation itself; otherwise the weak parameter is
* ignored.
*/
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_xchg(Int128 *ptr, Int128 new)
{
__int128_t *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128 old = *ptr_align;
while (!__atomic_compare_exchange_n(ptr_align, &old, new, true,
__ATOMIC_SEQ_CST, 0)) {
continue;
}
return old;
}
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_fetch_and(Int128 *ptr, Int128 val)
{
__int128_t *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128 old = *ptr_align;
while (!__atomic_compare_exchange_n(ptr_align, &old, old & val, true,
__ATOMIC_SEQ_CST, 0)) {
continue;
}
return old;
}
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_fetch_or(Int128 *ptr, Int128 val)
{
__int128_t *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128 old = *ptr_align;
while (!__atomic_compare_exchange_n(ptr_align, &old, old | val, true,
__ATOMIC_SEQ_CST, 0)) {
continue;
}
return old;
}
# define HAVE_CMPXCHG128 1
#elif defined(CONFIG_CMPXCHG128)
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
@ -36,6 +81,57 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new)
r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i);
return r.s;
}
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_xchg(Int128 *ptr, Int128 new)
{
Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128Alias o, n;
n.s = new;
o.s = *ptr_align;
while (1) {
__int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, n.i);
if (c == o.i) {
return o.s;
}
o.i = c;
}
}
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_fetch_and(Int128 *ptr, Int128 val)
{
Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128Alias o, v;
v.s = val;
o.s = *ptr_align;
while (1) {
__int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i & v.i);
if (c == o.i) {
return o.s;
}
o.i = c;
}
}
static inline Int128 ATTRIBUTE_ATOMIC128_OPT
atomic16_fetch_or(Int128 *ptr, Int128 val)
{
Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16);
Int128Alias o, v;
v.s = val;
o.s = *ptr_align;
while (1) {
__int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i | v.i);
if (c == o.i) {
return o.s;
}
o.i = c;
}
}
# define HAVE_CMPXCHG128 1
#else
/* Fallback definition that must be optimized away, or error. */

View file

@ -25,6 +25,7 @@
#include "hw/boards.h"
#include "system/reset.h"
#include "hw/loader.h"
#include "hw/mem/memory-device.h"
#include "elf.h"
#include "system/device_tree.h"
#include "qemu/config-file.h"
@ -515,6 +516,29 @@ static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu)
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
static int fdt_add_pmem_node(void *fdt, uint32_t acells, uint32_t scells,
int64_t mem_base, int64_t size, int64_t node)
{
int ret;
g_autofree char *nodename = g_strdup_printf("/pmem@%" PRIx64, mem_base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "compatible", "pmem-region");
ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells,
mem_base, scells, size);
if (ret) {
return ret;
}
if (node >= 0) {
return qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id",
node);
}
return 0;
}
int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
hwaddr addr_limit, AddressSpace *as, MachineState *ms,
ARMCPU *cpu)
@ -525,6 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
unsigned int i;
hwaddr mem_base, mem_len;
char **node_path;
g_autofree MemoryDeviceInfoList *md_list = NULL;
Error *err = NULL;
if (binfo->dtb_filename) {
@ -628,6 +653,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
}
}
md_list = qmp_memory_device_list();
for (MemoryDeviceInfoList *m = md_list; m != NULL; m = m->next) {
MemoryDeviceInfo *mi = m->value;
if (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM) {
PCDIMMDeviceInfo *di = mi->u.nvdimm.data;
rc = fdt_add_pmem_node(fdt, acells, scells,
di->addr, di->size, di->node);
if (rc < 0) {
fprintf(stderr, "couldn't add NVDIMM /pmem@%"PRIx64" node\n",
di->addr);
goto fail;
}
}
}
rc = fdt_path_offset(fdt, "/chosen");
if (rc < 0) {
qemu_fdt_add_subnode(fdt, "/chosen");

View file

@ -66,7 +66,7 @@ static void stm32f205_soc_initfn(Object *obj)
TYPE_STM32F2XX_TIMER);
}
s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ));
object_initialize_child(obj, "adc-irq-orgate", &s->adc_irqs, TYPE_OR_IRQ);
for (i = 0; i < STM_NUM_ADCS; i++) {
object_initialize_child(obj, "adc[*]", &s->adc[i], TYPE_STM32F2XX_ADC);
@ -171,12 +171,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
}
/* ADC 1 to 3 */
object_property_set_int(OBJECT(s->adc_irqs), "num-lines", STM_NUM_ADCS,
object_property_set_int(OBJECT(&s->adc_irqs), "num-lines", STM_NUM_ADCS,
&error_abort);
if (!qdev_realize(DEVICE(s->adc_irqs), NULL, errp)) {
if (!qdev_realize(DEVICE(&s->adc_irqs), NULL, errp)) {
return;
}
qdev_connect_gpio_out(DEVICE(s->adc_irqs), 0,
qdev_connect_gpio_out(DEVICE(&s->adc_irqs), 0,
qdev_get_gpio_in(armv7m, ADC_IRQ));
for (i = 0; i < STM_NUM_ADCS; i++) {
@ -187,7 +187,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp)
busdev = SYS_BUS_DEVICE(dev);
sysbus_mmio_map(busdev, 0, adc_addr[i]);
sysbus_connect_irq(busdev, 0,
qdev_get_gpio_in(DEVICE(s->adc_irqs), i));
qdev_get_gpio_in(DEVICE(&s->adc_irqs), i));
}
/* SPI 1 and 2 */

View file

@ -2917,7 +2917,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
const MachineState *ms = MACHINE(hotplug_dev);
const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM);
if (!vms->acpi_dev) {
if (!vms->acpi_dev && !(is_nvdimm && !dev->hotplugged)) {
error_setg(errp,
"memory hotplug is not enabled: missing acpi-ged device");
return;
@ -2949,8 +2949,10 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev,
nvdimm_plug(ms->nvdimms_state);
}
hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev),
dev, &error_abort);
if (vms->acpi_dev) {
hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev),
dev, &error_abort);
}
}
static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev,

View file

@ -31,6 +31,7 @@
#include "gicv3_internal.h"
#include "vgic_common.h"
#include "migration/blocker.h"
#include "migration/misc.h"
#include "qom/object.h"
#include "target/arm/cpregs.h"
@ -776,6 +777,17 @@ static void vm_change_state_handler(void *opaque, bool running,
}
}
static int kvm_arm_gicv3_notifier(NotifierWithReturn *notifier,
MigrationEvent *e, Error **errp)
{
if (e->type == MIG_EVENT_PRECOPY_DONE) {
GICv3State *s = container_of(notifier, GICv3State, cpr_notifier);
return kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES,
NULL, true, errp);
}
return 0;
}
static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
{
@ -917,6 +929,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp)
if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) {
qemu_add_vm_change_state_handler(vm_change_state_handler, s);
migration_add_notifier_mode(&s->cpr_notifier,
kvm_arm_gicv3_notifier,
MIG_MODE_CPR_TRANSFER);
}
}

View file

@ -100,9 +100,6 @@ GEN_ATOMIC_HELPER_ALL(umax_fetch)
GEN_ATOMIC_HELPER_ALL(xchg)
#undef GEN_ATOMIC_HELPER_ALL
#undef GEN_ATOMIC_HELPER
Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr,
Int128 cmpv, Int128 newv,
MemOpIdx oi, uintptr_t retaddr);
@ -110,6 +107,16 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr,
Int128 cmpv, Int128 newv,
MemOpIdx oi, uintptr_t retaddr);
GEN_ATOMIC_HELPER(xchg, Int128, o_le)
GEN_ATOMIC_HELPER(xchg, Int128, o_be)
GEN_ATOMIC_HELPER(fetch_and, Int128, o_le)
GEN_ATOMIC_HELPER(fetch_and, Int128, o_be)
GEN_ATOMIC_HELPER(fetch_or, Int128, o_le)
GEN_ATOMIC_HELPER(fetch_or, Int128, o_be)
#undef GEN_ATOMIC_HELPER_ALL
#undef GEN_ATOMIC_HELPER
uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr,
MemOpIdx oi, uintptr_t ra);
uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr,

View file

@ -59,7 +59,7 @@ struct STM32F205State {
STM32F2XXADCState adc[STM_NUM_ADCS];
STM32F2XXSPIState spi[STM_NUM_SPIS];
OrIRQState *adc_irqs;
OrIRQState adc_irqs;
MemoryRegion sram;
MemoryRegion flash;

View file

@ -27,6 +27,7 @@
#include "hw/sysbus.h"
#include "hw/intc/arm_gic_common.h"
#include "qom/object.h"
#include "qemu/notify.h"
/*
* Maximum number of possible interrupts, determined by the GIC architecture.
@ -271,6 +272,8 @@ struct GICv3State {
GICv3CPUState *cpu;
/* List of all ITSes connected to this GIC */
GPtrArray *itslist;
NotifierWithReturn cpr_notifier;
};
#define GICV3_BITMAP_ACCESSORS(BMP) \

View file

@ -344,6 +344,8 @@ void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32,
TCGArg, MemOp, TCGType);
void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64,
TCGArg, MemOp, TCGType);
void tcg_gen_atomic_xchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128,
TCGArg, MemOp, TCGType);
void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32,
TCGArg, MemOp, TCGType);
@ -411,6 +413,11 @@ void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32,
void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64,
TCGArg, MemOp, TCGType);
void tcg_gen_atomic_fetch_and_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128,
TCGArg, MemOp, TCGType);
void tcg_gen_atomic_fetch_or_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128,
TCGArg, MemOp, TCGType);
/* Vector ops */
void tcg_gen_mov_vec(TCGv_vec, TCGv_vec);

View file

@ -134,13 +134,16 @@ DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i128)
DEF_ATOMIC2(tcg_gen_atomic_xchg, i32)
DEF_ATOMIC2(tcg_gen_atomic_xchg, i64)
DEF_ATOMIC2(tcg_gen_atomic_xchg, i128)
DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i32)
DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i64)
DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i32)
DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i64)
DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i128)
DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i32)
DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i64)
DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i128)
DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i32)
DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i64)
DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i32)

View file

@ -154,7 +154,7 @@ abi_ulong get_elf_hwcap(CPUState *cs)
GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3);
GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4);
GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP);
GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS);
GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS);
GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT);
GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM);
GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP);
@ -215,6 +215,8 @@ abi_ulong get_elf_hwcap2(CPUState *cs)
GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16);
GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16);
GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16);
GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC);
GET_FEATURE_ID(aa64_lse128, ARM_HWCAP2_A64_LSE128);
return hwcaps;
}

File diff suppressed because it is too large Load diff

325
scripts/kernel-doc.py Executable file
View file

@ -0,0 +1,325 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
#
# pylint: disable=C0103,R0915
#
# Converted from the kernel-doc script originally written in Perl
# under GPLv2, copyrighted since 1998 by the following authors:
#
# Aditya Srivastava <yashsri421@gmail.com>
# Akira Yokosawa <akiyks@gmail.com>
# Alexander A. Klimov <grandmaster@al2klimov.de>
# Alexander Lobakin <aleksander.lobakin@intel.com>
# André Almeida <andrealmeid@igalia.com>
# Andy Shevchenko <andriy.shevchenko@linux.intel.com>
# Anna-Maria Behnsen <anna-maria@linutronix.de>
# Armin Kuster <akuster@mvista.com>
# Bart Van Assche <bart.vanassche@sandisk.com>
# Ben Hutchings <ben@decadent.org.uk>
# Borislav Petkov <bbpetkov@yahoo.de>
# Chen-Yu Tsai <wenst@chromium.org>
# Coco Li <lixiaoyan@google.com>
# Conchúr Navid <conchur@web.de>
# Daniel Santos <daniel.santos@pobox.com>
# Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk>
# Dan Luedtke <mail@danrl.de>
# Donald Hunter <donald.hunter@gmail.com>
# Gabriel Krisman Bertazi <krisman@collabora.co.uk>
# Greg Kroah-Hartman <gregkh@linuxfoundation.org>
# Harvey Harrison <harvey.harrison@gmail.com>
# Horia Geanta <horia.geanta@freescale.com>
# Ilya Dryomov <idryomov@gmail.com>
# Jakub Kicinski <kuba@kernel.org>
# Jani Nikula <jani.nikula@intel.com>
# Jason Baron <jbaron@redhat.com>
# Jason Gunthorpe <jgg@nvidia.com>
# Jérémy Bobbio <lunar@debian.org>
# Johannes Berg <johannes.berg@intel.com>
# Johannes Weiner <hannes@cmpxchg.org>
# Jonathan Cameron <Jonathan.Cameron@huawei.com>
# Jonathan Corbet <corbet@lwn.net>
# Jonathan Neuschäfer <j.neuschaefer@gmx.net>
# Kamil Rytarowski <n54@gmx.com>
# Kees Cook <kees@kernel.org>
# Laurent Pinchart <laurent.pinchart@ideasonboard.com>
# Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com>
# Linus Torvalds <torvalds@linux-foundation.org>
# Lucas De Marchi <lucas.demarchi@profusion.mobi>
# Mark Rutland <mark.rutland@arm.com>
# Markus Heiser <markus.heiser@darmarit.de>
# Martin Waitz <tali@admingilde.org>
# Masahiro Yamada <masahiroy@kernel.org>
# Matthew Wilcox <willy@infradead.org>
# Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
# Michal Wajdeczko <michal.wajdeczko@intel.com>
# Michael Zucchi
# Mike Rapoport <rppt@linux.ibm.com>
# Niklas Söderlund <niklas.soderlund@corigine.com>
# Nishanth Menon <nm@ti.com>
# Paolo Bonzini <pbonzini@redhat.com>
# Pavan Kumar Linga <pavan.kumar.linga@intel.com>
# Pavel Pisa <pisa@cmp.felk.cvut.cz>
# Peter Maydell <peter.maydell@linaro.org>
# Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
# Randy Dunlap <rdunlap@infradead.org>
# Richard Kennedy <richard@rsk.demon.co.uk>
# Rich Walker <rw@shadow.org.uk>
# Rolf Eike Beer <eike-kernel@sf-tec.de>
# Sakari Ailus <sakari.ailus@linux.intel.com>
# Silvio Fricke <silvio.fricke@gmail.com>
# Simon Huggins
# Tim Waugh <twaugh@redhat.com>
# Tomasz Warniełło <tomasz.warniello@gmail.com>
# Utkarsh Tripathi <utripathi2002@gmail.com>
# valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu>
# Vegard Nossum <vegard.nossum@oracle.com>
# Will Deacon <will.deacon@arm.com>
# Yacine Belkadi <yacine.belkadi.1@gmail.com>
# Yujie Liu <yujie.liu@intel.com>
"""
kernel_doc
==========
Print formatted kernel documentation to stdout
Read C language source or header FILEs, extract embedded
documentation comments, and print formatted documentation
to standard output.
The documentation comments are identified by the "/**"
opening comment mark.
See Documentation/doc-guide/kernel-doc.rst for the
documentation comment syntax.
"""
import argparse
import logging
import os
import sys
# Import Python modules
LIB_DIR = "lib/kdoc"
SRC_DIR = os.path.dirname(os.path.realpath(__file__))
sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR))
from kdoc_files import KernelFiles # pylint: disable=C0413
from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413
DESC = """
Read C language source or header FILEs, extract embedded documentation comments,
and print formatted documentation to standard output.
The documentation comments are identified by the "/**" opening comment mark.
See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax.
"""
EXPORT_FILE_DESC = """
Specify an additional FILE in which to look for EXPORT_SYMBOL information.
May be used multiple times.
"""
EXPORT_DESC = """
Only output documentation for the symbols that have been
exported using EXPORT_SYMBOL() and related macros in any input
FILE or -export-file FILE.
"""
INTERNAL_DESC = """
Only output documentation for the symbols that have NOT been
exported using EXPORT_SYMBOL() and related macros in any input
FILE or -export-file FILE.
"""
FUNCTION_DESC = """
Only output documentation for the given function or DOC: section
title. All other functions and DOC: sections are ignored.
May be used multiple times.
"""
NOSYMBOL_DESC = """
Exclude the specified symbol from the output documentation.
May be used multiple times.
"""
FILES_DESC = """
Header and C source files to be parsed.
"""
WARN_CONTENTS_BEFORE_SECTIONS_DESC = """
Warns if there are contents before sections (deprecated).
This option is kept just for backward-compatibility, but it does nothing,
neither here nor at the original Perl script.
"""
class MsgFormatter(logging.Formatter):
"""Helper class to format warnings on a similar way to kernel-doc.pl"""
def format(self, record):
record.levelname = record.levelname.capitalize()
return logging.Formatter.format(self, record)
def main():
"""Main program"""
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
description=DESC)
# Normal arguments
parser.add_argument("-v", "-verbose", "--verbose", action="store_true",
help="Verbose output, more warnings and other information.")
parser.add_argument("-d", "-debug", "--debug", action="store_true",
help="Enable debug messages")
parser.add_argument("-M", "-modulename", "--modulename",
default="Kernel API",
help="Allow setting a module name at the output.")
parser.add_argument("-l", "-enable-lineno", "--enable_lineno",
action="store_true",
help="Enable line number output (only in ReST mode)")
# Arguments to control the warning behavior
parser.add_argument("-Wreturn", "--wreturn", action="store_true",
help="Warns about the lack of a return markup on functions.")
parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc",
action="store_true",
help="Warns if initial short description is missing")
parser.add_argument("-Wcontents-before-sections",
"--wcontents-before-sections", action="store_true",
help=WARN_CONTENTS_BEFORE_SECTIONS_DESC)
parser.add_argument("-Wall", "--wall", action="store_true",
help="Enable all types of warnings")
parser.add_argument("-Werror", "--werror", action="store_true",
help="Treat warnings as errors.")
parser.add_argument("-export-file", "--export-file", action='append',
help=EXPORT_FILE_DESC)
# Output format mutually-exclusive group
out_group = parser.add_argument_group("Output format selection (mutually exclusive)")
out_fmt = out_group.add_mutually_exclusive_group()
out_fmt.add_argument("-m", "-man", "--man", action="store_true",
help="Output troff manual page format.")
out_fmt.add_argument("-r", "-rst", "--rst", action="store_true",
help="Output reStructuredText format (default).")
out_fmt.add_argument("-N", "-none", "--none", action="store_true",
help="Do not output documentation, only warnings.")
# Output selection mutually-exclusive group
sel_group = parser.add_argument_group("Output selection (mutually exclusive)")
sel_mut = sel_group.add_mutually_exclusive_group()
sel_mut.add_argument("-e", "-export", "--export", action='store_true',
help=EXPORT_DESC)
sel_mut.add_argument("-i", "-internal", "--internal", action='store_true',
help=INTERNAL_DESC)
sel_mut.add_argument("-s", "-function", "--symbol", action='append',
help=FUNCTION_DESC)
# Those are valid for all 3 types of filter
parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append',
help=NOSYMBOL_DESC)
parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections",
action='store_true', help="Don't outputt DOC sections")
parser.add_argument("files", metavar="FILE",
nargs="+", help=FILES_DESC)
args = parser.parse_args()
if args.wall:
args.wreturn = True
args.wshort_desc = True
args.wcontents_before_sections = True
logger = logging.getLogger()
if not args.debug:
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.DEBUG)
formatter = MsgFormatter('%(levelname)s: %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger.addHandler(handler)
python_ver = sys.version_info[:2]
if python_ver < (3,6):
logger.warning("Python 3.6 or later is required by kernel-doc")
# Return 0 here to avoid breaking compilation
sys.exit(0)
if python_ver < (3,7):
logger.warning("Python 3.7 or later is required for correct results")
if args.man:
out_style = ManFormat(modulename=args.modulename)
elif args.none:
out_style = None
else:
out_style = RestFormat()
kfiles = KernelFiles(verbose=args.verbose,
out_style=out_style, werror=args.werror,
wreturn=args.wreturn, wshort_desc=args.wshort_desc,
wcontents_before_sections=args.wcontents_before_sections)
kfiles.parse(args.files, export_file=args.export_file)
for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export,
internal=args.internal, symbol=args.symbol,
nosymbol=args.nosymbol, export_file=args.export_file,
no_doc_sections=args.no_doc_sections):
msg = t[1]
if msg:
print(msg)
error_count = kfiles.errors
if not error_count:
sys.exit(0)
if args.werror:
print(f"{error_count} warnings as errors")
sys.exit(error_count)
if args.verbose:
print(f"{error_count} errors")
if args.none:
sys.exit(0)
sys.exit(error_count)
# Call main method
if __name__ == "__main__":
main()

View file

@ -0,0 +1,291 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
#
# pylint: disable=R0903,R0913,R0914,R0917
"""
Parse lernel-doc tags on multiple kernel source files.
"""
import argparse
import logging
import os
import re
from kdoc_parser import KernelDoc
from kdoc_output import OutputFormat
class GlobSourceFiles:
"""
Parse C source code file names and directories via an Interactor.
"""
def __init__(self, srctree=None, valid_extensions=None):
"""
Initialize valid extensions with a tuple.
If not defined, assume default C extensions (.c and .h)
It would be possible to use python's glob function, but it is
very slow, and it is not interactive. So, it would wait to read all
directories before actually do something.
So, let's use our own implementation.
"""
if not valid_extensions:
self.extensions = (".c", ".h")
else:
self.extensions = valid_extensions
self.srctree = srctree
def _parse_dir(self, dirname):
"""Internal function to parse files recursively"""
with os.scandir(dirname) as obj:
for entry in obj:
name = os.path.join(dirname, entry.name)
if entry.is_dir():
yield from self._parse_dir(name)
if not entry.is_file():
continue
basename = os.path.basename(name)
if not basename.endswith(self.extensions):
continue
yield name
def parse_files(self, file_list, file_not_found_cb):
"""
Define an interator to parse all source files from file_list,
handling directories if any
"""
if not file_list:
return
for fname in file_list:
if self.srctree:
f = os.path.join(self.srctree, fname)
else:
f = fname
if os.path.isdir(f):
yield from self._parse_dir(f)
elif os.path.isfile(f):
yield f
elif file_not_found_cb:
file_not_found_cb(fname)
class KernelFiles():
"""
Parse kernel-doc tags on multiple kernel source files.
There are two type of parsers defined here:
- self.parse_file(): parses both kernel-doc markups and
EXPORT_SYMBOL* macros;
- self.process_export_file(): parses only EXPORT_SYMBOL* macros.
"""
def warning(self, msg):
"""Ancillary routine to output a warning and increment error count"""
self.config.log.warning(msg)
self.errors += 1
def error(self, msg):
"""Ancillary routine to output an error and increment error count"""
self.config.log.error(msg)
self.errors += 1
def parse_file(self, fname):
"""
Parse a single Kernel source.
"""
# Prevent parsing the same file twice if results are cached
if fname in self.files:
return
doc = KernelDoc(self.config, fname)
export_table, entries = doc.parse_kdoc()
self.export_table[fname] = export_table
self.files.add(fname)
self.export_files.add(fname) # parse_kdoc() already check exports
self.results[fname] = entries
def process_export_file(self, fname):
"""
Parses EXPORT_SYMBOL* macros from a single Kernel source file.
"""
# Prevent parsing the same file twice if results are cached
if fname in self.export_files:
return
doc = KernelDoc(self.config, fname)
export_table = doc.parse_export()
if not export_table:
self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}")
export_table = set()
self.export_table[fname] = export_table
self.export_files.add(fname)
def file_not_found_cb(self, fname):
"""
Callback to warn if a file was not found.
"""
self.error(f"Cannot find file {fname}")
def __init__(self, verbose=False, out_style=None,
werror=False, wreturn=False, wshort_desc=False,
wcontents_before_sections=False,
logger=None):
"""
Initialize startup variables and parse all files
"""
if not verbose:
verbose = bool(os.environ.get("KBUILD_VERBOSE", 0))
if out_style is None:
out_style = OutputFormat()
if not werror:
kcflags = os.environ.get("KCFLAGS", None)
if kcflags:
match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags)
if match:
werror = True
# reading this variable is for backwards compat just in case
# someone was calling it with the variable from outside the
# kernel's build system
kdoc_werror = os.environ.get("KDOC_WERROR", None)
if kdoc_werror:
werror = kdoc_werror
# Some variables are global to the parser logic as a whole as they are
# used to send control configuration to KernelDoc class. As such,
# those variables are read-only inside the KernelDoc.
self.config = argparse.Namespace
self.config.verbose = verbose
self.config.werror = werror
self.config.wreturn = wreturn
self.config.wshort_desc = wshort_desc
self.config.wcontents_before_sections = wcontents_before_sections
if not logger:
self.config.log = logging.getLogger("kernel-doc")
else:
self.config.log = logger
self.config.warning = self.warning
self.config.src_tree = os.environ.get("SRCTREE", None)
# Initialize variables that are internal to KernelFiles
self.out_style = out_style
self.errors = 0
self.results = {}
self.files = set()
self.export_files = set()
self.export_table = {}
def parse(self, file_list, export_file=None):
"""
Parse all files
"""
glob = GlobSourceFiles(srctree=self.config.src_tree)
for fname in glob.parse_files(file_list, self.file_not_found_cb):
self.parse_file(fname)
for fname in glob.parse_files(export_file, self.file_not_found_cb):
self.process_export_file(fname)
def out_msg(self, fname, name, arg):
"""
Return output messages from a file name using the output style
filtering.
If output type was not handled by the syler, return None.
"""
# NOTE: we can add rules here to filter out unwanted parts,
# although OutputFormat.msg already does that.
return self.out_style.msg(fname, name, arg)
def msg(self, enable_lineno=False, export=False, internal=False,
symbol=None, nosymbol=None, no_doc_sections=False,
filenames=None, export_file=None):
"""
Interacts over the kernel-doc results and output messages,
returning kernel-doc markups on each interaction
"""
self.out_style.set_config(self.config)
if not filenames:
filenames = sorted(self.results.keys())
glob = GlobSourceFiles(srctree=self.config.src_tree)
for fname in filenames:
function_table = set()
if internal or export:
if not export_file:
export_file = [fname]
for f in glob.parse_files(export_file, self.file_not_found_cb):
function_table |= self.export_table[f]
if symbol:
for s in symbol:
function_table.add(s)
self.out_style.set_filter(export, internal, symbol, nosymbol,
function_table, enable_lineno,
no_doc_sections)
msg = ""
if fname not in self.results:
self.config.log.warning("No kernel-doc for file %s", fname)
continue
for arg in self.results[fname]:
m = self.out_msg(fname, arg.name, arg)
if m is None:
ln = arg.get("ln", 0)
dtype = arg.get('type', "")
self.config.log.warning("%s:%d Can't handle %s",
fname, ln, dtype)
else:
msg += m
if msg:
yield fname, msg

View file

@ -0,0 +1,42 @@
# SPDX-License-Identifier: GPL-2.0
#
# A class that will, eventually, encapsulate all of the parsed data that we
# then pass into the output modules.
#
class KdocItem:
def __init__(self, name, type, start_line, **other_stuff):
self.name = name
self.type = type
self.declaration_start_line = start_line
self.sections = {}
self.sections_start_lines = {}
self.parameterlist = []
self.parameterdesc_start_lines = []
self.parameterdescs = {}
self.parametertypes = {}
#
# Just save everything else into our own dict so that the output
# side can grab it directly as before. As we move things into more
# structured data, this will, hopefully, fade away.
#
self.other_stuff = other_stuff
def get(self, key, default = None):
return self.other_stuff.get(key, default)
def __getitem__(self, key):
return self.get(key)
#
# Tracking of section and parameter information.
#
def set_sections(self, sections, start_lines):
self.sections = sections
self.section_start_lines = start_lines
def set_params(self, names, descs, types, starts):
self.parameterlist = names
self.parameterdescs = descs
self.parametertypes = types
self.parameterdesc_start_lines = starts

View file

@ -0,0 +1,749 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
#
# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917
"""
Implement output filters to print kernel-doc documentation.
The implementation uses a virtual base class (OutputFormat) which
contains a dispatches to virtual methods, and some code to filter
out output messages.
The actual implementation is done on one separate class per each type
of output. Currently, there are output classes for ReST and man/troff.
"""
import os
import re
from datetime import datetime
from kdoc_parser import KernelDoc, type_param
from kdoc_re import KernRe
function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False)
# match expressions used to find embedded type information
type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False)
type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False)
type_func = KernRe(r"(\w+)\(\)", cache=False)
type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
# Special RST handling for func ptr params
type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False)
# Special RST handling for structs with func ptr params
type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False)
type_env = KernRe(r"(\$\w+)", cache=False)
type_enum = KernRe(r"#(enum\s*([_\w]+))", cache=False)
type_struct = KernRe(r"#(struct\s*([_\w]+))", cache=False)
type_typedef = KernRe(r"#(([A-Z][_\w]*))", cache=False)
type_union = KernRe(r"#(union\s*([_\w]+))", cache=False)
type_member = KernRe(r"#([_\w]+)(\.|->)([_\w]+)", cache=False)
type_fallback = KernRe(r"((?!))", cache=False) # this never matches
type_member_func = type_member + KernRe(r"\(\)", cache=False)
class OutputFormat:
"""
Base class for OutputFormat. If used as-is, it means that only
warnings will be displayed.
"""
# output mode.
OUTPUT_ALL = 0 # output all symbols and doc sections
OUTPUT_INCLUDE = 1 # output only specified symbols
OUTPUT_EXPORTED = 2 # output exported symbols
OUTPUT_INTERNAL = 3 # output non-exported symbols
# Virtual member to be overriden at the inherited classes
highlights = []
def __init__(self):
"""Declare internal vars and set mode to OUTPUT_ALL"""
self.out_mode = self.OUTPUT_ALL
self.enable_lineno = None
self.nosymbol = {}
self.symbol = None
self.function_table = None
self.config = None
self.no_doc_sections = False
self.data = ""
def set_config(self, config):
"""
Setup global config variables used by both parser and output.
"""
self.config = config
def set_filter(self, export, internal, symbol, nosymbol, function_table,
enable_lineno, no_doc_sections):
"""
Initialize filter variables according with the requested mode.
Only one choice is valid between export, internal and symbol.
The nosymbol filter can be used on all modes.
"""
self.enable_lineno = enable_lineno
self.no_doc_sections = no_doc_sections
self.function_table = function_table
if symbol:
self.out_mode = self.OUTPUT_INCLUDE
elif export:
self.out_mode = self.OUTPUT_EXPORTED
elif internal:
self.out_mode = self.OUTPUT_INTERNAL
else:
self.out_mode = self.OUTPUT_ALL
if nosymbol:
self.nosymbol = set(nosymbol)
def highlight_block(self, block):
"""
Apply the RST highlights to a sub-block of text.
"""
for r, sub in self.highlights:
block = r.sub(sub, block)
return block
def out_warnings(self, args):
"""
Output warnings for identifiers that will be displayed.
"""
for log_msg in args.warnings:
self.config.warning(log_msg)
def check_doc(self, name, args):
"""Check if DOC should be output"""
if self.no_doc_sections:
return False
if name in self.nosymbol:
return False
if self.out_mode == self.OUTPUT_ALL:
self.out_warnings(args)
return True
if self.out_mode == self.OUTPUT_INCLUDE:
if name in self.function_table:
self.out_warnings(args)
return True
return False
def check_declaration(self, dtype, name, args):
"""
Checks if a declaration should be output or not based on the
filtering criteria.
"""
if name in self.nosymbol:
return False
if self.out_mode == self.OUTPUT_ALL:
self.out_warnings(args)
return True
if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]:
if name in self.function_table:
return True
if self.out_mode == self.OUTPUT_INTERNAL:
if dtype != "function":
self.out_warnings(args)
return True
if name not in self.function_table:
self.out_warnings(args)
return True
return False
def msg(self, fname, name, args):
"""
Handles a single entry from kernel-doc parser
"""
self.data = ""
dtype = args.type
if dtype == "doc":
self.out_doc(fname, name, args)
return self.data
if not self.check_declaration(dtype, name, args):
return self.data
if dtype == "function":
self.out_function(fname, name, args)
return self.data
if dtype == "enum":
self.out_enum(fname, name, args)
return self.data
if dtype == "typedef":
self.out_typedef(fname, name, args)
return self.data
if dtype in ["struct", "union"]:
self.out_struct(fname, name, args)
return self.data
# Warn if some type requires an output logic
self.config.log.warning("doesn't now how to output '%s' block",
dtype)
return None
# Virtual methods to be overridden by inherited classes
# At the base class, those do nothing.
def out_doc(self, fname, name, args):
"""Outputs a DOC block"""
def out_function(self, fname, name, args):
"""Outputs a function"""
def out_enum(self, fname, name, args):
"""Outputs an enum"""
def out_typedef(self, fname, name, args):
"""Outputs a typedef"""
def out_struct(self, fname, name, args):
"""Outputs a struct"""
class RestFormat(OutputFormat):
"""Consts and functions used by ReST output"""
highlights = [
(type_constant, r"``\1``"),
(type_constant2, r"``\1``"),
# Note: need to escape () to avoid func matching later
(type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"),
(type_member, r":c:type:`\1\2\3 <\1>`"),
(type_fp_param, r"**\1\\(\\)**"),
(type_fp_param2, r"**\1\\(\\)**"),
(type_func, r"\1()"),
(type_enum, r":c:type:`\1 <\2>`"),
(type_struct, r":c:type:`\1 <\2>`"),
(type_typedef, r":c:type:`\1 <\2>`"),
(type_union, r":c:type:`\1 <\2>`"),
# in rst this can refer to any type
(type_fallback, r":c:type:`\1`"),
(type_param_ref, r"**\1\2**")
]
blankline = "\n"
sphinx_literal = KernRe(r'^[^.].*::$', cache=False)
sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False)
def __init__(self):
"""
Creates class variables.
Not really mandatory, but it is a good coding style and makes
pylint happy.
"""
super().__init__()
self.lineprefix = ""
def print_lineno(self, ln):
"""Outputs a line number"""
if self.enable_lineno and ln is not None:
ln += 1
self.data += f".. LINENO {ln}\n"
def output_highlight(self, args):
"""
Outputs a C symbol that may require being converted to ReST using
the self.highlights variable
"""
input_text = args
output = ""
in_literal = False
litprefix = ""
block = ""
for line in input_text.strip("\n").split("\n"):
# If we're in a literal block, see if we should drop out of it.
# Otherwise, pass the line straight through unmunged.
if in_literal:
if line.strip(): # If the line is not blank
# If this is the first non-blank line in a literal block,
# figure out the proper indent.
if not litprefix:
r = KernRe(r'^(\s*)')
if r.match(line):
litprefix = '^' + r.group(1)
else:
litprefix = ""
output += line + "\n"
elif not KernRe(litprefix).match(line):
in_literal = False
else:
output += line + "\n"
else:
output += line + "\n"
# Not in a literal block (or just dropped out)
if not in_literal:
block += line + "\n"
if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line):
in_literal = True
litprefix = ""
output += self.highlight_block(block)
block = ""
# Handle any remaining block
if block:
output += self.highlight_block(block)
# Print the output with the line prefix
for line in output.strip("\n").split("\n"):
self.data += self.lineprefix + line + "\n"
def out_section(self, args, out_docblock=False):
"""
Outputs a block section.
This could use some work; it's used to output the DOC: sections, and
starts by putting out the name of the doc section itself, but that
tends to duplicate a header already in the template file.
"""
for section, text in args.sections.items():
# Skip sections that are in the nosymbol_table
if section in self.nosymbol:
continue
if out_docblock:
if not self.out_mode == self.OUTPUT_INCLUDE:
self.data += f".. _{section}:\n\n"
self.data += f'{self.lineprefix}**{section}**\n\n'
else:
self.data += f'{self.lineprefix}**{section}**\n\n'
self.print_lineno(args.section_start_lines.get(section, 0))
self.output_highlight(text)
self.data += "\n"
self.data += "\n"
def out_doc(self, fname, name, args):
if not self.check_doc(name, args):
return
self.out_section(args, out_docblock=True)
def out_function(self, fname, name, args):
oldprefix = self.lineprefix
signature = ""
func_macro = args.get('func_macro', False)
if func_macro:
signature = name
else:
if args.get('functiontype'):
signature = args['functiontype'] + " "
signature += name + " ("
ln = args.declaration_start_line
count = 0
for parameter in args.parameterlist:
if count != 0:
signature += ", "
count += 1
dtype = args.parametertypes.get(parameter, "")
if function_pointer.search(dtype):
signature += function_pointer.group(1) + parameter + function_pointer.group(3)
else:
signature += dtype
if not func_macro:
signature += ")"
self.print_lineno(ln)
if args.get('typedef') or not args.get('functiontype'):
self.data += f".. c:macro:: {name}\n\n"
if args.get('typedef'):
self.data += " **Typedef**: "
self.lineprefix = ""
self.output_highlight(args.get('purpose', ""))
self.data += "\n\n**Syntax**\n\n"
self.data += f" ``{signature}``\n\n"
else:
self.data += f"``{signature}``\n\n"
else:
self.data += f".. c:function:: {signature}\n\n"
if not args.get('typedef'):
self.print_lineno(ln)
self.lineprefix = " "
self.output_highlight(args.get('purpose', ""))
self.data += "\n"
# Put descriptive text into a container (HTML <div>) to help set
# function prototypes apart
self.lineprefix = " "
if args.parameterlist:
self.data += ".. container:: kernelindent\n\n"
self.data += f"{self.lineprefix}**Parameters**\n\n"
for parameter in args.parameterlist:
parameter_name = KernRe(r'\[.*').sub('', parameter)
dtype = args.parametertypes.get(parameter, "")
if dtype:
self.data += f"{self.lineprefix}``{dtype}``\n"
else:
self.data += f"{self.lineprefix}``{parameter}``\n"
self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
self.lineprefix = " "
if parameter_name in args.parameterdescs and \
args.parameterdescs[parameter_name] != KernelDoc.undescribed:
self.output_highlight(args.parameterdescs[parameter_name])
self.data += "\n"
else:
self.data += f"{self.lineprefix}*undescribed*\n\n"
self.lineprefix = " "
self.out_section(args)
self.lineprefix = oldprefix
def out_enum(self, fname, name, args):
oldprefix = self.lineprefix
ln = args.declaration_start_line
self.data += f"\n\n.. c:enum:: {name}\n\n"
self.print_lineno(ln)
self.lineprefix = " "
self.output_highlight(args.get('purpose', ''))
self.data += "\n"
self.data += ".. container:: kernelindent\n\n"
outer = self.lineprefix + " "
self.lineprefix = outer + " "
self.data += f"{outer}**Constants**\n\n"
for parameter in args.parameterlist:
self.data += f"{outer}``{parameter}``\n"
if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed:
self.output_highlight(args.parameterdescs[parameter])
else:
self.data += f"{self.lineprefix}*undescribed*\n\n"
self.data += "\n"
self.lineprefix = oldprefix
self.out_section(args)
def out_typedef(self, fname, name, args):
oldprefix = self.lineprefix
ln = args.declaration_start_line
self.data += f"\n\n.. c:type:: {name}\n\n"
self.print_lineno(ln)
self.lineprefix = " "
self.output_highlight(args.get('purpose', ''))
self.data += "\n"
self.lineprefix = oldprefix
self.out_section(args)
def out_struct(self, fname, name, args):
purpose = args.get('purpose', "")
declaration = args.get('definition', "")
dtype = args.type
ln = args.declaration_start_line
self.data += f"\n\n.. c:{dtype}:: {name}\n\n"
self.print_lineno(ln)
oldprefix = self.lineprefix
self.lineprefix += " "
self.output_highlight(purpose)
self.data += "\n"
self.data += ".. container:: kernelindent\n\n"
self.data += f"{self.lineprefix}**Definition**::\n\n"
self.lineprefix = self.lineprefix + " "
declaration = declaration.replace("\t", self.lineprefix)
self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n"
self.data += f"{declaration}{self.lineprefix}" + "};\n\n"
self.lineprefix = " "
self.data += f"{self.lineprefix}**Members**\n\n"
for parameter in args.parameterlist:
if not parameter or parameter.startswith("#"):
continue
parameter_name = parameter.split("[", maxsplit=1)[0]
if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
continue
self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
self.data += f"{self.lineprefix}``{parameter}``\n"
self.lineprefix = " "
self.output_highlight(args.parameterdescs[parameter_name])
self.lineprefix = " "
self.data += "\n"
self.data += "\n"
self.lineprefix = oldprefix
self.out_section(args)
class ManFormat(OutputFormat):
"""Consts and functions used by man pages output"""
highlights = (
(type_constant, r"\1"),
(type_constant2, r"\1"),
(type_func, r"\\fB\1\\fP"),
(type_enum, r"\\fI\1\\fP"),
(type_struct, r"\\fI\1\\fP"),
(type_typedef, r"\\fI\1\\fP"),
(type_union, r"\\fI\1\\fP"),
(type_param, r"\\fI\1\\fP"),
(type_param_ref, r"\\fI\1\2\\fP"),
(type_member, r"\\fI\1\2\3\\fP"),
(type_fallback, r"\\fI\1\\fP")
)
blankline = ""
date_formats = [
"%a %b %d %H:%M:%S %Z %Y",
"%a %b %d %H:%M:%S %Y",
"%Y-%m-%d",
"%b %d %Y",
"%B %d %Y",
"%m %d %Y",
]
def __init__(self, modulename):
"""
Creates class variables.
Not really mandatory, but it is a good coding style and makes
pylint happy.
"""
super().__init__()
self.modulename = modulename
dt = None
tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP")
if tstamp:
for fmt in self.date_formats:
try:
dt = datetime.strptime(tstamp, fmt)
break
except ValueError:
pass
if not dt:
dt = datetime.now()
self.man_date = dt.strftime("%B %Y")
def output_highlight(self, block):
"""
Outputs a C symbol that may require being highlighted with
self.highlights variable using troff syntax
"""
contents = self.highlight_block(block)
if isinstance(contents, list):
contents = "\n".join(contents)
for line in contents.strip("\n").split("\n"):
line = KernRe(r"^\s*").sub("", line)
if not line:
continue
if line[0] == ".":
self.data += "\\&" + line + "\n"
else:
self.data += line + "\n"
def out_doc(self, fname, name, args):
if not self.check_doc(name, args):
return
self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n"
for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
self.output_highlight(text)
def out_function(self, fname, name, args):
"""output function in man"""
self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
self.data += f"{name} \\- {args['purpose']}\n"
self.data += ".SH SYNOPSIS\n"
if args.get('functiontype', ''):
self.data += f'.B "{args["functiontype"]}" {name}' + "\n"
else:
self.data += f'.B "{name}' + "\n"
count = 0
parenth = "("
post = ","
for parameter in args.parameterlist:
if count == len(args.parameterlist) - 1:
post = ");"
dtype = args.parametertypes.get(parameter, "")
if function_pointer.match(dtype):
# Pointer-to-function
self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n"
else:
dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype)
self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n"
count += 1
parenth = ""
if args.parameterlist:
self.data += ".SH ARGUMENTS\n"
for parameter in args.parameterlist:
parameter_name = re.sub(r'\[.*', '', parameter)
self.data += f'.IP "{parameter}" 12' + "\n"
self.output_highlight(args.parameterdescs.get(parameter_name, ""))
for section, text in args.sections.items():
self.data += f'.SH "{section.upper()}"' + "\n"
self.output_highlight(text)
def out_enum(self, fname, name, args):
self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
self.data += f"enum {name} \\- {args['purpose']}\n"
self.data += ".SH SYNOPSIS\n"
self.data += f"enum {name}" + " {\n"
count = 0
for parameter in args.parameterlist:
self.data += f'.br\n.BI " {parameter}"' + "\n"
if count == len(args.parameterlist) - 1:
self.data += "\n};\n"
else:
self.data += ", \n.br\n"
count += 1
self.data += ".SH Constants\n"
for parameter in args.parameterlist:
parameter_name = KernRe(r'\[.*').sub('', parameter)
self.data += f'.IP "{parameter}" 12' + "\n"
self.output_highlight(args.parameterdescs.get(parameter_name, ""))
for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
self.output_highlight(text)
def out_typedef(self, fname, name, args):
module = self.modulename
purpose = args.get('purpose')
self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
self.data += f"typedef {name} \\- {purpose}\n"
for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
self.output_highlight(text)
def out_struct(self, fname, name, args):
module = self.modulename
purpose = args.get('purpose')
definition = args.get('definition')
self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
self.data += f"{args.type} {name} \\- {purpose}\n"
# Replace tabs with two spaces and handle newlines
declaration = definition.replace("\t", " ")
declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration)
self.data += ".SH SYNOPSIS\n"
self.data += f"{args.type} {name} " + "{" + "\n.br\n"
self.data += f'.BI "{declaration}\n' + "};\n.br\n\n"
self.data += ".SH Members\n"
for parameter in args.parameterlist:
if parameter.startswith("#"):
continue
parameter_name = re.sub(r"\[.*", "", parameter)
if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
continue
self.data += f'.IP "{parameter}" 12' + "\n"
self.output_highlight(args.parameterdescs.get(parameter_name))
for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
self.output_highlight(text)

File diff suppressed because it is too large Load diff

270
scripts/lib/kdoc/kdoc_re.py Normal file
View file

@ -0,0 +1,270 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>.
"""
Regular expression ancillary classes.
Those help caching regular expressions and do matching for kernel-doc.
"""
import re
# Local cache for regular expressions
re_cache = {}
class KernRe:
"""
Helper class to simplify regex declaration and usage,
It calls re.compile for a given pattern. It also allows adding
regular expressions and define sub at class init time.
Regular expressions can be cached via an argument, helping to speedup
searches.
"""
def _add_regex(self, string, flags):
"""
Adds a new regex or re-use it from the cache.
"""
self.regex = re_cache.get(string, None)
if not self.regex:
self.regex = re.compile(string, flags=flags)
if self.cache:
re_cache[string] = self.regex
def __init__(self, string, cache=True, flags=0):
"""
Compile a regular expression and initialize internal vars.
"""
self.cache = cache
self.last_match = None
self._add_regex(string, flags)
def __str__(self):
"""
Return the regular expression pattern.
"""
return self.regex.pattern
def __add__(self, other):
"""
Allows adding two regular expressions into one.
"""
return KernRe(str(self) + str(other), cache=self.cache or other.cache,
flags=self.regex.flags | other.regex.flags)
def match(self, string):
"""
Handles a re.match storing its results
"""
self.last_match = self.regex.match(string)
return self.last_match
def search(self, string):
"""
Handles a re.search storing its results
"""
self.last_match = self.regex.search(string)
return self.last_match
def findall(self, string):
"""
Alias to re.findall
"""
return self.regex.findall(string)
def split(self, string):
"""
Alias to re.split
"""
return self.regex.split(string)
def sub(self, sub, string, count=0):
"""
Alias to re.sub
"""
return self.regex.sub(sub, string, count=count)
def group(self, num):
"""
Returns the group results of the last match
"""
return self.last_match.group(num)
class NestedMatch:
"""
Finding nested delimiters is hard with regular expressions. It is
even harder on Python with its normal re module, as there are several
advanced regular expressions that are missing.
This is the case of this pattern:
'\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;'
which is used to properly match open/close parenthesis of the
string search STRUCT_GROUP(),
Add a class that counts pairs of delimiters, using it to match and
replace nested expressions.
The original approach was suggested by:
https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex
Although I re-implemented it to make it more generic and match 3 types
of delimiters. The logic checks if delimiters are paired. If not, it
will ignore the search string.
"""
# TODO: make NestedMatch handle multiple match groups
#
# Right now, regular expressions to match it are defined only up to
# the start delimiter, e.g.:
#
# \bSTRUCT_GROUP\(
#
# is similar to: STRUCT_GROUP\((.*)\)
# except that the content inside the match group is delimiter's aligned.
#
# The content inside parenthesis are converted into a single replace
# group (e.g. r`\1').
#
# It would be nice to change such definition to support multiple
# match groups, allowing a regex equivalent to.
#
# FOO\((.*), (.*), (.*)\)
#
# it is probably easier to define it not as a regular expression, but
# with some lexical definition like:
#
# FOO(arg1, arg2, arg3)
DELIMITER_PAIRS = {
'{': '}',
'(': ')',
'[': ']',
}
RE_DELIM = re.compile(r'[\{\}\[\]\(\)]')
def _search(self, regex, line):
"""
Finds paired blocks for a regex that ends with a delimiter.
The suggestion of using finditer to match pairs came from:
https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex
but I ended using a different implementation to align all three types
of delimiters and seek for an initial regular expression.
The algorithm seeks for open/close paired delimiters and place them
into a stack, yielding a start/stop position of each match when the
stack is zeroed.
The algorithm shoud work fine for properly paired lines, but will
silently ignore end delimiters that preceeds an start delimiter.
This should be OK for kernel-doc parser, as unaligned delimiters
would cause compilation errors. So, we don't need to rise exceptions
to cover such issues.
"""
stack = []
for match_re in regex.finditer(line):
start = match_re.start()
offset = match_re.end()
d = line[offset - 1]
if d not in self.DELIMITER_PAIRS:
continue
end = self.DELIMITER_PAIRS[d]
stack.append(end)
for match in self.RE_DELIM.finditer(line[offset:]):
pos = match.start() + offset
d = line[pos]
if d in self.DELIMITER_PAIRS:
end = self.DELIMITER_PAIRS[d]
stack.append(end)
continue
# Does the end delimiter match what it is expected?
if stack and d == stack[-1]:
stack.pop()
if not stack:
yield start, offset, pos + 1
break
def search(self, regex, line):
"""
This is similar to re.search:
It matches a regex that it is followed by a delimiter,
returning occurrences only if all delimiters are paired.
"""
for t in self._search(regex, line):
yield line[t[0]:t[2]]
def sub(self, regex, sub, line, count=0):
"""
This is similar to re.sub:
It matches a regex that it is followed by a delimiter,
replacing occurrences only if all delimiters are paired.
if r'\1' is used, it works just like re: it places there the
matched paired data with the delimiter stripped.
If count is different than zero, it will replace at most count
items.
"""
out = ""
cur_pos = 0
n = 0
for start, end, pos in self._search(regex, line):
out += line[cur_pos:start]
# Value, ignoring start/end delimiters
value = line[end:pos - 1]
# replaces \1 at the sub string, if \1 is used there
new_sub = sub
new_sub = new_sub.replace(r'\1', value)
out += new_sub
# Drop end ';' if any
if line[pos] == ';':
pos += 1
cur_pos = pos
n += 1
if count and count >= n:
break
# Append the remaining string
l = len(line)
out += line[cur_pos:l]
return out

View file

@ -228,22 +228,27 @@ static bool event_supported(uint16_t number)
return supported_event_map[number] != UNSUPPORTED_EVENT;
}
static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
static CPAccessResult do_pmreg_access(CPUARMState *env, bool is_pmcr)
{
/*
* Performance monitor registers user accessibility is controlled
* by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable
* by PMUSERENR. MDCR_EL2.TPM/TPMCR and MDCR_EL3.TPM allow configurable
* trapping to EL2 or EL3 for other accesses.
*/
int el = arm_current_el(env);
uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);
if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) {
return CP_ACCESS_TRAP_EL1;
}
if (el < 2 && (mdcr_el2 & MDCR_TPM)) {
return CP_ACCESS_TRAP_EL2;
if (el < 2) {
uint64_t mdcr_el2 = arm_mdcr_el2_eff(env);
if (mdcr_el2 & MDCR_TPM) {
return CP_ACCESS_TRAP_EL2;
}
if (is_pmcr && (mdcr_el2 & MDCR_TPMCR)) {
return CP_ACCESS_TRAP_EL2;
}
}
if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) {
return CP_ACCESS_TRAP_EL3;
@ -252,6 +257,19 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
return CP_ACCESS_OK;
}
static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
return do_pmreg_access(env, false);
}
static CPAccessResult pmreg_access_pmcr(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
{
return do_pmreg_access(env, true);
}
static CPAccessResult pmreg_access_xevcntr(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
@ -1187,14 +1205,14 @@ void define_pm_cpregs(ARMCPU *cpu)
.fgt = FGT_PMCR_EL0,
.type = ARM_CP_IO | ARM_CP_ALIAS,
.fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr),
.accessfn = pmreg_access,
.accessfn = pmreg_access_pmcr,
.readfn = pmcr_read, .raw_readfn = raw_read,
.writefn = pmcr_write, .raw_writefn = raw_write,
};
const ARMCPRegInfo pmcr64 = {
.name = "PMCR_EL0", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0,
.access = PL0_RW, .accessfn = pmreg_access,
.access = PL0_RW, .accessfn = pmreg_access_pmcr,
.fgt = FGT_PMCR_EL0,
.type = ARM_CP_IO,
.fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr),

View file

@ -406,9 +406,14 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64ISAR0, CRC32) != 0;
}
static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id)
static inline bool isar_feature_aa64_lse(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) != 0;
return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2;
}
static inline bool isar_feature_aa64_lse128(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 3;
}
static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id)
@ -604,6 +609,11 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES);
}
static inline bool isar_feature_aa64_cssc(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64ISAR2, CSSC) != 0;
}
static inline bool isar_feature_aa64_lut(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT);
@ -904,6 +914,16 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id)
return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2;
}
static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, TCRX) != 0;
}
static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0;
}
static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id)
{
return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 &&

View file

@ -644,6 +644,12 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el)
if (cpu_isar_feature(aa64_fgt, cpu)) {
env->cp15.scr_el3 |= SCR_FGTEN;
}
if (cpu_isar_feature(aa64_tcr2, cpu)) {
env->cp15.scr_el3 |= SCR_TCR2EN;
}
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
env->cp15.scr_el3 |= SCR_SCTLR2EN;
}
}
if (target_el == 2) {

View file

@ -337,6 +337,7 @@ typedef struct CPUArchState {
};
uint64_t sctlr_el[4];
};
uint64_t sctlr2_el[4]; /* Extension to System control register. */
uint64_t vsctlr; /* Virtualization System control register. */
uint64_t cpacr_el1; /* Architectural feature access control register */
uint64_t cptr_el[4]; /* ARMv8 feature trap registers */
@ -365,6 +366,7 @@ typedef struct CPUArchState {
uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */
/* MMU translation table base control. */
uint64_t tcr_el[4];
uint64_t tcr2_el[3];
uint64_t vtcr_el2; /* Virtualization Translation Control. */
uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */
uint32_t c2_data; /* MPU data cacheable bits. */
@ -1420,6 +1422,19 @@ void pmu_init(ARMCPU *cpu);
#define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */
#define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */
#define SCTLR2_EMEC (1ULL << 1) /* FEAT_MEC */
#define SCTLR2_NMEA (1ULL << 2) /* FEAT_DoubleFault2 */
#define SCTLR2_ENADERR (1ULL << 3) /* FEAT_ADERR */
#define SCTLR2_ENANERR (1ULL << 4) /* FEAT_ANERR */
#define SCTLR2_EASE (1ULL << 5) /* FEAT_DoubleFault2 */
#define SCTLR2_ENIDCP128 (1ULL << 6) /* FEAT_SYSREG128 */
#define SCTLR2_ENPACM (1ULL << 7) /* FEAT_PAuth_LR */
#define SCTLR2_ENPACM0 (1ULL << 8) /* FEAT_PAuth_LR */
#define SCTLR2_CPTA (1ULL << 9) /* FEAT_CPA2 */
#define SCTLR2_CPTA0 (1ULL << 10) /* FEAT_CPA2 */
#define SCTLR2_CPTM (1ULL << 11) /* FEAT_CPA2 */
#define SCTLR2_CPTM0 (1ULL << 12) /* FEAT_CAP2 */
#define CPSR_M (0x1fU)
#define CPSR_T (1U << 5)
#define CPSR_F (1U << 6)
@ -1712,6 +1727,8 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask)
#define SCR_HXEN (1ULL << 38)
#define SCR_TRNDR (1ULL << 40)
#define SCR_ENTP2 (1ULL << 41)
#define SCR_TCR2EN (1ULL << 43)
#define SCR_SCTLR2EN (1ULL << 44)
#define SCR_GPF (1ULL << 48)
#define SCR_NSE (1ULL << 62)

View file

@ -741,6 +741,12 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value)
if (cpu_isar_feature(aa64_ecv, cpu)) {
valid_mask |= SCR_ECVEN;
}
if (cpu_isar_feature(aa64_tcr2, cpu)) {
valid_mask |= SCR_TCR2EN;
}
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
valid_mask |= SCR_SCTLR2EN;
}
} else {
valid_mask &= ~(SCR_RW | SCR_ST);
if (cpu_isar_feature(aa32_ras, cpu)) {
@ -3907,23 +3913,24 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri,
ARMCPU *cpu = env_archcpu(env);
uint64_t valid_mask = 0;
/* FEAT_MOPS adds MSCEn and MCE2 */
if (cpu_isar_feature(aa64_mops, cpu)) {
valid_mask |= HCRX_MSCEN | HCRX_MCE2;
}
/* FEAT_NMI adds TALLINT, VINMI and VFNMI */
if (cpu_isar_feature(aa64_nmi, cpu)) {
valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI;
}
/* FEAT_CMOW adds CMOW */
if (cpu_isar_feature(aa64_cmow, cpu)) {
valid_mask |= HCRX_CMOW;
}
/* FEAT_XS adds FGTnXS, FnXS */
if (cpu_isar_feature(aa64_xs, cpu)) {
valid_mask |= HCRX_FGTNXS | HCRX_FNXS;
}
if (cpu_isar_feature(aa64_tcr2, cpu)) {
valid_mask |= HCRX_TCR2EN;
}
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
valid_mask |= HCRX_SCTLR2EN;
}
/* Clear RES0 bits. */
env->cp15.hcrx_el2 = value & valid_mask;
@ -3981,11 +3988,19 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env)
* This may need to be revisited for future bits.
*/
if (!arm_is_el2_enabled(env)) {
ARMCPU *cpu = env_archcpu(env);
uint64_t hcrx = 0;
if (cpu_isar_feature(aa64_mops, env_archcpu(env))) {
/* MSCEn behaves as 1 if EL2 is not enabled */
/* Bits which whose effective value is 1 if el2 not enabled. */
if (cpu_isar_feature(aa64_mops, cpu)) {
hcrx |= HCRX_MSCEN;
}
if (cpu_isar_feature(aa64_tcr2, cpu)) {
hcrx |= HCRX_TCR2EN;
}
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
hcrx |= HCRX_SCTLR2EN;
}
return hcrx;
}
if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) {
@ -4513,6 +4528,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
static const struct E2HAlias aliases[] = {
{ K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0),
"SCTLR", "SCTLR_EL2", "SCTLR_EL12" },
{ K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3),
"SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 },
{ K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2),
"CPACR", "CPTR_EL2", "CPACR_EL12" },
{ K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0),
@ -4521,6 +4538,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu)
"TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" },
{ K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2),
"TCR_EL1", "TCR_EL2", "TCR_EL12" },
{ K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3),
"TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 },
{ K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0),
"SPSR_EL1", "SPSR_EL2", "SPSR_EL12" },
{ K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1),
@ -5994,6 +6013,133 @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = {
.resetvalue = 0 },
};
static CPAccessResult sctlr2_el2_access(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) < 3
&& arm_feature(env, ARM_FEATURE_EL3)
&& !(env->cp15.scr_el3 & SCR_SCTLR2EN)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
static CPAccessResult sctlr2_el1_access(CPUARMState *env,
const ARMCPRegInfo *ri,
bool isread)
{
CPAccessResult ret = access_tvm_trvm(env, ri, isread);
if (ret != CP_ACCESS_OK) {
return ret;
}
if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_SCTLR2EN)) {
return CP_ACCESS_TRAP_EL2;
}
return sctlr2_el2_access(env, ri, isread);
}
static void sctlr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
uint64_t valid_mask = 0;
value &= valid_mask;
raw_write(env, ri, value);
}
static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
uint64_t valid_mask = 0;
value &= valid_mask;
raw_write(env, ri, value);
}
static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
uint64_t valid_mask = 0;
value &= valid_mask;
raw_write(env, ri, value);
}
static const ARMCPRegInfo sctlr2_reginfo[] = {
{ .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0,
.access = PL1_RW, .accessfn = sctlr2_el1_access,
.writefn = sctlr2_el1_write, .fgt = FGT_SCTLR_EL1,
.nv2_redirect_offset = 0x278 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) },
{ .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0,
.access = PL2_RW, .accessfn = sctlr2_el2_access,
.writefn = sctlr2_el2_write,
.fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) },
{ .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0,
.access = PL3_RW, .writefn = sctlr2_el3_write,
.fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) },
};
static CPAccessResult tcr2_el2_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
if (arm_current_el(env) < 3
&& arm_feature(env, ARM_FEATURE_EL3)
&& !(env->cp15.scr_el3 & SCR_TCR2EN)) {
return CP_ACCESS_TRAP_EL3;
}
return CP_ACCESS_OK;
}
static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri,
bool isread)
{
CPAccessResult ret = access_tvm_trvm(env, ri, isread);
if (ret != CP_ACCESS_OK) {
return ret;
}
if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_TCR2EN)) {
return CP_ACCESS_TRAP_EL2;
}
return tcr2_el2_access(env, ri, isread);
}
static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
uint64_t valid_mask = 0;
value &= valid_mask;
raw_write(env, ri, value);
}
static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri,
uint64_t value)
{
uint64_t valid_mask = 0;
value &= valid_mask;
raw_write(env, ri, value);
}
static const ARMCPRegInfo tcr2_reginfo[] = {
{ .name = "TCR2_EL1", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0,
.access = PL1_RW, .accessfn = tcr2_el1_access,
.writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1,
.nv2_redirect_offset = 0x270 | NV2_REDIR_NV1,
.fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) },
{ .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64,
.opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 2, .crm = 0,
.access = PL2_RW, .accessfn = tcr2_el2_access,
.writefn = tcr2_el2_write,
.fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) },
};
void register_cp_regs_for_features(ARMCPU *cpu)
{
/* Register all the coprocessor registers based on feature bits */
@ -7223,6 +7369,14 @@ void register_cp_regs_for_features(ARMCPU *cpu)
define_arm_cp_regs(cpu, nmi_reginfo);
}
if (cpu_isar_feature(aa64_sctlr2, cpu)) {
define_arm_cp_regs(cpu, sctlr2_reginfo);
}
if (cpu_isar_feature(aa64_tcr2, cpu)) {
define_arm_cp_regs(cpu, tcr2_reginfo);
}
if (cpu_isar_feature(any_predinv, cpu)) {
define_arm_cp_regs(cpu, predinv_reginfo);
}

View file

@ -113,11 +113,6 @@ FIELD(DBGWCR, WT, 20, 1)
FIELD(DBGWCR, MASK, 24, 5)
FIELD(DBGWCR, SSCE, 29, 1)
#define VTCR_NSW (1u << 29)
#define VTCR_NSA (1u << 30)
#define VSTCR_SW VTCR_NSW
#define VSTCR_SA VTCR_NSA
/* Bit definitions for CPACR (AArch32 only) */
FIELD(CPACR, CP10, 20, 2)
FIELD(CPACR, CP11, 22, 2)
@ -201,6 +196,24 @@ FIELD(CPTR_EL3, TCPAC, 31, 1)
#define TTBCR_SH1 (1U << 28)
#define TTBCR_EAE (1U << 31)
#define TCR2_PNCH (1ULL << 0)
#define TCR2_PIE (1ULL << 1)
#define TCR2_E0POE (1ULL << 2)
#define TCR2_POE (1ULL << 3)
#define TCR2_AIE (1ULL << 4)
#define TCR2_D128 (1ULL << 5)
#define TCR2_PTTWI (1ULL << 10)
#define TCR2_HAFT (1ULL << 11)
#define TCR2_AMEC0 (1ULL << 12)
#define TCR2_AMEC1 (1ULL << 13)
#define TCR2_DISCH0 (1ULL << 14)
#define TCR2_DISCH1 (1ULL << 15)
#define TCR2_A2 (1ULL << 16)
#define TCR2_FNG0 (1ULL << 17)
#define TCR2_FNG1 (1ULL << 18)
#define TCR2_FNGNA0 (1ULL << 20)
#define TCR2_FNGNA1 (1ULL << 21)
FIELD(VTCR, T0SZ, 0, 6)
FIELD(VTCR, SL0, 6, 2)
FIELD(VTCR, IRGN0, 8, 2)
@ -220,6 +233,9 @@ FIELD(VTCR, NSA, 30, 1)
FIELD(VTCR, DS, 32, 1)
FIELD(VTCR, SL2, 33, 1)
FIELD(VSTCR, SW, 29, 1)
FIELD(VSTCR, SA, 30, 1)
#define HCRX_ENAS0 (1ULL << 0)
#define HCRX_ENALS (1ULL << 1)
#define HCRX_ENASR (1ULL << 2)
@ -232,6 +248,8 @@ FIELD(VTCR, SL2, 33, 1)
#define HCRX_CMOW (1ULL << 9)
#define HCRX_MCE2 (1ULL << 10)
#define HCRX_MSCEN (1ULL << 11)
#define HCRX_TCR2EN (1ULL << 14)
#define HCRX_SCTLR2EN (1ULL << 15)
#define HPFAR_NS (1ULL << 63)

View file

@ -193,9 +193,9 @@ static ARMMMUIdx ptw_idx_for_stage_2(CPUARMState *env, ARMMMUIdx stage2idx)
return ARMMMUIdx_Phys_Realm;
case ARMSS_Secure:
if (stage2idx == ARMMMUIdx_Stage2_S) {
s2walk_secure = !(env->cp15.vstcr_el2 & VSTCR_SW);
s2walk_secure = !(env->cp15.vstcr_el2 & R_VSTCR_SW_MASK);
} else {
s2walk_secure = !(env->cp15.vtcr_el2 & VTCR_NSW);
s2walk_secure = !(env->cp15.vtcr_el2 & R_VTCR_NSW_MASK);
}
return s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS;
default:
@ -3372,9 +3372,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw,
*/
if (in_space == ARMSS_Secure) {
result->f.attrs.secure =
!(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW))
!(env->cp15.vstcr_el2 & (R_VSTCR_SA_MASK | R_VSTCR_SW_MASK))
&& (ipa_secure
|| !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW)));
|| !(env->cp15.vtcr_el2 & (R_VTCR_NSA_MASK | R_VTCR_NSW_MASK)));
result->f.attrs.space = arm_secure_to_space(result->f.attrs.secure);
}

View file

@ -156,6 +156,16 @@ MOVZ . 10 100101 .. ................ ..... @movw_32
MOVK . 11 100101 .. ................ ..... @movw_64
MOVK . 11 100101 .. ................ ..... @movw_32
# Min/Max (immediate)
@minmaxi_s sf:1 .. ........... imm:s8 rn:5 rd:5 &rri_sf
@minmaxi_u sf:1 .. ........... imm:8 rn:5 rd:5 &rri_sf
SMAX_i . 00 1000111 0000 ........ ..... ..... @minmaxi_s
SMIN_i . 00 1000111 0010 ........ ..... ..... @minmaxi_s
UMAX_i . 00 1000111 0001 ........ ..... ..... @minmaxi_u
UMIN_i . 00 1000111 0011 ........ ..... ..... @minmaxi_u
# Bitfield
&bitfield rd rn sf immr imms
@ -536,6 +546,13 @@ SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic
LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5
# Atomic 128-bit memory operations
&atomic128 rn rt rt2 a r
@atomic128 ........ a:1 r:1 . rt2:5 ...... rn:5 rt:5 &atomic128
LDCLRP 00011001 . . 1 ..... 000100 ..... ..... @atomic128
LDSETP 00011001 . . 1 ..... 001100 ..... ..... @atomic128
SWPP 00011001 . . 1 ..... 100000 ..... ..... @atomic128
# Load/store register (pointer authentication)
# LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous
@ -698,6 +715,11 @@ GMI 1 00 11010110 ..... 000101 ..... ..... @rrr
PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr
SMAX . 00 11010110 ..... 011000 ..... ..... @rrr_sf
SMIN . 00 11010110 ..... 011010 ..... ..... @rrr_sf
UMAX . 00 11010110 ..... 011001 ..... ..... @rrr_sf
UMIN . 00 11010110 ..... 011011 ..... ..... @rrr_sf
# Data Processing (1-source)
@rr . .......... ..... ...... rn:5 rd:5 &rr
@ -711,6 +733,10 @@ REV64 1 10 11010110 00000 000011 ..... ..... @rr
CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf
CLS . 10 11010110 00000 000101 ..... ..... @rr_sf
CTZ . 10 11010110 00000 000110 ..... ..... @rr_sf
CNT . 10 11010110 00000 000111 ..... ..... @rr_sf
ABS . 10 11010110 00000 001000 ..... ..... @rr_sf
&pacaut rd rn z
@pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut

View file

@ -1145,7 +1145,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */
t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */
t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */
t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */
t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* FEAT_LSE, FEAT_LSE128 */
t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */
t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */
t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */
@ -1178,6 +1178,7 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */
t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */
t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */
t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */
SET_IDREG(isar, ID_AA64ISAR2, t);
t = GET_IDREG(isar, ID_AA64PFR0);
@ -1247,7 +1248,11 @@ void aarch64_max_tcg_initfn(Object *obj)
t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */
SET_IDREG(isar, ID_AA64MMFR2, t);
FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */
t = GET_IDREG(isar, ID_AA64MMFR3);
t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */
t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */
t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */
SET_IDREG(isar, ID_AA64MMFR3, t);
t = GET_IDREG(isar, ID_AA64ZFR0);
t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */

View file

@ -3237,7 +3237,7 @@ static bool trans_LDXP(DisasContext *s, arg_stxr *a)
static bool trans_CASP(DisasContext *s, arg_CASP *a)
{
if (!dc_isar_feature(aa64_atomics, s)) {
if (!dc_isar_feature(aa64_lse, s)) {
return false;
}
if (((a->rt | a->rs) & 1) != 0) {
@ -3250,7 +3250,7 @@ static bool trans_CASP(DisasContext *s, arg_CASP *a)
static bool trans_CAS(DisasContext *s, arg_CAS *a)
{
if (!dc_isar_feature(aa64_atomics, s)) {
if (!dc_isar_feature(aa64_lse, s)) {
return false;
}
gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz);
@ -3743,15 +3743,64 @@ static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn,
return true;
}
TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false)
TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true)
TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false)
TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false)
TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false)
TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false)
TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false)
TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false)
TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false)
TRANS_FEAT(LDADD, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false)
TRANS_FEAT(LDCLR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true)
TRANS_FEAT(LDEOR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false)
TRANS_FEAT(LDSET, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false)
TRANS_FEAT(LDSMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false)
TRANS_FEAT(LDSMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false)
TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false)
TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false)
TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false)
typedef void Atomic128ThreeOpFn(TCGv_i128, TCGv_i64, TCGv_i128, TCGArg, MemOp);
static bool do_atomic128_ld(DisasContext *s, arg_atomic128 *a,
Atomic128ThreeOpFn *fn, bool invert)
{
MemOp mop;
int rlo, rhi;
TCGv_i64 clean_addr, tlo, thi;
TCGv_i128 t16;
if (a->rt == 31 || a->rt2 == 31 || a->rt == a->rt2) {
return false;
}
if (a->rn == 31) {
gen_check_sp_alignment(s);
}
mop = check_atomic_align(s, a->rn, MO_128);
clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false,
a->rn != 31, mop);
rlo = (s->be_data == MO_LE ? a->rt : a->rt2);
rhi = (s->be_data == MO_LE ? a->rt2 : a->rt);
tlo = read_cpu_reg(s, rlo, true);
thi = read_cpu_reg(s, rhi, true);
if (invert) {
tcg_gen_not_i64(tlo, tlo);
tcg_gen_not_i64(thi, thi);
}
/*
* The tcg atomic primitives are all full barriers. Therefore we
* can ignore the Acquire and Release bits of this instruction.
*/
t16 = tcg_temp_new_i128();
tcg_gen_concat_i64_i128(t16, tlo, thi);
fn(t16, clean_addr, t16, get_mem_index(s), mop);
tcg_gen_extr_i128_i64(cpu_reg(s, rlo), cpu_reg(s, rhi), t16);
return true;
}
TRANS_FEAT(LDCLRP, aa64_lse128, do_atomic128_ld,
a, tcg_gen_atomic_fetch_and_i128, true)
TRANS_FEAT(LDSETP, aa64_lse128, do_atomic128_ld,
a, tcg_gen_atomic_fetch_or_i128, false)
TRANS_FEAT(SWPP, aa64_lse128, do_atomic128_ld,
a, tcg_gen_atomic_xchg_i128, false)
static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a)
{
@ -3759,7 +3808,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a)
TCGv_i64 clean_addr;
MemOp mop;
if (!dc_isar_feature(aa64_atomics, s) ||
if (!dc_isar_feature(aa64_lse, s) ||
!dc_isar_feature(aa64_rcpc_8_3, s)) {
return false;
}
@ -4552,6 +4601,50 @@ TRANS(SUB_i, gen_rri, a, 1, 1, tcg_gen_sub_i64)
TRANS(ADDS_i, gen_rri, a, 0, 1, a->sf ? gen_add64_CC : gen_add32_CC)
TRANS(SUBS_i, gen_rri, a, 0, 1, a->sf ? gen_sub64_CC : gen_sub32_CC)
/*
* Min/Max (immediate)
*/
static void gen_wrap3_i32(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, NeonGenTwoOpFn fn)
{
TCGv_i32 t1 = tcg_temp_new_i32();
TCGv_i32 t2 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t1, n);
tcg_gen_extrl_i64_i32(t2, m);
fn(t1, t1, t2);
tcg_gen_extu_i32_i64(d, t1);
}
static void gen_smax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
{
gen_wrap3_i32(d, n, m, tcg_gen_smax_i32);
}
static void gen_smin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
{
gen_wrap3_i32(d, n, m, tcg_gen_smin_i32);
}
static void gen_umax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
{
gen_wrap3_i32(d, n, m, tcg_gen_umax_i32);
}
static void gen_umin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m)
{
gen_wrap3_i32(d, n, m, tcg_gen_umin_i32);
}
TRANS_FEAT(SMAX_i, aa64_cssc, gen_rri, a, 0, 0,
a->sf ? tcg_gen_smax_i64 : gen_smax32_i64)
TRANS_FEAT(SMIN_i, aa64_cssc, gen_rri, a, 0, 0,
a->sf ? tcg_gen_smin_i64 : gen_smin32_i64)
TRANS_FEAT(UMAX_i, aa64_cssc, gen_rri, a, 0, 0,
a->sf ? tcg_gen_umax_i64 : gen_umax32_i64)
TRANS_FEAT(UMIN_i, aa64_cssc, gen_rri, a, 0, 0,
a->sf ? tcg_gen_umin_i64 : gen_umin32_i64)
/*
* Add/subtract (immediate, with tags)
*/
@ -8157,6 +8250,28 @@ static bool trans_PACGA(DisasContext *s, arg_rrr *a)
return false;
}
static bool gen_rrr(DisasContext *s, arg_rrr_sf *a, ArithTwoOp fn)
{
TCGv_i64 tcg_rm = cpu_reg(s, a->rm);
TCGv_i64 tcg_rn = cpu_reg(s, a->rn);
TCGv_i64 tcg_rd = cpu_reg(s, a->rd);
fn(tcg_rd, tcg_rn, tcg_rm);
if (!a->sf) {
tcg_gen_ext32u_i64(tcg_rd, tcg_rd);
}
return true;
}
TRANS_FEAT(SMAX, aa64_cssc, gen_rrr, a,
a->sf ? tcg_gen_smax_i64 : gen_smax32_i64)
TRANS_FEAT(SMIN, aa64_cssc, gen_rrr, a,
a->sf ? tcg_gen_smin_i64 : gen_smin32_i64)
TRANS_FEAT(UMAX, aa64_cssc, gen_rrr, a,
a->sf ? tcg_gen_umax_i64 : gen_umax32_i64)
TRANS_FEAT(UMIN, aa64_cssc, gen_rrr, a,
a->sf ? tcg_gen_umin_i64 : gen_umin32_i64)
typedef void ArithOneOp(TCGv_i64, TCGv_i64);
static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn)
@ -8165,13 +8280,22 @@ static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn)
return true;
}
/*
* Perform 32-bit operation fn on the low half of n;
* the high half of the output is zeroed.
*/
static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn)
{
TCGv_i32 t = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t, n);
fn(t, t);
tcg_gen_extu_i32_i64(d, t);
}
static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
TCGv_i32 t32 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t32, tcg_rn);
gen_helper_rbit(t32, t32);
tcg_gen_extu_i32_i64(tcg_rd, t32);
gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit);
}
static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask)
@ -8227,16 +8351,43 @@ static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
TCGv_i32 t32 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t32, tcg_rn);
tcg_gen_clrsb_i32(t32, t32);
tcg_gen_extu_i32_i64(tcg_rd, t32);
gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_clrsb_i32);
}
TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32)
TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32)
static void gen_ctz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
TCGv_i32 t32 = tcg_temp_new_i32();
tcg_gen_extrl_i64_i32(t32, tcg_rn);
tcg_gen_ctzi_i32(t32, t32, 32);
tcg_gen_extu_i32_i64(tcg_rd, t32);
}
static void gen_ctz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
tcg_gen_ctzi_i64(tcg_rd, tcg_rn, 64);
}
static void gen_cnt32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_ctpop_i32);
}
static void gen_abs32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn)
{
gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_abs_i32);
}
TRANS_FEAT(CTZ, aa64_cssc, gen_rr, a->rd, a->rn,
a->sf ? gen_ctz64 : gen_ctz32)
TRANS_FEAT(CNT, aa64_cssc, gen_rr, a->rd, a->rn,
a->sf ? tcg_gen_ctpop_i64 : gen_cnt32)
TRANS_FEAT(ABS, aa64_cssc, gen_rr, a->rd, a->rn,
a->sf ? tcg_gen_abs_i64 : gen_abs32)
static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn)
{
TCGv_i64 tcg_rd, tcg_rn;

View file

@ -801,6 +801,8 @@ typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv_i64,
TCGv_i32, TCGv_i32);
typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv_i64,
TCGv_i64, TCGv_i32);
typedef void (*gen_atomic_op_i128)(TCGv_i128, TCGv_env, TCGv_i64,
TCGv_i128, TCGv_i32);
#ifdef CONFIG_ATOMIC64
# define WITH_ATOMIC64(X) X,
@ -1201,6 +1203,94 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val,
}
}
static void do_nonatomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val,
TCGArg idx, MemOp memop, bool new_val,
void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64))
{
TCGv_i128 t = tcg_temp_ebb_new_i128();
TCGv_i128 r = tcg_temp_ebb_new_i128();
tcg_gen_qemu_ld_i128_int(r, addr, idx, memop);
gen(TCGV128_LOW(t), TCGV128_LOW(r), TCGV128_LOW(val));
gen(TCGV128_HIGH(t), TCGV128_HIGH(r), TCGV128_HIGH(val));
tcg_gen_qemu_st_i128_int(t, addr, idx, memop);
tcg_gen_mov_i128(ret, r);
tcg_temp_free_i128(t);
tcg_temp_free_i128(r);
}
static void do_atomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val,
TCGArg idx, MemOp memop, void * const table[])
{
gen_atomic_op_i128 gen = table[memop & (MO_SIZE | MO_BSWAP)];
if (gen) {
MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx);
TCGv_i64 a64 = maybe_extend_addr64(addr);
gen(ret, tcg_env, a64, val, tcg_constant_i32(oi));
maybe_free_addr64(a64);
return;
}
gen_helper_exit_atomic(tcg_env);
/* Produce a result */
tcg_gen_movi_i64(TCGV128_LOW(ret), 0);
tcg_gen_movi_i64(TCGV128_HIGH(ret), 0);
}
#define GEN_ATOMIC_HELPER128(NAME, OP, NEW) \
static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \
[MO_8] = gen_helper_atomic_##NAME##b, \
[MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \
[MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \
[MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \
[MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \
WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \
WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \
WITH_ATOMIC128([MO_128 | MO_LE] = gen_helper_atomic_##NAME##o_le) \
WITH_ATOMIC128([MO_128 | MO_BE] = gen_helper_atomic_##NAME##o_be) \
}; \
void tcg_gen_atomic_##NAME##_i32_chk(TCGv_i32 ret, TCGTemp *addr, \
TCGv_i32 val, TCGArg idx, \
MemOp memop, TCGType addr_type) \
{ \
tcg_debug_assert(addr_type == tcg_ctx->addr_type); \
tcg_debug_assert((memop & MO_SIZE) <= MO_32); \
if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \
do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \
} else { \
do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \
tcg_gen_##OP##_i32); \
} \
} \
void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \
TCGv_i64 val, TCGArg idx, \
MemOp memop, TCGType addr_type) \
{ \
tcg_debug_assert(addr_type == tcg_ctx->addr_type); \
tcg_debug_assert((memop & MO_SIZE) <= MO_64); \
if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \
do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \
} else { \
do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \
tcg_gen_##OP##_i64); \
} \
} \
void tcg_gen_atomic_##NAME##_i128_chk(TCGv_i128 ret, TCGTemp *addr, \
TCGv_i128 val, TCGArg idx, \
MemOp memop, TCGType addr_type) \
{ \
tcg_debug_assert(addr_type == tcg_ctx->addr_type); \
tcg_debug_assert((memop & MO_SIZE) == MO_128); \
if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \
do_atomic_op_i128(ret, addr, val, idx, memop, table_##NAME); \
} else { \
do_nonatomic_op_i128(ret, addr, val, idx, memop, NEW, \
tcg_gen_##OP##_i64); \
} \
}
#define GEN_ATOMIC_HELPER(NAME, OP, NEW) \
static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \
[MO_8] = gen_helper_atomic_##NAME##b, \
@ -1239,8 +1329,8 @@ void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \
}
GEN_ATOMIC_HELPER(fetch_add, add, 0)
GEN_ATOMIC_HELPER(fetch_and, and, 0)
GEN_ATOMIC_HELPER(fetch_or, or, 0)
GEN_ATOMIC_HELPER128(fetch_and, and, 0)
GEN_ATOMIC_HELPER128(fetch_or, or, 0)
GEN_ATOMIC_HELPER(fetch_xor, xor, 0)
GEN_ATOMIC_HELPER(fetch_smin, smin, 0)
GEN_ATOMIC_HELPER(fetch_umin, umin, 0)
@ -1266,6 +1356,7 @@ static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b)
tcg_gen_mov_i64(r, b);
}
GEN_ATOMIC_HELPER(xchg, mov2, 0)
GEN_ATOMIC_HELPER128(xchg, mov2, 0)
#undef GEN_ATOMIC_HELPER
#undef GEN_ATOMIC_HELPER128

View file

@ -9,7 +9,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
from os.path import join
from qemu_test import QemuSystemTest, Asset
from qemu_test import exec_command, wait_for_console_pattern
@ -77,15 +77,16 @@ echo device_passthrough_test_ok
class Aarch64DevicePassthrough(QemuSystemTest):
# https://github.com/pbo-linaro/qemu-linux-stack
# https://github.com/pbo-linaro/qemu-linux-stack/tree/device_passthrough
# $ ./build.sh && ./archive_artifacts.sh out.tar.xz
#
# Linux kernel is compiled with defconfig +
# IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD
# https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde
ASSET_DEVICE_PASSTHROUGH_STACK = Asset(
('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/'
'download/device_passthrough.tar.xz'),
'812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d')
('https://github.com/pbo-linaro/qemu-linux-stack/'
'releases/download/build/device_passthrough-c3fb84a.tar.xz'),
'15ac2b02bed0c0ea8e3e007de0bcfdaf6fd51c1ba98213f841dc7d01d6f72f04')
# This tests the device passthrough implementation, by booting a VM
# supporting it with two nvme disks attached, and launching a nested VM
@ -96,16 +97,16 @@ class Aarch64DevicePassthrough(QemuSystemTest):
self.vm.set_console()
stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch()
self.archive_extract(stack_path_tar_gz, format="tar")
stack_path_tar = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch()
self.archive_extract(stack_path_tar, format="tar")
stack = self.scratch_file('out')
kernel = os.path.join(stack, 'Image.gz')
rootfs_host = os.path.join(stack, 'host.ext4')
disk_vfio = os.path.join(stack, 'disk_vfio')
disk_iommufd = os.path.join(stack, 'disk_iommufd')
guest_cmd = os.path.join(stack, 'guest.sh')
nested_guest_cmd = os.path.join(stack, 'nested_guest.sh')
kernel = join(stack, 'Image.gz')
rootfs_host = join(stack, 'host.ext4')
disk_vfio = join(stack, 'disk_vfio')
disk_iommufd = join(stack, 'disk_iommufd')
guest_cmd = join(stack, 'guest.sh')
nested_guest_cmd = join(stack, 'nested_guest.sh')
# we generate two random disks
with open(disk_vfio, "wb") as d: d.write(randbytes(512))
with open(disk_iommufd, "wb") as d: d.write(randbytes(1024))

View file

@ -10,21 +10,23 @@
# SPDX-License-Identifier: GPL-2.0-or-later
import os
from os.path import join
import shutil
from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern
from qemu_test import exec_command_and_wait_for_pattern
from test_rme_virt import test_realms_guest
class Aarch64RMESbsaRefMachine(QemuSystemTest):
# Stack is built with OP-TEE build environment from those instructions:
# Stack is inspired from:
# https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/
# https://github.com/pbo-linaro/qemu-rme-stack
# https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_sbsa_release
# ./build.sh && ./archive_artifacts.sh out.tar.xz
ASSET_RME_STACK_SBSA = Asset(
('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/'
'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'),
'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e')
('https://github.com/pbo-linaro/qemu-linux-stack/'
'releases/download/build/rme_sbsa_release-a7f02cf.tar.xz'),
'27d8400b11befb828d6db0cab97e7ae102d0992c928d3dfbf38b24b6cf6c324c')
# This tests the FEAT_RME cpu implementation, by booting a VM supporting it,
# and launching a nested VM using it.
@ -35,35 +37,41 @@ class Aarch64RMESbsaRefMachine(QemuSystemTest):
self.vm.set_console()
stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch()
self.archive_extract(stack_path_tar_gz, format="tar")
stack_path_tar = self.ASSET_RME_STACK_SBSA.fetch()
self.archive_extract(stack_path_tar, format="tar")
rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa')
pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd')
pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd')
virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual')
drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4')
rme_stack = self.scratch_file('.')
pflash0 = join(rme_stack, 'out', 'SBSA_FLASH0.fd')
pflash1 = join(rme_stack, 'out', 'SBSA_FLASH1.fd')
rootfs = join(rme_stack, 'out', 'host.ext4')
self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on')
efi = join(rme_stack, 'out', 'EFI')
os.makedirs(efi, exist_ok=True)
shutil.copyfile(join(rme_stack, 'out', 'Image'), join(efi, 'Image'))
with open(join(efi, 'startup.nsh'), 'w') as startup:
startup.write('fs0:Image nokaslr root=/dev/vda rw init=/init --'
' /host/out/lkvm run --realm'
' -m 256m'
' --restricted_mem'
' --kernel /host/out/Image'
' --disk /host/out/guest.ext4'
' --params "root=/dev/vda rw init=/init"')
self.vm.add_args('-cpu', 'max,x-rme=on')
self.vm.add_args('-smp', '2')
self.vm.add_args('-m', '2G')
self.vm.add_args('-M', 'sbsa-ref')
self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash')
self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash')
self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw')
self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0')
self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0')
self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0')
self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0')
self.vm.add_args('-device', 'virtio-net-pci,netdev=net0')
self.vm.add_args('-netdev', 'user,id=net0')
self.vm.add_args('-drive', f'file=fat:rw:{efi},format=raw')
self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio')
self.vm.add_args('-virtfs',
f'local,path={rme_stack}/,mount_tag=host,'
'security_model=mapped,readonly=off')
self.vm.launch()
# Wait for host VM boot to complete.
wait_for_console_pattern(self, 'Welcome to Buildroot',
failure_message='Synchronous Exception at')
exec_command_and_wait_for_pattern(self, 'root', '#')
test_realms_guest(self)
# Wait for host and guest VM boot to complete.
wait_for_console_pattern(self, 'root@guest',
failure_message='Kernel panic')
if __name__ == '__main__':
QemuSystemTest.main()

View file

@ -9,50 +9,22 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
from os.path import join
from qemu_test import QemuSystemTest, Asset
from qemu_test import exec_command, wait_for_console_pattern
from qemu_test import exec_command_and_wait_for_pattern
def test_realms_guest(test_rme_instance):
# Boot the (nested) guest VM
exec_command(test_rme_instance,
'qemu-system-aarch64 -M virt,gic-version=3 '
'-cpu host -enable-kvm -m 512M '
'-M confidential-guest-support=rme0 '
'-object rme-guest,id=rme0 '
'-device virtio-net-pci,netdev=net0,romfile= '
'-netdev user,id=net0 '
'-kernel /mnt/out/bin/Image '
'-initrd /mnt/out-br/images/rootfs.cpio '
'-serial stdio')
# Detect Realm activation during (nested) guest boot.
wait_for_console_pattern(test_rme_instance,
'SMC_RMI_REALM_ACTIVATE')
# Wait for (nested) guest boot to complete.
wait_for_console_pattern(test_rme_instance,
'Welcome to Buildroot')
exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#')
# query (nested) guest cca report
exec_command(test_rme_instance, 'cca-workload-attestation report')
wait_for_console_pattern(test_rme_instance,
'"cca-platform-hash-algo-id": "sha-256"')
wait_for_console_pattern(test_rme_instance,
'"cca-realm-hash-algo-id": "sha-512"')
wait_for_console_pattern(test_rme_instance,
'"cca-realm-public-key-hash-algo-id": "sha-256"')
class Aarch64RMEVirtMachine(QemuSystemTest):
# Stack is built with OP-TEE build environment from those instructions:
# Stack is inspired from:
# https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/
# https://github.com/pbo-linaro/qemu-rme-stack
# https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_release
# ./build.sh && ./archive_artifacts.sh out.tar.xz
ASSET_RME_STACK_VIRT = Asset(
('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/'
'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'),
'1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad')
('https://github.com/pbo-linaro/qemu-linux-stack/'
'releases/download/build/rme_release-86101e5.tar.xz'),
'e42fef8439badb52a071ac446fc33cff4cb7d61314c7a28fdbe61a11e1faad3a')
# This tests the FEAT_RME cpu implementation, by booting a VM supporting it,
# and launching a nested VM using it.
@ -63,15 +35,16 @@ class Aarch64RMEVirtMachine(QemuSystemTest):
self.vm.set_console()
stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch()
self.archive_extract(stack_path_tar_gz, format="tar")
stack_path_tar = self.ASSET_RME_STACK_VIRT.fetch()
self.archive_extract(stack_path_tar, format="tar")
rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8')
kernel = os.path.join(rme_stack, 'out', 'bin', 'Image')
bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin')
drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4')
rme_stack = self.scratch_file('.')
kernel = join(rme_stack, 'out', 'Image')
bios = join(rme_stack, 'out', 'flash.bin')
rootfs = join(rme_stack, 'out', 'host.ext4')
self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on')
self.vm.add_args('-cpu', 'max,x-rme=on')
self.vm.add_args('-smp', '2')
self.vm.add_args('-m', '2G')
self.vm.add_args('-M', 'virt,acpi=off,'
'virtualization=on,'
@ -79,23 +52,25 @@ class Aarch64RMEVirtMachine(QemuSystemTest):
'gic-version=3')
self.vm.add_args('-bios', bios)
self.vm.add_args('-kernel', kernel)
self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0')
self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0')
self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0')
self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0')
self.vm.add_args('-device', 'virtio-net-pci,netdev=net0')
self.vm.add_args('-netdev', 'user,id=net0')
self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio')
self.vm.add_args('-virtfs',
f'local,path={rme_stack}/,mount_tag=host,'
'security_model=mapped,readonly=off')
# We need to add nokaslr to avoid triggering this sporadic bug:
# https://gitlab.com/qemu-project/qemu/-/issues/2823
self.vm.add_args('-append', 'root=/dev/vda nokaslr')
self.vm.add_args('-append',
'nokaslr root=/dev/vda rw init=/init --'
' /host/out/lkvm run --realm'
' -m 256m'
' --restricted_mem'
' --kernel /host/out/Image'
' --disk /host/out/guest.ext4'
' --params "root=/dev/vda rw init=/init"')
self.vm.launch()
# Wait for host VM boot to complete.
wait_for_console_pattern(self, 'Welcome to Buildroot',
failure_message='Synchronous Exception at')
exec_command_and_wait_for_pattern(self, 'root', '#')
test_realms_guest(self)
# Wait for host and guest VM boot to complete.
wait_for_console_pattern(self, 'root@guest',
failure_message='Kernel panic')
if __name__ == '__main__':
QemuSystemTest.main()

View file

@ -977,7 +977,7 @@ void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds,
* @cb: Pointer to the callback function
* @skip_old_versioned: true if versioned old machine types should be skipped
*
* Call a callback function for every name of all available machines.
* Call a callback function for every name of all available machines.
*/
void qtest_cb_for_every_machine(void (*cb)(const char *machine),
bool skip_old_versioned);