2023-05-03 14:41:35

by Piyush Malgujar

[permalink] [raw]
Subject: [PATCH] ACPI: APEI: EINJ: EINJV2 support added

Added changes to support EINJV2 from ACPI V6.5. This adds
new additional actions: EINJV2_SET_ERROR_TYPE and
EINJV2_GET_ERROR_TYPE which supports PCIe,processor and
memory error for single component syndrome.

Signed-off-by: Piyush Malgujar <[email protected]>
---
drivers/acpi/apei/einj.c | 172 +++++++++++++++++++++++++++++++++++----
include/acpi/actbl1.h | 4 +-
2 files changed, 157 insertions(+), 19 deletions(-)

diff --git a/drivers/acpi/apei/einj.c b/drivers/acpi/apei/einj.c
index 013eb621dc92a7c11b00577a890c06fc64453a2c..97f87ad125af79b2a6f73c0741f728cb9c799992 100644
--- a/drivers/acpi/apei/einj.c
+++ b/drivers/acpi/apei/einj.c
@@ -32,6 +32,7 @@
#define SLEEP_UNIT_MAX 5000 /* 5ms */
/* Firmware should respond within 1 seconds */
#define FIRMWARE_TIMEOUT (1 * USEC_PER_SEC)
+#define ACPI65_EINJV2_SUPP BIT(30)
#define ACPI5_VENDOR_BIT BIT(31)
#define MEM_ERROR_MASK (ACPI_EINJ_MEMORY_CORRECTABLE | \
ACPI_EINJ_MEMORY_UNCORRECTABLE | \
@@ -42,6 +43,51 @@
*/
static int acpi5;

+/*
+ * ACPI version 6.5 provides a EINJV2_SET_ERROR_TYPE action.
+ */
+static int einjv2_supp;
+
+struct syndrome_array {
+ union {
+ u32 acpi_id;
+ u32 device_id;
+ u32 pcie_sbdf;
+ u8 fru_id[16];
+ } comp_id;
+ union {
+ u32 proc_synd;
+ u32 mem_synd;
+ u32 pcie_synd;
+ u8 vendor_synd[16];
+ } comp_synd;
+};
+
+struct einjv2_set_error_type {
+ u32 type;
+ u8 type_code;
+ u8 flags[3];
+ u32 length;
+ u32 severity;
+ u64 memory_address;
+ u64 memory_address_range;
+ u32 syndrome_count;
+ struct syndrome_array array[0];
+};
+
+enum {
+ EINJV2_PROCESSOR_ERROR = 0x1,
+ EINJV2_MEMORY_ERROR = 0x2,
+ EINJV2_PCIE_ERROR = 0x4,
+};
+
+enum {
+ EINJV2_FLAG_ADD_VALID = 0x1,
+ EINJV2_FLAG_ADD_RANGE_VALID = 0x2,
+ EINJV2_FLAG_SEVERITY_VALID = 0x4,
+ EINJV2_FLAG_COMP_VALID = 0x8,
+};
+
struct set_error_type_with_address {
u32 type;
u32 vendor_extension;
@@ -155,6 +201,13 @@ static int __einj_get_available_error_type(u32 *type)
return rc;
*type = apei_exec_ctx_get_output(&ctx);

+ if (*type & ACPI65_EINJV2_SUPP) {
+ rc = apei_exec_run(&ctx, ACPI_EINJV2_GET_ERROR_TYPE);
+ if (rc)
+ return rc;
+ *type = apei_exec_ctx_get_output(&ctx);
+ }
+
return 0;
}

@@ -205,7 +258,7 @@ static void check_vendor_extension(u64 paddr,
static void *einj_get_parameter_address(void)
{
int i;
- u64 pa_v4 = 0, pa_v5 = 0;
+ u64 pa_v4 = 0, pa_v5 = 0, pa_v65 = 0;
struct acpi_whea_header *entry;

entry = EINJ_TAB_ENTRY(einj_tab);
@@ -220,8 +273,22 @@ static void *einj_get_parameter_address(void)
entry->register_region.space_id ==
ACPI_ADR_SPACE_SYSTEM_MEMORY)
pa_v5 = get_unaligned(&entry->register_region.address);
+ if (entry->action == ACPI_EINJV2_SET_ERROR_TYPE &&
+ entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
+ entry->register_region.space_id ==
+ ACPI_ADR_SPACE_SYSTEM_MEMORY)
+ pa_v65 = get_unaligned(&entry->register_region.address);
entry++;
}
+ if (pa_v65) {
+ struct einjv2_set_error_type *v65param;
+
+ v65param = acpi_os_map_iomem(pa_v65, sizeof(*v65param));
+ if (v65param) {
+ einjv2_supp = 1;
+ return v65param;
+ }
+ }
if (pa_v5) {
struct set_error_type_with_address *v5param;

@@ -356,7 +423,9 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
* This will cause resource conflict with regular memory. So
* remove it from trigger table resources.
*/
- if ((param_extension || acpi5) && (type & MEM_ERROR_MASK) && param2) {
+ if ((param_extension || acpi5 || einjv2_supp) &&
+ ((einjv2_supp && (type & EINJV2_MEMORY_ERROR)) ||
+ type & MEM_ERROR_MASK) && param2) {
struct apei_resources addr_resources;

apei_resources_init(&addr_resources);
@@ -402,7 +471,7 @@ static int __einj_error_trigger(u64 trigger_paddr, u32 type,
}

static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
- u64 param3, u64 param4)
+ u64 param3, u64 param4, u64 param5)
{
struct apei_exec_context ctx;
u64 val, trigger_paddr, timeout = FIRMWARE_TIMEOUT;
@@ -414,7 +483,46 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
if (rc)
return rc;
apei_exec_ctx_set_input(&ctx, type);
- if (acpi5) {
+
+ if (einjv2_supp) {
+ struct einjv2_set_error_type *v65param = einj_param;
+
+ v65param->type = type;
+ v65param->flags[0] = flags;
+
+ if (v65param->flags[0] & EINJV2_FLAG_COMP_VALID) {
+ switch (v65param->type) {
+ case EINJV2_PROCESSOR_ERROR:
+ v65param->array[0].comp_id.acpi_id = param1;
+ v65param->array[0].comp_synd.proc_synd = param2;
+ break;
+ case EINJV2_MEMORY_ERROR:
+ v65param->array[0].comp_id.device_id = param3;
+ v65param->array[0].comp_synd.mem_synd = param4;
+ break;
+ case EINJV2_PCIE_ERROR:
+ v65param->array[0].comp_id.pcie_sbdf = param1;
+ v65param->array[0].comp_synd.pcie_synd = param2;
+ break;
+ }
+ }
+
+ if (v65param->type & EINJV2_MEMORY_ERROR) {
+ if (v65param->flags[0] & EINJV2_FLAG_ADD_VALID)
+ v65param->memory_address = param1;
+ if (v65param->flags[0] & EINJV2_FLAG_ADD_RANGE_VALID)
+ v65param->memory_address_range = param2;
+ if (v65param->flags[0] & EINJV2_FLAG_SEVERITY_VALID)
+ v65param->severity = param5;
+ } else {
+ if (v65param->flags[0] & EINJV2_FLAG_SEVERITY_VALID)
+ v65param->severity = param3;
+ }
+ v65param->syndrome_count = 1;
+ v65param->length = sizeof(struct einjv2_set_error_type) +
+ ((v65param->syndrome_count) *
+ sizeof(struct syndrome_array));
+ } else if (acpi5) {
struct set_error_type_with_address *v5param = einj_param;

v5param->type = type;
@@ -514,15 +622,23 @@ static int __einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,

/* Inject the specified hardware error */
static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
- u64 param3, u64 param4)
+ u64 param3, u64 param4, u64 param5)
{
int rc;
u64 base_addr, size;

/* If user manually set "flags", make sure it is legal */
- if (flags && (flags &
- ~(SETWA_FLAGS_APICID|SETWA_FLAGS_MEM|SETWA_FLAGS_PCIE_SBDF)))
- return -EINVAL;
+ if (einjv2_supp) {
+ if (flags && (flags &
+ ~(EINJV2_FLAG_ADD_VALID | EINJV2_FLAG_ADD_RANGE_VALID |
+ EINJV2_FLAG_SEVERITY_VALID | EINJV2_FLAG_COMP_VALID)))
+ return -EINVAL;
+ } else {
+ if (flags && (flags &
+ ~(SETWA_FLAGS_APICID | SETWA_FLAGS_MEM |
+ SETWA_FLAGS_PCIE_SBDF)))
+ return -EINVAL;
+ }

/*
* We need extra sanity checks for memory errors.
@@ -530,11 +646,13 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,
*/

/* ensure param1/param2 existed */
- if (!(param_extension || acpi5))
+ if (!(param_extension || acpi5 || einjv2_supp))
goto inject;

/* ensure injection is memory related */
- if (type & ACPI5_VENDOR_BIT) {
+ if (einjv2_supp && !(type & EINJV2_MEMORY_ERROR)) {
+ goto inject;
+ } else if (type & ACPI5_VENDOR_BIT) {
if (vendor_flags != SETWA_FLAGS_MEM)
goto inject;
} else if (!(type & MEM_ERROR_MASK) && !(flags & SETWA_FLAGS_MEM))
@@ -564,7 +682,7 @@ static int einj_error_inject(u32 type, u32 flags, u64 param1, u64 param2,

inject:
mutex_lock(&einj_mutex);
- rc = __einj_error_inject(type, flags, param1, param2, param3, param4);
+ rc = __einj_error_inject(type, flags, param1, param2, param3, param4, param5);
mutex_unlock(&einj_mutex);

return rc;
@@ -576,7 +694,15 @@ static u64 error_param1;
static u64 error_param2;
static u64 error_param3;
static u64 error_param4;
+static u64 error_param5;
static struct dentry *einj_debug_dir;
+
+static const char * const einjv2_error_type_string[] = {
+ "0x00000001\tEINJV2 Processor Error\n",
+ "0x00000002\tEINJV2 Memory Error\n",
+ "0x00000004\tEINJV2 PCI Express Error\n",
+};
+
static const char * const einj_error_type_string[] = {
"0x00000001\tProcessor Correctable\n",
"0x00000002\tProcessor Uncorrectable non-fatal\n",
@@ -600,15 +726,22 @@ static const char * const einj_error_type_string[] = {

static int available_error_type_show(struct seq_file *m, void *v)
{
- int rc;
+ int rc, pos;
u32 available_error_type = 0;

rc = einj_get_available_error_type(&available_error_type);
if (rc)
return rc;
- for (int pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
- if (available_error_type & BIT(pos))
- seq_puts(m, einj_error_type_string[pos]);
+
+ if (einjv2_supp) {
+ for (pos = 0; pos < ARRAY_SIZE(einjv2_error_type_string); pos++)
+ if (available_error_type & BIT(pos))
+ seq_puts(m, einjv2_error_type_string[pos]);
+ } else {
+ for (pos = 0; pos < ARRAY_SIZE(einj_error_type_string); pos++)
+ if (available_error_type & BIT(pos))
+ seq_puts(m, einj_error_type_string[pos]);
+ }

return 0;
}
@@ -662,8 +795,9 @@ static int error_inject_set(void *data, u64 val)
if (!error_type)
return -EINVAL;

- return einj_error_inject(error_type, error_flags, error_param1, error_param2,
- error_param3, error_param4);
+ return einj_error_inject(error_type, error_flags, error_param1,
+ error_param2, error_param3, error_param4,
+ error_param5);
}

DEFINE_DEBUGFS_ATTRIBUTE(error_inject_fops, NULL, error_inject_set, "%llu\n");
@@ -743,7 +877,7 @@ static int __init einj_init(void)
}

einj_param = einj_get_parameter_address();
- if ((param_extension || acpi5) && einj_param) {
+ if ((param_extension || acpi5 || einjv2_supp) && einj_param) {
debugfs_create_x32("flags", S_IRUSR | S_IWUSR, einj_debug_dir,
&error_flags);
debugfs_create_x64("param1", S_IRUSR | S_IWUSR, einj_debug_dir,
@@ -754,6 +888,8 @@ static int __init einj_init(void)
&error_param3);
debugfs_create_x64("param4", S_IRUSR | S_IWUSR, einj_debug_dir,
&error_param4);
+ debugfs_create_x64("param5", S_IRUSR | S_IWUSR, einj_debug_dir,
+ &error_param5);
debugfs_create_x32("notrigger", S_IRUSR | S_IWUSR,
einj_debug_dir, &notrigger);
}
diff --git a/include/acpi/actbl1.h b/include/acpi/actbl1.h
index 58b0490a2ad12e26cab30209e461429a1f5dec30..de89c18f3443489683c9aafaf6a894e60d98374b 100644
--- a/include/acpi/actbl1.h
+++ b/include/acpi/actbl1.h
@@ -1026,7 +1026,9 @@ enum acpi_einj_actions {
ACPI_EINJ_GET_COMMAND_STATUS = 7,
ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS = 8,
ACPI_EINJ_GET_EXECUTE_TIMINGS = 9,
- ACPI_EINJ_ACTION_RESERVED = 10, /* 10 and greater are reserved */
+ ACPI_EINJV2_SET_ERROR_TYPE = 0x10,
+ ACPI_EINJV2_GET_ERROR_TYPE = 0x11,
+ ACPI_EINJ_ACTION_RESERVED = 0x12, /* 12 and greater are reserved */
ACPI_EINJ_TRIGGER_ERROR = 0xFF /* Except for this value */
};

--
2.17.1


2023-05-03 16:26:31

by Luck, Tony

[permalink] [raw]
Subject: RE: [PATCH] ACPI: APEI: EINJ: EINJV2 support added

> drivers/acpi/apei/einj.c | 172 +++++++++++++++++++++++++++++++++++----
> include/acpi/actbl1.h | 4 +-
> 2 files changed, 157 insertions(+), 19 deletions(-)

Needs an update to Documentation/firmware-guide/acpi/apei/einj.rst
with some example of how to use the EINJV2 injection. It appears that
param3 and param4 are now used for memory injection instead of
(or as well as?) param1 and param2. But it isn't all that obvious what
they do.

-Tony

2023-05-08 20:44:52

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH] ACPI: APEI: EINJ: EINJV2 support added

Hi Piyush,

kernel test robot noticed the following build warnings:

[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on linus/master v6.4-rc1 next-20230508]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Piyush-Malgujar/ACPI-APEI-EINJ-EINJV2-support-added/20230503-223915
base: https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link: https://lore.kernel.org/r/20230503143759.10485-1-pmalgujar%40marvell.com
patch subject: [PATCH] ACPI: APEI: EINJ: EINJV2 support added
config: x86_64-randconfig-s022 (https://download.01.org/0day-ci/archive/20230509/[email protected]/config)
compiler: gcc-11 (Debian 11.3.0-12) 11.3.0
reproduce:
# apt-get install sparse
# sparse version: v0.6.4-39-gce1a6720-dirty
# https://github.com/intel-lab-lkp/linux/commit/ca5bbbdcc074577ee88ccaa2d078a37eb5eec36f
git remote add linux-review https://github.com/intel-lab-lkp/linux
git fetch --no-tags linux-review Piyush-Malgujar/ACPI-APEI-EINJ-EINJV2-support-added/20230503-223915
git checkout ca5bbbdcc074577ee88ccaa2d078a37eb5eec36f
# save the config file
mkdir build_dir && cp config build_dir/.config
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 olddefconfig
make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/acpi/apei/ drivers/platform/x86/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <[email protected]>
| Link: https://lore.kernel.org/oe-kbuild-all/[email protected]/

sparse warnings: (new ones prefixed by >>)
drivers/acpi/apei/einj.c:247:11: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct vendor_error_type_extension *v @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:247:11: sparse: expected struct vendor_error_type_extension *v
drivers/acpi/apei/einj.c:247:11: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:255:29: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __iomem *virt @@ got struct vendor_error_type_extension *v @@
drivers/acpi/apei/einj.c:255:29: sparse: expected void [noderef] __iomem *virt
drivers/acpi/apei/einj.c:255:29: sparse: got struct vendor_error_type_extension *v
>> drivers/acpi/apei/einj.c:286:26: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct einjv2_set_error_type *v65param @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:286:26: sparse: expected struct einjv2_set_error_type *v65param
drivers/acpi/apei/einj.c:286:26: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:295:25: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct set_error_type_with_address *v5param @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:295:25: sparse: expected struct set_error_type_with_address *v5param
drivers/acpi/apei/einj.c:295:25: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:305:25: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct einj_parameter *v4param @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:305:25: sparse: expected struct einj_parameter *v4param
drivers/acpi/apei/einj.c:305:25: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:309:45: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __iomem *virt @@ got struct einj_parameter *v4param @@
drivers/acpi/apei/einj.c:309:45: sparse: expected void [noderef] __iomem *virt
drivers/acpi/apei/einj.c:309:45: sparse: got struct einj_parameter *v4param
drivers/acpi/apei/einj.c:376:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct acpi_einj_trigger *trigger_tab @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:376:21: sparse: expected struct acpi_einj_trigger *trigger_tab
drivers/acpi/apei/einj.c:376:21: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:402:17: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void volatile [noderef] __iomem *addr @@ got struct acpi_einj_trigger *trigger_tab @@
drivers/acpi/apei/einj.c:402:17: sparse: expected void volatile [noderef] __iomem *addr
drivers/acpi/apei/einj.c:402:17: sparse: got struct acpi_einj_trigger *trigger_tab
drivers/acpi/apei/einj.c:403:21: sparse: sparse: incorrect type in assignment (different address spaces) @@ expected struct acpi_einj_trigger *trigger_tab @@ got void [noderef] __iomem * @@
drivers/acpi/apei/einj.c:403:21: sparse: expected struct acpi_einj_trigger *trigger_tab
drivers/acpi/apei/einj.c:403:21: sparse: got void [noderef] __iomem *
drivers/acpi/apei/einj.c:468:25: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void volatile [noderef] __iomem *addr @@ got struct acpi_einj_trigger *trigger_tab @@
drivers/acpi/apei/einj.c:468:25: sparse: expected void volatile [noderef] __iomem *addr
drivers/acpi/apei/einj.c:468:25: sparse: got struct acpi_einj_trigger *trigger_tab
drivers/acpi/apei/einj.c:930:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@ expected void [noderef] __iomem *virt @@ got void *static [assigned] [toplevel] einj_param @@
drivers/acpi/apei/einj.c:930:37: sparse: expected void [noderef] __iomem *virt
drivers/acpi/apei/einj.c:930:37: sparse: got void *static [assigned] [toplevel] einj_param

vim +286 drivers/acpi/apei/einj.c

237
238 static void check_vendor_extension(u64 paddr,
239 struct set_error_type_with_address *v5param)
240 {
241 int offset = v5param->vendor_extension;
242 struct vendor_error_type_extension *v;
243 u32 sbdf;
244
245 if (!offset)
246 return;
> 247 v = acpi_os_map_iomem(paddr + offset, sizeof(*v));
248 if (!v)
249 return;
250 sbdf = v->pcie_sbdf;
251 sprintf(vendor_dev, "%x:%x:%x.%x vendor_id=%x device_id=%x rev_id=%x\n",
252 sbdf >> 24, (sbdf >> 16) & 0xff,
253 (sbdf >> 11) & 0x1f, (sbdf >> 8) & 0x7,
254 v->vendor_id, v->device_id, v->rev_id);
255 acpi_os_unmap_iomem(v, sizeof(*v));
256 }
257
258 static void *einj_get_parameter_address(void)
259 {
260 int i;
261 u64 pa_v4 = 0, pa_v5 = 0, pa_v65 = 0;
262 struct acpi_whea_header *entry;
263
264 entry = EINJ_TAB_ENTRY(einj_tab);
265 for (i = 0; i < einj_tab->entries; i++) {
266 if (entry->action == ACPI_EINJ_SET_ERROR_TYPE &&
267 entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
268 entry->register_region.space_id ==
269 ACPI_ADR_SPACE_SYSTEM_MEMORY)
270 pa_v4 = get_unaligned(&entry->register_region.address);
271 if (entry->action == ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS &&
272 entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
273 entry->register_region.space_id ==
274 ACPI_ADR_SPACE_SYSTEM_MEMORY)
275 pa_v5 = get_unaligned(&entry->register_region.address);
276 if (entry->action == ACPI_EINJV2_SET_ERROR_TYPE &&
277 entry->instruction == ACPI_EINJ_WRITE_REGISTER &&
278 entry->register_region.space_id ==
279 ACPI_ADR_SPACE_SYSTEM_MEMORY)
280 pa_v65 = get_unaligned(&entry->register_region.address);
281 entry++;
282 }
283 if (pa_v65) {
284 struct einjv2_set_error_type *v65param;
285
> 286 v65param = acpi_os_map_iomem(pa_v65, sizeof(*v65param));
287 if (v65param) {
288 einjv2_supp = 1;
289 return v65param;
290 }
291 }
292 if (pa_v5) {
293 struct set_error_type_with_address *v5param;
294
295 v5param = acpi_os_map_iomem(pa_v5, sizeof(*v5param));
296 if (v5param) {
297 acpi5 = 1;
298 check_vendor_extension(pa_v5, v5param);
299 return v5param;
300 }
301 }
302 if (param_extension && pa_v4) {
303 struct einj_parameter *v4param;
304
305 v4param = acpi_os_map_iomem(pa_v4, sizeof(*v4param));
306 if (!v4param)
307 return NULL;
308 if (v4param->reserved1 || v4param->reserved2) {
309 acpi_os_unmap_iomem(v4param, sizeof(*v4param));
310 return NULL;
311 }
312 return v4param;
313 }
314
315 return NULL;
316 }
317

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests