mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
RISC-V updates for v6.19-rc3
- Add probing and userspace reporting support for the standard RISC-V
ISA extensions Zilsd and Zclsd, which implement load/store dual
instructions on RV32
- Abstract the register saving code in setup_sigcontext() so it can be
used for stateful RISC-V ISA extensions beyond the vector extension
- Add the SBI extension ID and some initial data structure definitions
for the RISC-V standard SBI debug trigger extension
- Clean up some code slightly: change some page table functions to
avoid atomic operations oinn !SMP and to avoid unnecessary casts to
atomic_long_t; and use the existing RISCV_FULL_BARRIER macro in
place of some open-coded "fence rw,rw" instructions
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEElRDoIDdEz9/svf2Kx4+xDQu9KksFAmlQ5hUACgkQx4+xDQu9
KkuzjQ/+LV27EOKp3ofhkN6yD+u/MZJv2L8d/L+iuZ6mC7ynerVqY7jHj1bUtSWG
lGJPLLtB3rWITsN0tTkQZDE+LySABACvGsHsH0jDOPu25EUu8fmKL+4UOBpJ4EmH
GrldJCgJjZiV9/NdFqUSwpsZKfuNC3IT5hQvEfFmBGexMb4O0ch2LTrqNTxsS5eu
x1a5DJPbls8olyTYYMoIIbQMU35bHdQWSGEUBvKpisziD11E5c9P9zZcN/X7TnFZ
9wxDCeCmdtiayJzWECRO5HFErxw16IWOBsW7JwanOLlJuE2vi/hEW3bwAAQMaYSh
FvN6ID9d6fE9cuStNfqILuWeF8cogVkowMzEX9ud6hFp8AKE/mAlF4Rd26qIAbwE
Migv/MjSuMKSYfPIcJ+fkrMoPgMeMCoGW34NtSV7MjSujBYozjjnN/Yc5iSu5rdN
16b08rtusMvQI0Eowt21RO7iXRauK1+6Xcag5WZytD+sdMZkPxOepDK/mZijiida
w8FYENlHEjhlF7A9Uiy8igVCjXJg63r3ctcnzB2wskUb+gfJF9IXdpv0O7ZO6IoJ
6Q4OI3oNhHSWic7noXYu+dSbTT4DjlPpUBnI79X7bPo+6Ck3X9fr8YPpHzKnmHN9
aVixfWk8zShNutROkt+3vQYzgNACcNtMT9Uz5Ce2FI3urqALp1M=
=Pgyz
-----END PGP SIGNATURE-----
Merge tag 'riscv-for-linus-6.19-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux
Pull RISC-V updates from Paul Walmsley:
"Nothing exotic here; these are the cleanup and new ISA extension
probing patches (not including CFI):
- Add probing and userspace reporting support for the standard RISC-V
ISA extensions Zilsd and Zclsd, which implement load/store dual
instructions on RV32
- Abstract the register saving code in setup_sigcontext() so it can
be used for stateful RISC-V ISA extensions beyond the vector
extension
- Add the SBI extension ID and some initial data structure
definitions for the RISC-V standard SBI debug trigger extension
- Clean up some code slightly: change some page table functions to
avoid atomic operations oinn !SMP and to avoid unnecessary casts to
atomic_long_t; and use the existing RISCV_FULL_BARRIER macro in
place of some open-coded 'fence rw,rw' instructions"
* tag 'riscv-for-linus-6.19-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/riscv/linux:
riscv: Add SBI debug trigger extension and function ids
riscv/atomic.h: use RISCV_FULL_BARRIER in _arch_atomic* function.
riscv: hwprobe: export Zilsd and Zclsd ISA extensions
riscv: add ISA extension parsing for Zilsd and Zclsd
dt-bindings: riscv: add Zilsd and Zclsd extension descriptions
riscv: mm: use xchg() on non-atomic_long_t variables, not atomic_long_xchg()
riscv: mm: ptep_get_and_clear(): avoid atomic ops when !CONFIG_SMP
riscv: mm: pmdp_huge_get_and_clear(): avoid atomic ops when !CONFIG_SMP
riscv: signal: abstract header saving for setup_sigcontext
This commit is contained in:
commit
03de3e44a7
@ -281,6 +281,14 @@ The following keys are defined:
|
||||
* :c:macro:`RISCV_HWPROBE_EXT_ZICBOP`: The Zicbop extension is supported, as
|
||||
ratified in commit 3dd606f ("Create cmobase-v1.0.pdf") of riscv-CMOs.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_EXT_ZILSD`: The Zilsd extension is supported as
|
||||
defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
|
||||
load/store pair for RV32 with the main manual") of the riscv-isa-manual.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_EXT_ZCLSD`: The Zclsd extension is supported as
|
||||
defined in the RISC-V ISA manual starting from commit f88abf1 ("Integrating
|
||||
load/store pair for RV32 with the main manual") of the riscv-isa-manual.
|
||||
|
||||
* :c:macro:`RISCV_HWPROBE_KEY_CPUPERF_0`: Deprecated. Returns similar values to
|
||||
:c:macro:`RISCV_HWPROBE_KEY_MISALIGNED_SCALAR_PERF`, but the key was
|
||||
mistakenly classified as a bitmask rather than a value.
|
||||
|
||||
@ -377,6 +377,20 @@ properties:
|
||||
guarantee on LR/SC sequences, as ratified in commit b1d806605f87
|
||||
("Updated to ratified state.") of the riscv profiles specification.
|
||||
|
||||
- const: zilsd
|
||||
description:
|
||||
The standard Zilsd extension which provides support for aligned
|
||||
register-pair load and store operations in 32-bit instruction
|
||||
encodings, as ratified in commit f88abf1 ("Integrating
|
||||
load/store pair for RV32 with the main manual") of riscv-isa-manual.
|
||||
|
||||
- const: zclsd
|
||||
description:
|
||||
The Zclsd extension implements the compressed (16-bit) version of the
|
||||
Load/Store Pair for RV32. As with Zilsd, this extension was ratified
|
||||
in commit f88abf1 ("Integrating load/store pair for RV32 with the
|
||||
main manual") of riscv-isa-manual.
|
||||
|
||||
- const: zk
|
||||
description:
|
||||
The standard Zk Standard Scalar cryptography extension as ratified
|
||||
@ -882,6 +896,16 @@ properties:
|
||||
anyOf:
|
||||
- const: v
|
||||
- const: zve32x
|
||||
# Zclsd depends on Zilsd and Zca
|
||||
- if:
|
||||
contains:
|
||||
anyOf:
|
||||
- const: zclsd
|
||||
then:
|
||||
contains:
|
||||
allOf:
|
||||
- const: zilsd
|
||||
- const: zca
|
||||
|
||||
allOf:
|
||||
# Zcf extension does not exist on rv64
|
||||
@ -899,6 +923,18 @@ allOf:
|
||||
not:
|
||||
contains:
|
||||
const: zcf
|
||||
# Zilsd extension does not exist on rv64
|
||||
- if:
|
||||
properties:
|
||||
riscv,isa-base:
|
||||
contains:
|
||||
const: rv64i
|
||||
then:
|
||||
properties:
|
||||
riscv,isa-extensions:
|
||||
not:
|
||||
contains:
|
||||
const: zilsd
|
||||
|
||||
additionalProperties: true
|
||||
...
|
||||
|
||||
@ -203,7 +203,7 @@ ATOMIC_OPS(xor, xor, i)
|
||||
" add %[rc], %[p], %[a]\n" \
|
||||
" sc." sfx ".rl %[rc], %[rc], %[c]\n" \
|
||||
" bnez %[rc], 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
RISCV_FULL_BARRIER \
|
||||
"1:\n" \
|
||||
: [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \
|
||||
: [a]"r" (_a), [u]"r" (_u) \
|
||||
@ -242,7 +242,7 @@ static __always_inline s64 arch_atomic64_fetch_add_unless(atomic64_t *v, s64 a,
|
||||
" addi %[rc], %[p], 1\n" \
|
||||
" sc." sfx ".rl %[rc], %[rc], %[c]\n" \
|
||||
" bnez %[rc], 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
RISCV_FULL_BARRIER \
|
||||
"1:\n" \
|
||||
: [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \
|
||||
: \
|
||||
@ -268,7 +268,7 @@ static __always_inline bool arch_atomic_inc_unless_negative(atomic_t *v)
|
||||
" addi %[rc], %[p], -1\n" \
|
||||
" sc." sfx ".rl %[rc], %[rc], %[c]\n" \
|
||||
" bnez %[rc], 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
RISCV_FULL_BARRIER \
|
||||
"1:\n" \
|
||||
: [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \
|
||||
: \
|
||||
@ -294,7 +294,7 @@ static __always_inline bool arch_atomic_dec_unless_positive(atomic_t *v)
|
||||
" bltz %[rc], 1f\n" \
|
||||
" sc." sfx ".rl %[rc], %[rc], %[c]\n" \
|
||||
" bnez %[rc], 0b\n" \
|
||||
" fence rw, rw\n" \
|
||||
RISCV_FULL_BARRIER \
|
||||
"1:\n" \
|
||||
: [p]"=&r" (_prev), [rc]"=&r" (_rc), [c]"+A" (counter) \
|
||||
: \
|
||||
|
||||
@ -108,6 +108,8 @@
|
||||
#define RISCV_ISA_EXT_ZICBOP 99
|
||||
#define RISCV_ISA_EXT_SVRSW60T59B 100
|
||||
#define RISCV_ISA_EXT_ZALASR 101
|
||||
#define RISCV_ISA_EXT_ZILSD 102
|
||||
#define RISCV_ISA_EXT_ZCLSD 103
|
||||
|
||||
#define RISCV_ISA_EXT_XLINUXENVCFG 127
|
||||
|
||||
|
||||
@ -660,7 +660,13 @@ extern int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long a
|
||||
static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
|
||||
unsigned long address, pte_t *ptep)
|
||||
{
|
||||
pte_t pte = __pte(atomic_long_xchg((atomic_long_t *)ptep, 0));
|
||||
#ifdef CONFIG_SMP
|
||||
pte_t pte = __pte(xchg(&ptep->pte, 0));
|
||||
#else
|
||||
pte_t pte = *ptep;
|
||||
|
||||
set_pte(ptep, __pte(0));
|
||||
#endif
|
||||
|
||||
page_table_check_pte_clear(mm, pte);
|
||||
|
||||
@ -997,7 +1003,13 @@ static inline int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||
static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
|
||||
unsigned long address, pmd_t *pmdp)
|
||||
{
|
||||
pmd_t pmd = __pmd(atomic_long_xchg((atomic_long_t *)pmdp, 0));
|
||||
#ifdef CONFIG_SMP
|
||||
pmd_t pmd = __pmd(xchg(&pmdp->pmd, 0));
|
||||
#else
|
||||
pmd_t pmd = *pmdp;
|
||||
|
||||
pmd_clear(pmdp);
|
||||
#endif
|
||||
|
||||
page_table_check_pmd_clear(mm, pmd);
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ enum sbi_ext_id {
|
||||
SBI_EXT_NACL = 0x4E41434C,
|
||||
SBI_EXT_FWFT = 0x46574654,
|
||||
SBI_EXT_MPXY = 0x4D505859,
|
||||
SBI_EXT_DBTR = 0x44425452,
|
||||
|
||||
/* Experimentals extensions must lie within this range */
|
||||
SBI_EXT_EXPERIMENTAL_START = 0x08000000,
|
||||
@ -505,6 +506,34 @@ enum sbi_mpxy_rpmi_attribute_id {
|
||||
#define SBI_MPXY_CHAN_CAP_SEND_WITHOUT_RESP BIT(4)
|
||||
#define SBI_MPXY_CHAN_CAP_GET_NOTIFICATIONS BIT(5)
|
||||
|
||||
/* SBI debug triggers function IDs */
|
||||
enum sbi_ext_dbtr_fid {
|
||||
SBI_EXT_DBTR_NUM_TRIGGERS = 0,
|
||||
SBI_EXT_DBTR_SETUP_SHMEM,
|
||||
SBI_EXT_DBTR_TRIG_READ,
|
||||
SBI_EXT_DBTR_TRIG_INSTALL,
|
||||
SBI_EXT_DBTR_TRIG_UPDATE,
|
||||
SBI_EXT_DBTR_TRIG_UNINSTALL,
|
||||
SBI_EXT_DBTR_TRIG_ENABLE,
|
||||
SBI_EXT_DBTR_TRIG_DISABLE,
|
||||
};
|
||||
|
||||
struct sbi_dbtr_data_msg {
|
||||
unsigned long tstate;
|
||||
unsigned long tdata1;
|
||||
unsigned long tdata2;
|
||||
unsigned long tdata3;
|
||||
};
|
||||
|
||||
struct sbi_dbtr_id_msg {
|
||||
unsigned long idx;
|
||||
};
|
||||
|
||||
union sbi_dbtr_shmem_entry {
|
||||
struct sbi_dbtr_data_msg data;
|
||||
struct sbi_dbtr_id_msg id;
|
||||
};
|
||||
|
||||
/* SBI spec version fields */
|
||||
#define SBI_SPEC_VERSION_DEFAULT 0x1
|
||||
#define SBI_SPEC_VERSION_MAJOR_SHIFT 24
|
||||
|
||||
@ -424,6 +424,9 @@ static inline bool riscv_v_vstate_ctrl_user_allowed(void) { return false; }
|
||||
#define riscv_v_thread_free(tsk) do {} while (0)
|
||||
#define riscv_v_setup_ctx_cache() do {} while (0)
|
||||
#define riscv_v_thread_alloc(tsk) do {} while (0)
|
||||
#define get_cpu_vector_context() do {} while (0)
|
||||
#define put_cpu_vector_context() do {} while (0)
|
||||
#define riscv_v_vstate_set_restore(task, regs) do {} while (0)
|
||||
|
||||
#endif /* CONFIG_RISCV_ISA_V */
|
||||
|
||||
|
||||
@ -84,6 +84,9 @@ struct riscv_hwprobe {
|
||||
#define RISCV_HWPROBE_EXT_ZABHA (1ULL << 58)
|
||||
#define RISCV_HWPROBE_EXT_ZALASR (1ULL << 59)
|
||||
#define RISCV_HWPROBE_EXT_ZICBOP (1ULL << 60)
|
||||
#define RISCV_HWPROBE_EXT_ZILSD (1ULL << 61)
|
||||
#define RISCV_HWPROBE_EXT_ZCLSD (1ULL << 62)
|
||||
|
||||
#define RISCV_HWPROBE_KEY_CPUPERF_0 5
|
||||
#define RISCV_HWPROBE_MISALIGNED_UNKNOWN (0 << 0)
|
||||
#define RISCV_HWPROBE_MISALIGNED_EMULATED (1 << 0)
|
||||
|
||||
@ -242,6 +242,28 @@ static int riscv_ext_zcf_validate(const struct riscv_isa_ext_data *data,
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
static int riscv_ext_zilsd_validate(const struct riscv_isa_ext_data *data,
|
||||
const unsigned long *isa_bitmap)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_64BIT))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int riscv_ext_zclsd_validate(const struct riscv_isa_ext_data *data,
|
||||
const unsigned long *isa_bitmap)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_64BIT))
|
||||
return -EINVAL;
|
||||
|
||||
if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZILSD) &&
|
||||
__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZCA))
|
||||
return 0;
|
||||
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
static int riscv_vector_f_validate(const struct riscv_isa_ext_data *data,
|
||||
const unsigned long *isa_bitmap)
|
||||
{
|
||||
@ -484,6 +506,8 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
|
||||
__RISCV_ISA_EXT_DATA_VALIDATE(zcd, RISCV_ISA_EXT_ZCD, riscv_ext_zcd_validate),
|
||||
__RISCV_ISA_EXT_DATA_VALIDATE(zcf, RISCV_ISA_EXT_ZCF, riscv_ext_zcf_validate),
|
||||
__RISCV_ISA_EXT_DATA_VALIDATE(zcmop, RISCV_ISA_EXT_ZCMOP, riscv_ext_zca_depends),
|
||||
__RISCV_ISA_EXT_DATA_VALIDATE(zclsd, RISCV_ISA_EXT_ZCLSD, riscv_ext_zclsd_validate),
|
||||
__RISCV_ISA_EXT_DATA_VALIDATE(zilsd, RISCV_ISA_EXT_ZILSD, riscv_ext_zilsd_validate),
|
||||
__RISCV_ISA_EXT_DATA(zba, RISCV_ISA_EXT_ZBA),
|
||||
__RISCV_ISA_EXT_DATA(zbb, RISCV_ISA_EXT_ZBB),
|
||||
__RISCV_ISA_EXT_DATA(zbc, RISCV_ISA_EXT_ZBC),
|
||||
|
||||
@ -68,18 +68,19 @@ static long save_fp_state(struct pt_regs *regs,
|
||||
#define restore_fp_state(task, regs) (0)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RISCV_ISA_V
|
||||
|
||||
static long save_v_state(struct pt_regs *regs, void __user **sc_vec)
|
||||
static long save_v_state(struct pt_regs *regs, void __user *sc_vec)
|
||||
{
|
||||
struct __riscv_ctx_hdr __user *hdr;
|
||||
struct __sc_riscv_v_state __user *state;
|
||||
void __user *datap;
|
||||
long err;
|
||||
|
||||
hdr = *sc_vec;
|
||||
/* Place state to the user's signal context space after the hdr */
|
||||
state = (struct __sc_riscv_v_state __user *)(hdr + 1);
|
||||
if (!IS_ENABLED(CONFIG_RISCV_ISA_V) ||
|
||||
!((has_vector() || has_xtheadvector()) &&
|
||||
riscv_v_vstate_query(regs)))
|
||||
return 0;
|
||||
|
||||
/* Place state to the user's signal context space */
|
||||
state = (struct __sc_riscv_v_state __user *)sc_vec;
|
||||
/* Point datap right after the end of __sc_riscv_v_state */
|
||||
datap = state + 1;
|
||||
|
||||
@ -97,15 +98,11 @@ static long save_v_state(struct pt_regs *regs, void __user **sc_vec)
|
||||
err |= __put_user((__force void *)datap, &state->v_state.datap);
|
||||
/* Copy the whole vector content to user space datap. */
|
||||
err |= __copy_to_user(datap, current->thread.vstate.datap, riscv_v_vsize);
|
||||
/* Copy magic to the user space after saving all vector conetext */
|
||||
err |= __put_user(RISCV_V_MAGIC, &hdr->magic);
|
||||
err |= __put_user(riscv_v_sc_size, &hdr->size);
|
||||
if (unlikely(err))
|
||||
return err;
|
||||
return -EFAULT;
|
||||
|
||||
/* Only progress the sv_vec if everything has done successfully */
|
||||
*sc_vec += riscv_v_sc_size;
|
||||
return 0;
|
||||
/* Only return the size if everything has done successfully */
|
||||
return riscv_v_sc_size;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -142,10 +139,20 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)
|
||||
*/
|
||||
return copy_from_user(current->thread.vstate.datap, datap, riscv_v_vsize);
|
||||
}
|
||||
#else
|
||||
#define save_v_state(task, regs) (0)
|
||||
#define __restore_v_state(task, regs) (0)
|
||||
#endif
|
||||
|
||||
struct arch_ext_priv {
|
||||
__u32 magic;
|
||||
long (*save)(struct pt_regs *regs, void __user *sc_vec);
|
||||
};
|
||||
|
||||
struct arch_ext_priv arch_ext_list[] = {
|
||||
{
|
||||
.magic = RISCV_V_MAGIC,
|
||||
.save = &save_v_state,
|
||||
},
|
||||
};
|
||||
|
||||
const size_t nr_arch_exts = ARRAY_SIZE(arch_ext_list);
|
||||
|
||||
static long restore_sigcontext(struct pt_regs *regs,
|
||||
struct sigcontext __user *sc)
|
||||
@ -270,7 +277,8 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
|
||||
{
|
||||
struct sigcontext __user *sc = &frame->uc.uc_mcontext;
|
||||
struct __riscv_ctx_hdr __user *sc_ext_ptr = &sc->sc_extdesc.hdr;
|
||||
long err;
|
||||
struct arch_ext_priv *arch_ext;
|
||||
long err, i, ext_size;
|
||||
|
||||
/* sc_regs is structured the same as the start of pt_regs */
|
||||
err = __copy_to_user(&sc->sc_regs, regs, sizeof(sc->sc_regs));
|
||||
@ -278,8 +286,20 @@ static long setup_sigcontext(struct rt_sigframe __user *frame,
|
||||
if (has_fpu())
|
||||
err |= save_fp_state(regs, &sc->sc_fpregs);
|
||||
/* Save the vector state. */
|
||||
if ((has_vector() || has_xtheadvector()) && riscv_v_vstate_query(regs))
|
||||
err |= save_v_state(regs, (void __user **)&sc_ext_ptr);
|
||||
for (i = 0; i < nr_arch_exts; i++) {
|
||||
arch_ext = &arch_ext_list[i];
|
||||
if (!arch_ext->save)
|
||||
continue;
|
||||
|
||||
ext_size = arch_ext->save(regs, sc_ext_ptr + 1);
|
||||
if (ext_size <= 0) {
|
||||
err |= ext_size;
|
||||
} else {
|
||||
err |= __put_user(arch_ext->magic, &sc_ext_ptr->magic);
|
||||
err |= __put_user(ext_size, &sc_ext_ptr->size);
|
||||
sc_ext_ptr = (void *)sc_ext_ptr + ext_size;
|
||||
}
|
||||
}
|
||||
/* Write zero to fp-reserved space and check it on restore_sigcontext */
|
||||
err |= __put_user(0, &sc->sc_extdesc.reserved);
|
||||
/* And put END __riscv_ctx_hdr at the end. */
|
||||
|
||||
@ -121,6 +121,7 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
|
||||
EXT_KEY(ZBS);
|
||||
EXT_KEY(ZCA);
|
||||
EXT_KEY(ZCB);
|
||||
EXT_KEY(ZCLSD);
|
||||
EXT_KEY(ZCMOP);
|
||||
EXT_KEY(ZICBOM);
|
||||
EXT_KEY(ZICBOP);
|
||||
@ -130,6 +131,7 @@ static void hwprobe_isa_ext0(struct riscv_hwprobe *pair,
|
||||
EXT_KEY(ZIHINTNTL);
|
||||
EXT_KEY(ZIHINTPAUSE);
|
||||
EXT_KEY(ZIHPM);
|
||||
EXT_KEY(ZILSD);
|
||||
EXT_KEY(ZIMOP);
|
||||
EXT_KEY(ZKND);
|
||||
EXT_KEY(ZKNE);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user