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:
commit
e101d33792
44 changed files with 4389 additions and 2663 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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, '..')
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
102
host/include/aarch64/host/atomic128-cas.h.inc
Normal file
102
host/include/aarch64/host/atomic128-cas.h.inc
Normal 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 */
|
||||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) \
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
2442
scripts/kernel-doc
2442
scripts/kernel-doc
File diff suppressed because it is too large
Load diff
325
scripts/kernel-doc.py
Executable file
325
scripts/kernel-doc.py
Executable 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()
|
||||
291
scripts/lib/kdoc/kdoc_files.py
Normal file
291
scripts/lib/kdoc/kdoc_files.py
Normal 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
|
||||
42
scripts/lib/kdoc/kdoc_item.py
Normal file
42
scripts/lib/kdoc/kdoc_item.py
Normal 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
|
||||
749
scripts/lib/kdoc/kdoc_output.py
Normal file
749
scripts/lib/kdoc/kdoc_output.py
Normal 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)
|
||||
1670
scripts/lib/kdoc/kdoc_parser.py
Normal file
1670
scripts/lib/kdoc/kdoc_parser.py
Normal file
File diff suppressed because it is too large
Load diff
270
scripts/lib/kdoc/kdoc_re.py
Normal file
270
scripts/lib/kdoc/kdoc_re.py
Normal 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
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue