2020-05-04 15:22:52

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 00/14] ASI - Part III (ASI Test Driver and CLI)

This is part III of ASI RFC v4. Please refer to the cover letter of
part I for an overview the ASI RFC.

https://lore.kernel.org/lkml/[email protected]/

This part provides a driver and a CLI for testing and introspecting an
ASI. The driver creates a test ASI and provide the ability to run some
test sequences on the test ASI such as:

- a simple enter/exit ASI
- using printk with ASI
- access a map or unmapped buffer while in ASI
- receive an interrupt or NMI while in ASI
- scheduling in/out a task using ASI

The driver reports if the ASI remains active or not at the end of the
test sequence. It also provides the ability to:

- query the ASI fault (i.e. places where the ASI had a fault)
- clear the ASI fault log
- toggle the reporting of stack trace on an ASI fault
- query ASI mappings (i.e. VA ranges mapped in the ASI)
- add an ASI mapping
- clear an ASI mapping

The asicmd user CLI is provided to interact with the ASI test driver.
More details and examples are provided below for using the asicmd CLI.

Thanks,

alex.

=====

ASI Test Driver and CLI Usage
-----------------------------
The ASI test driver is built as a loadable module (asi.ko) in the
drivers/staging/asi/ directory. You can load it with modprobe or insmod.

The ASI test CLI (asicmd) is built in the drivers/staging/asi/ directory.
The ASI test driver should be loaded before using the asicmd CLI.

Usage:
------
Running the asicmd without arguments will show the different commands
available:

# ./asicmd
Usage: asicmd (<cmd>|<test>...)

Commands:
all - run all tests
fault - list ASI faults
fltclr - clear ASI faults
stkon - show stack on ASI fault
stkoff - do not show stack on ASI fault
map - list ASI mappings
mapadd - add ASI mappings
mapclr - clear ASI mapping

Tests:
nop - enter/exit ASI and nothing else
mem - enter ASI and accessed an unmapped buffer
memmap - enter ASI and accessed a mapped buffer
intr - receive an interruption while running with ASI
nmi - receive a NMI while running with ASI
intrnmi - receive a NMI in an interrupt received while running with ASI
sched - call schedule() while running with ASI
printk - call printk() while running with ASI

Running a Test Sequence:
------------------------
Just specify the test sequence to run. For example, to run the "nop"
test sequence, run "asicmd nop":

# ./asicmd nop
Test nop (sequence 0)
- rv = 0 ; result = 0 ; asi active
- expect = asi active
TEST OK

The command reports that the ASI was active at the end of the test
sequence and that this was the expected result. So the test was
successful.

Another example with the "mem" test sequence:

# ./asicmd mem
Test mem (sequence 2)
- rv = 0 ; result = 0 ; asi inactive
- expect = asi inactive
TEST OK

This tests sequence access a buffer which is not mapped in ASI so it
causes ASI to exit. The test effectively reports that the ASI was inactive
at the end of the test sequence, but this is the expected result so the
test is successful.

All test sequences can be run with the "all" command: asicmd all

Managing ASI Faults:
--------------------
The asicmd CLI has commands to manage ASI faults i.e. faults that occurred
while running ASI because ASI is accessing an unmapped address:

- "asicmd fault" reports all ASI faults. It shows the address where the
fault has occurred and the number of time the fault has occurred.

- "asicmd fltclr" clears the ASI fault log.

For example, the "mem" will cause an ASI fault and we can see it with the
"fault" command:

# ./asicmd fltclr

# ./asicmd fault
ASI has no fault

# ./asicmd mem
Test mem (sequence 2)
- rv = 0 ; result = 0 ; asi inactive
- expect = asi inactive
TEST OK

# ./asicmd fault
ADDRESS COUNT SYMBOL
0xffffffffc0515037 1 asidrv_mem_run+0x37/0x7f [asi]


Managing ASI Mappings:
----------------------
The asicmd CLI has commands to manage ASI mappings i.e. address ranges
which are mapped in the ASI page-table:

- "asicmd map" reports all ASI mappings. It shows the address, size and
page-table level of the mappings.

- "asicmd mapadd" adds a mapping to the ASI page-table. The syntax is:

asicmd mapadd [percpu:]<addr>:<size>[:<level>]

. <addr> is the start address of the mapping and <size> is its size.
. <level> is the page-table level of the mapping, either "pte", "pmd",
"pud", "p4d" or "pgd". By default, the level is "pte".
. "percpu" should be prepended when <addr> is the offset of a percpu
buffer

- "asicmd" mapclr" clears a mapping in the ASI page-table. The syntax is:

asicmd mapclr [percpu:]<addr>

. <addr> is the start address of the mapping. The system automatically
figures out the size and page-table level of the mapping.
. "percpu" should be prepended when <addr> is the offset of a percpu
buffer

Example:

# ./asicmd map
ADDRESS SIZE LEVEL
0xffff88806946f000 0x838 PTE
0xffff88806820a000 0x60 PTE
0xffffffffc0515000 0x7000 PTE
0xffff88807ddac4c0 0xa0 PTE
0xffff88807dd2c4c0 0xa0 PTE
0xffff88807dcac4c0 0xa0 PTE
0xffff88807dc2c4c0 0xa0 PTE
0xffff88807dd98bc0 0x8 PTE
0xffff88807dd18bc0 0x8 PTE
0xffff88807dc98bc0 0x8 PTE
0xffff88807dc18bc0 0x8 PTE
0xffff88807dd80000 0x30 PTE
0xffff88807dd00000 0x30 PTE
0xffff88807dc80000 0x30 PTE
0xffff88807dc00000 0x30 PTE
0xfffffe0000001000 0x8000000000 P4D
0xffffffff80000000 0x40000000 PTE

This shows the default core mappings for ASI. These mappings shouldn't be
removed.

If the ASI faults then you can add the missing mapping. For example with
the "printk" test sequence:

# ./asicmd fltclr

# ./asicmd printk
Test printk (sequence 1)
- rv = 0 ; result = 0 ; asi inactive
- expect = asi active
TEST FAILED - unexpected ASI state

The "printk" test sequence failed because of an ASI fault:

# ./asicmd fault
ADDRESS COUNT SYMBOL
0xffffffff8110b5a1 1 vprintk_func+0x11/0xbc

With the source code or debugger, we can figure out that the fault is due
to printk_context not being mapped in the ASI.

0xffffffff8110b5a1 <+17>: mov %gs:0x7ef106f8(%rip),%eax # 0x1bca0 <printk_context>

So we can add the missing mapping into the ASI with "mapadd"; note that
printk_context is a percpu buffer and that sizeof(printk_context) is 4:

# ./asicmd mapadd percpu:0x1bca0:4
mapadd 1bca0/4/0 percpu

If we re-run the "printk" test then we got:

# ./asicmd fltclr

# ./asicmd printk
Test printk (sequence 1)
- rv = 0 ; result = 0 ; asi inactive
- expect = asi active
TEST FAILED - unexpected ASI state

# ./asicmd fault
ADDRESS COUNT SYMBOL
0xffffffff811081f3 1 log_store.constprop.27+0x1f3/0x280

We still see a new fault but at a difference address (this time because
cpu_number is not mapped).

-----

Alexandre Chartre (14):
mm/asi: Define the test ASI type
asidrv: Introduce the ASI driver
asidrv: Introduce the ASIDRV_IOCTL_RUN_SEQUENCE ioctl
asidrv: Sequence to test ASI access to mapped/unmapped memory
asidrv: Sequence to test interrupt on ASI
asidrv: Sequence to test NMI on ASI
asidrv: Sequence to test interrupt+NMI on ASI
asidrv: Sequence to test scheduling in/out with ASI
asidrv: Add ioctls to manage ASI page faults
asidrv: Add ioctls to manage ASI mapped VA ranges
asidrv/asicmd: Introduce the asicmd command
asidrv/asicmd: Add more test sequences for testing ASI
asidrv/asicmd: Add options to manage ASI page faults
asidrv/asicmd: Add options to manage ASI mapped VA ranges

arch/x86/include/asm/asi.h | 2 +
arch/x86/mm/asi.c | 1 +
drivers/staging/Makefile | 1 +
drivers/staging/asi/Makefile | 13 +
drivers/staging/asi/asicmd.c | 439 ++++++++++++++
drivers/staging/asi/asidrv.c | 1077 ++++++++++++++++++++++++++++++++++
drivers/staging/asi/asidrv.h | 102 ++++
7 files changed, 1635 insertions(+)
create mode 100644 drivers/staging/asi/Makefile
create mode 100644 drivers/staging/asi/asicmd.c
create mode 100644 drivers/staging/asi/asidrv.c
create mode 100644 drivers/staging/asi/asidrv.h

--
2.18.2


2020-05-04 15:24:10

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 04/14] asidrv: Sequence to test ASI access to mapped/unmapped memory

Add a sequence to test if ASI exit or not when accessing a mapped
or unmapped memory buffer.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asidrv.c | 70 ++++++++++++++++++++++++++++++++++++
drivers/staging/asi/asidrv.h | 3 ++
2 files changed, 73 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 4f0548edb2f9..4231b56db167 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -14,9 +14,21 @@

#include "asidrv.h"

+#define ASIDRV_TEST_BUFFER_SIZE PAGE_SIZE
+
+/* Number of read for mem/memmap test sequence */
+#define ASIDRV_MEM_READ_COUNT 1000
+
+enum asidrv_state {
+ ASIDRV_STATE_NONE,
+ ASIDRV_STATE_INTR_WAITING,
+ ASIDRV_STATE_INTR_RECEIVED,
+};
+
struct asidrv_test {
struct asi *asi; /* ASI for testing */
struct dpt *dpt; /* ASI decorated page-table */
+ char *buffer; /* buffer for testing */
};

struct asidrv_sequence {
@@ -42,6 +54,10 @@ static struct asidrv_test *asidrv_test_create(void)
if (!test)
return NULL;

+ test->buffer = kzalloc(ASIDRV_TEST_BUFFER_SIZE, GFP_KERNEL);
+ if (!test->buffer)
+ goto error;
+
/*
* Create and fill a decorator page-table to be used with the ASI.
*/
@@ -96,6 +112,7 @@ static void asidrv_test_destroy(struct asidrv_test *test)
if (test->asi)
asi_destroy(test->asi);

+ kfree(test->buffer);
kfree(test);
}

@@ -143,6 +160,51 @@ static int asidrv_asi_is_active(struct asi *asi)
return false;
}

+/*
+ * Memory Buffer Access Test Sequences
+ */
+
+#define OPTNONE __attribute__((optimize(0)))
+
+static enum asidrv_run_error OPTNONE asidrv_mem_run(struct asidrv_test *test)
+{
+ char c;
+ int i, index;
+
+ /*
+ * Do random reads in the test buffer, and return if the ASI
+ * becomes inactive.
+ */
+ for (i = 0; i < ASIDRV_MEM_READ_COUNT; i++) {
+ index = get_cycles() % ASIDRV_TEST_BUFFER_SIZE;
+ c = test->buffer[index];
+ if (!asidrv_asi_is_active(test->asi)) {
+ pr_warn("ASI inactive after reading byte %d at %d\n",
+ i + 1, index);
+ break;
+ }
+ }
+
+ return ASIDRV_RUN_ERR_NONE;
+}
+
+static enum asidrv_run_error asidrv_memmap_setup(struct asidrv_test *test)
+{
+ int err;
+
+ pr_debug("mapping test buffer %px\n", test->buffer);
+ err = dpt_map(test->dpt, test->buffer, ASIDRV_TEST_BUFFER_SIZE);
+ if (err)
+ return ASIDRV_RUN_ERR_MAP_BUFFER;
+
+ return ASIDRV_RUN_ERR_NONE;
+}
+
+static void asidrv_memmap_cleanup(struct asidrv_test *test)
+{
+ dpt_unmap(test->dpt, test->buffer);
+}
+
/*
* Printk Test Sequence
*/
@@ -161,6 +223,14 @@ struct asidrv_sequence asidrv_sequences[] = {
"printk",
NULL, asidrv_printk_run, NULL,
},
+ [ASIDRV_SEQ_MEM] = {
+ "mem",
+ NULL, asidrv_mem_run, NULL,
+ },
+ [ASIDRV_SEQ_MEMMAP] = {
+ "memmap",
+ asidrv_memmap_setup, asidrv_mem_run, asidrv_memmap_cleanup,
+ },
};

static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 33acb058c443..1e820cc64f13 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -8,6 +8,8 @@
enum asidrv_seqnum {
ASIDRV_SEQ_NOP, /* empty sequence */
ASIDRV_SEQ_PRINTK, /* printk sequence */
+ ASIDRV_SEQ_MEM, /* access unmapped memory */
+ ASIDRV_SEQ_MEMMAP, /* access mapped memory */
};

enum asidrv_run_error {
@@ -17,6 +19,7 @@ enum asidrv_run_error {
ASIDRV_RUN_ERR_MAP_TASK, /* failed to map current task */
ASIDRV_RUN_ERR_ENTER, /* failed to enter ASI */
ASIDRV_RUN_ERR_ACTIVE, /* ASI is not active after entering ASI */
+ ASIDRV_RUN_ERR_MAP_BUFFER, /* failed to map buffer */
};

#define ASIDRV_IOCTL_RUN_SEQUENCE _IOWR('a', 1, struct asidrv_run_param)
--
2.18.2

2020-05-04 18:43:17

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 07/14] asidrv: Sequence to test interrupt+NMI on ASI

Add a sequence to test if an ASI remains active after receiving
an interrupt which is itself interrupted by an NMI.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asidrv.c | 62 +++++++++++++++++++++++++++++++++++-
drivers/staging/asi/asidrv.h | 1 +
2 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 8f964c9e0b14..e08f2a107e89 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -49,6 +49,7 @@ struct asidrv_test {
struct work_struct work; /* work for other cpu */
bool work_set;
enum asidrv_run_error run_error;
+ bool intrnmi;
};

struct asidrv_sequence {
@@ -65,6 +66,7 @@ static void asidrv_test_destroy(struct asidrv_test *test);
static void asidrv_run_fini(struct asidrv_test *test);
static void asidrv_run_cleanup(struct asidrv_test *test,
struct asidrv_sequence *sequence);
+static void asidrv_intrnmi_send(struct work_struct *work);

static struct asidrv_test *asidrv_test_create(void)
{
@@ -284,7 +286,10 @@ static enum asidrv_run_error asidrv_nmi_setup(struct asidrv_test *test)
}

/* set work to have another cpu to send us an NMI */
- INIT_WORK(&test->work, asidrv_nmi_send);
+ if (test->intrnmi)
+ INIT_WORK(&test->work, asidrv_intrnmi_send);
+ else
+ INIT_WORK(&test->work, asidrv_nmi_send);

test->work_set = true;

@@ -336,6 +341,12 @@ static void asidrv_intr_handler(void *info)

pr_debug("Received interrupt\n");
atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+
+ if (!test->intrnmi)
+ return;
+
+ pr_debug("Waiting for NMI in interrupt\n");
+ asidrv_nmi_run(test);
}

static void asidrv_intr_send(struct work_struct *work)
@@ -383,6 +394,50 @@ static enum asidrv_run_error asidrv_intr_run(struct asidrv_test *test)
return err;
}

+/*
+ * Interrupt+NMI Test Sequence
+ */
+
+static void asidrv_intrnmi_send(struct work_struct *work)
+{
+ /* send and interrupt and then send an NMI */
+ asidrv_intr_send(work);
+ asidrv_nmi_send(work);
+}
+
+static enum asidrv_run_error asidrv_intrnmi_setup(struct asidrv_test *test)
+{
+ test->intrnmi = true;
+ return asidrv_nmi_setup(test);
+}
+
+static enum asidrv_run_error asidrv_intrnmi_run(struct asidrv_test *test)
+{
+ enum asidrv_run_error err;
+ enum asidrv_state state;
+
+ /*
+ * Wait for state changes indicating that an interrupt and
+ * then an NMI were received.
+ */
+ err = asidrv_wait_transition(test,
+ ASIDRV_STATE_INTR_WAITING,
+ ASIDRV_STATE_NMI_RECEIVED,
+ ASIDRV_TIMEOUT_INTERRUPT);
+ if (err == ASIDRV_RUN_ERR_TIMEOUT) {
+ state = atomic_read(&test->state);
+ if (state == ASIDRV_STATE_INTR_WAITING) {
+ pr_debug("Interrupt wait timeout\n");
+ err = ASIDRV_RUN_ERR_INTR;
+ } else {
+ pr_debug("NMI wait timeout\n");
+ err = ASIDRV_RUN_ERR_NMI;
+ }
+ }
+
+ return err;
+}
+
/*
* Memory Buffer Access Test Sequences
*/
@@ -462,6 +517,10 @@ struct asidrv_sequence asidrv_sequences[] = {
"nmi",
asidrv_nmi_setup, asidrv_nmi_run, asidrv_nmi_cleanup,
},
+ [ASIDRV_SEQ_INTRNMI] = {
+ "intr+nmi",
+ asidrv_intrnmi_setup, asidrv_intrnmi_run, asidrv_nmi_cleanup,
+ },
};

static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
@@ -509,6 +568,7 @@ static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
int run_err;

test->work_set = false;
+ test->intrnmi = false;

if (sequence->setup) {
run_err = sequence->setup(test);
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 6fac32d56f6f..de9f7ad993e0 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -12,6 +12,7 @@ enum asidrv_seqnum {
ASIDRV_SEQ_MEMMAP, /* access mapped memory */
ASIDRV_SEQ_INTERRUPT, /* interrupt sequence */
ASIDRV_SEQ_NMI, /* NMI interrupt sequence */
+ ASIDRV_SEQ_INTRNMI, /* NMI in interrupt sequence */
};

enum asidrv_run_error {
--
2.18.2

2020-05-04 18:43:23

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 01/14] mm/asi: Define the test ASI type

Define the test ASI type which can be used for testing or experimenting
ASI.

Signed-off-by: Alexandre Chartre <[email protected]>
---
arch/x86/include/asm/asi.h | 2 ++
arch/x86/mm/asi.c | 1 +
drivers/staging/Makefile | 1 +
3 files changed, 4 insertions(+)

diff --git a/arch/x86/include/asm/asi.h b/arch/x86/include/asm/asi.h
index eafed750e07f..de4f790c630b 100644
--- a/arch/x86/include/asm/asi.h
+++ b/arch/x86/include/asm/asi.h
@@ -66,6 +66,7 @@ struct asi_tlb_state {
#ifdef CONFIG_PAGE_TABLE_ISOLATION
#define ASI_PCID_PREFIX_USER 0x80 /* user ASI */
#endif
+#define ASI_PCID_PREFIX_TEST 0xff /* test ASI */

struct asi_type {
int pcid_prefix; /* PCID prefix */
@@ -156,6 +157,7 @@ extern int asi_init_dpt(struct dpt *dpt);
#ifdef CONFIG_PAGE_TABLE_ISOLATION
DECLARE_ASI_TYPE(user);
#endif
+DECLARE_ASI_TYPE(test);

static inline void asi_set_log_policy(struct asi *asi, int policy)
{
diff --git a/arch/x86/mm/asi.c b/arch/x86/mm/asi.c
index 8b670ed13729..e0b0d70415d7 100644
--- a/arch/x86/mm/asi.c
+++ b/arch/x86/mm/asi.c
@@ -17,6 +17,7 @@
#ifdef CONFIG_PAGE_TABLE_ISOLATION
DEFINE_ASI_TYPE(user, ASI_PCID_PREFIX_USER, false);
#endif
+DEFINE_ASI_TYPE(test, ASI_PCID_PREFIX_TEST, true);

static void asi_log_fault(struct asi *asi, struct pt_regs *regs,
unsigned long error_code, unsigned long address,
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 4d34198151b3..fb50e669579b 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_QLGE) += qlge/
obj-$(CONFIG_WFX) += wfx/
+obj-$(CONFIG_ADDRESS_SPACE_ISOLATION) += asi/
--
2.18.2

2020-05-04 18:43:25

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 09/14] asidrv: Add ioctls to manage ASI page faults

Add ioctls to list and clear ASI page faults. Also add an ioctl to display
or not stack trace on ASI page fault.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asidrv.c | 88 ++++++++++++++++++++++++++++++++++++
drivers/staging/asi/asidrv.h | 32 +++++++++++++
2 files changed, 120 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 9ca17e0b654e..e6edfbe5acea 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -4,6 +4,7 @@
*/

#include <linux/fs.h>
+#include <linux/kallsyms.h>
#include <linux/kthread.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
@@ -790,12 +791,99 @@ static int asidrv_ioctl_run_sequence(struct asidrv_test *test,
return 0;
}

+/*
+ * ASI fault ioctls
+ */
+
+static int asidrv_ioctl_list_fault(struct asi *asi, unsigned long arg)
+{
+ struct asidrv_fault_list __user *uflist;
+ struct asidrv_fault_list *flist;
+ size_t flist_size;
+ __u32 uflist_len;
+ int i;
+
+ uflist = (struct asidrv_fault_list __user *)arg;
+ if (copy_from_user(&uflist_len, &uflist->length, sizeof(uflist_len)))
+ return -EFAULT;
+
+ uflist_len = min_t(unsigned int, uflist_len, ASI_FAULT_LOG_SIZE);
+
+ flist_size = sizeof(*flist) + sizeof(struct asidrv_fault) * uflist_len;
+ flist = kzalloc(flist_size, GFP_KERNEL);
+ if (!flist)
+ return -ENOMEM;
+
+ for (i = 0; i < ASI_FAULT_LOG_SIZE; i++) {
+ if (!asi->fault_log[i].address)
+ break;
+ if (i < uflist_len) {
+ flist->fault[i].addr = asi->fault_log[i].address;
+ flist->fault[i].count = asi->fault_log[i].count;
+ sprint_symbol(flist->fault[i].symbol,
+ asi->fault_log[i].address);
+ }
+ }
+ flist->length = i;
+
+ if (copy_to_user(uflist, flist, flist_size)) {
+ kfree(flist);
+ return -EFAULT;
+ }
+
+ if (i >= ASI_FAULT_LOG_SIZE)
+ pr_warn("ASI %p: fault log buffer is full [%d]\n", asi, i);
+
+ kfree(flist);
+
+ return 0;
+}
+
+static int asidrv_ioctl_clear_fault(struct asi *asi)
+{
+ int i;
+
+ for (i = 0; i < ASI_FAULT_LOG_SIZE; i++) {
+ if (!asi->fault_log[i].address)
+ break;
+ asi->fault_log[i].address = 0;
+ }
+
+ pr_debug("ASI %p: faults cleared\n", asi);
+ return 0;
+}
+
+static int asidrv_ioctl_log_fault_stack(struct asi *asi, bool log_stack)
+{
+ if (log_stack) {
+ asi->fault_log_policy |= ASI_FAULT_LOG_STACK;
+ pr_debug("ASI %p: setting fault stack\n", asi);
+ } else {
+ asi->fault_log_policy &= ~ASI_FAULT_LOG_STACK;
+ pr_debug("ASI %p: clearing fault stack\n", asi);
+ }
+
+ return 0;
+}
+
static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct asidrv_test *test = asidrv_test;
+ struct asi *asi = test->asi;

switch (cmd) {

+ /* ASI fault ioctls */
+
+ case ASIDRV_IOCTL_LIST_FAULT:
+ return asidrv_ioctl_list_fault(asi, arg);
+
+ case ASIDRV_IOCTL_CLEAR_FAULT:
+ return asidrv_ioctl_clear_fault(asi);
+
+ case ASIDRV_IOCTL_LOG_FAULT_STACK:
+ return asidrv_ioctl_log_fault_stack(asi, arg);
+
/* Test ioctls */

case ASIDRV_IOCTL_RUN_SEQUENCE:
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 9f540b119883..99ab9843e36b 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -36,9 +36,41 @@ enum asidrv_run_error {

#define ASIDRV_IOCTL_RUN_SEQUENCE _IOWR('a', 1, struct asidrv_run_param)

+/*
+ * ASIDRV_IOCTL_LIST_FAULT: return the list of ASI faults.
+ *
+ * User should set 'length' with the number of entries available in the
+ * 'fault' array. On return, 'length' is set to the number of ASI faults
+ * (which can be larger than the original 'length' value), and the 'fault'
+ * array is filled with the ASI faults.
+ */
+#define ASIDRV_IOCTL_LIST_FAULT _IOWR('a', 2, struct asidrv_fault_list)
+#define ASIDRV_IOCTL_CLEAR_FAULT _IO('a', 3)
+#define ASIDRV_IOCTL_LOG_FAULT_STACK _IO('a', 4)
+
+#define ASIDRV_KSYM_NAME_LEN 128
+/*
+ * We need KSYM_SYMBOL_LEN to lookup symbol. However it's not part of
+ * userland include. So we use a reasonably large value (KSYM_SYMBOL_LEN
+ * is around 310).
+ */
+#define ASIDRV_KSYM_SYMBOL_LEN 512
+
struct asidrv_run_param {
__u32 sequence; /* sequence to run */
__u32 run_error; /* result error after run */
__u32 asi_active; /* ASI is active after run? */
};
+
+struct asidrv_fault {
+ __u64 addr;
+ char symbol[ASIDRV_KSYM_SYMBOL_LEN];
+ __u32 count;
+};
+
+struct asidrv_fault_list {
+ __u32 length;
+ struct asidrv_fault fault[0];
+};
+
#endif
--
2.18.2

2020-05-04 18:43:25

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 05/14] asidrv: Sequence to test interrupt on ASI

Add a sequence to test if an ASI remains active after receiving
an interrupt.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asidrv.c | 144 +++++++++++++++++++++++++++++++++--
drivers/staging/asi/asidrv.h | 5 ++
2 files changed, 144 insertions(+), 5 deletions(-)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index 4231b56db167..a3c7da2bf16e 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -7,6 +7,7 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>

#include <asm/asi.h>
#include <asm/dpt.h>
@@ -19,6 +20,12 @@
/* Number of read for mem/memmap test sequence */
#define ASIDRV_MEM_READ_COUNT 1000

+/* Timeout for target to be ready to receive an interrupt */
+#define ASIDRV_TIMEOUT_TARGET_READY 1
+
+/* Timeout for receiving an interrupt */
+#define ASIDRV_TIMEOUT_INTERRUPT 5
+
enum asidrv_state {
ASIDRV_STATE_NONE,
ASIDRV_STATE_INTR_WAITING,
@@ -29,6 +36,13 @@ struct asidrv_test {
struct asi *asi; /* ASI for testing */
struct dpt *dpt; /* ASI decorated page-table */
char *buffer; /* buffer for testing */
+
+ /* runtime */
+ atomic_t state; /* runtime state */
+ int cpu; /* cpu the test is running on */
+ struct work_struct work; /* work for other cpu */
+ bool work_set;
+ enum asidrv_run_error run_error;
};

struct asidrv_sequence {
@@ -160,6 +174,107 @@ static int asidrv_asi_is_active(struct asi *asi)
return false;
}

+/*
+ * Wait for an atomic value to be set or the timeout to expire.
+ * Return 0 if the value is set, or -1 if the timeout expires.
+ */
+static enum asidrv_run_error asidrv_wait(struct asidrv_test *test,
+ int value, unsigned int timeout)
+{
+ cycles_t start = get_cycles();
+ cycles_t stop = start + timeout * tsc_khz * 1000;
+
+ while (get_cycles() < stop) {
+ if (atomic_read(&test->state) == value ||
+ test->run_error != ASIDRV_RUN_ERR_NONE)
+ return test->run_error;
+ cpu_relax();
+ }
+
+ /* timeout reached */
+ return ASIDRV_RUN_ERR_TIMEOUT;
+}
+
+/*
+ * Wait for an atomic value to transition from the initial value (set
+ * on entry) to the final value, or to timeout. Return 0 if the transition
+ * was done, or -1 if the timeout expires.
+ */
+static enum asidrv_run_error asidrv_wait_transition(struct asidrv_test *test,
+ int initial, int final,
+ unsigned int timeout)
+{
+ /* set the initial state value */
+ atomic_set(&test->state, initial);
+
+ /* do an active wait for the state changes */
+ return asidrv_wait(test, final, timeout);
+}
+
+/*
+ * Interrupt Test Sequence
+ */
+
+static void asidrv_intr_handler(void *info)
+{
+ struct asidrv_test *test = info;
+
+ /* ASI should be interrupted by the interrupt */
+ if (asidrv_asi_is_active(test->asi)) {
+ test->run_error = ASIDRV_RUN_ERR_INTR_ASI_ACTIVE;
+ atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+ return;
+ }
+
+ pr_debug("Received interrupt\n");
+ atomic_set(&test->state, ASIDRV_STATE_INTR_RECEIVED);
+}
+
+static void asidrv_intr_send(struct work_struct *work)
+{
+ struct asidrv_test *test = container_of(work, struct asidrv_test, work);
+ enum asidrv_run_error err;
+
+ /* wait for cpu target to be ready, then send an interrupt */
+ err = asidrv_wait(test,
+ ASIDRV_STATE_INTR_WAITING,
+ ASIDRV_TIMEOUT_TARGET_READY);
+ if (err) {
+ pr_debug("Target cpu %d not ready, interrupt not sent: error %d\n",
+ test->cpu, err);
+ return;
+ }
+
+ pr_debug("Sending interrupt to cpu %d\n", test->cpu);
+ smp_call_function_single(test->cpu, asidrv_intr_handler,
+ test, false);
+}
+
+static enum asidrv_run_error asidrv_intr_setup(struct asidrv_test *test)
+{
+ /* set work to have another cpu to send us an interrupt */
+ INIT_WORK(&test->work, asidrv_intr_send);
+ test->work_set = true;
+ return ASIDRV_RUN_ERR_NONE;
+}
+
+static enum asidrv_run_error asidrv_intr_run(struct asidrv_test *test)
+{
+ enum asidrv_run_error err;
+
+ /* wait for state changes indicating that an interrupt was received */
+ err = asidrv_wait_transition(test,
+ ASIDRV_STATE_INTR_WAITING,
+ ASIDRV_STATE_INTR_RECEIVED,
+ ASIDRV_TIMEOUT_INTERRUPT);
+ if (err == ASIDRV_RUN_ERR_TIMEOUT) {
+ pr_debug("Interrupt wait timeout\n");
+ err = ASIDRV_RUN_ERR_INTR;
+ }
+
+ return err;
+}
+
/*
* Memory Buffer Access Test Sequences
*/
@@ -231,12 +346,18 @@ struct asidrv_sequence asidrv_sequences[] = {
"memmap",
asidrv_memmap_setup, asidrv_mem_run, asidrv_memmap_cleanup,
},
+ [ASIDRV_SEQ_INTERRUPT] = {
+ "interrupt",
+ asidrv_intr_setup, asidrv_intr_run, NULL,
+ },
};

static enum asidrv_run_error asidrv_run_init(struct asidrv_test *test)
{
int err;

+ test->run_error = ASIDRV_RUN_ERR_NONE;
+
/*
* Map the current stack, we need it to enter ASI.
*/
@@ -272,18 +393,31 @@ static void asidrv_run_fini(struct asidrv_test *test)
static enum asidrv_run_error asidrv_run_setup(struct asidrv_test *test,
struct asidrv_sequence *sequence)
{
- int run_err = ASIDRV_RUN_ERR_NONE;
+ unsigned int other_cpu;
+ int run_err;
+
+ test->work_set = false;

if (sequence->setup) {
run_err = sequence->setup(test);
if (run_err)
- goto failed;
+ return run_err;
}

- return ASIDRV_RUN_ERR_NONE;
+ if (test->work_set) {
+ other_cpu = cpumask_any_but(cpu_online_mask, test->cpu);
+ if (other_cpu == test->cpu) {
+ pr_debug("Sequence %s requires an extra online cpu\n",
+ sequence->name);
+ asidrv_run_cleanup(test, sequence);
+ return ASIDRV_RUN_ERR_NCPUS;
+ }

-failed:
- return run_err;
+ atomic_set(&test->state, ASIDRV_STATE_NONE);
+ schedule_work_on(other_cpu, &test->work);
+ }
+
+ return ASIDRV_RUN_ERR_NONE;
}

static void asidrv_run_cleanup(struct asidrv_test *test,
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 1e820cc64f13..8055d96a0058 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -10,6 +10,7 @@ enum asidrv_seqnum {
ASIDRV_SEQ_PRINTK, /* printk sequence */
ASIDRV_SEQ_MEM, /* access unmapped memory */
ASIDRV_SEQ_MEMMAP, /* access mapped memory */
+ ASIDRV_SEQ_INTERRUPT, /* interrupt sequence */
};

enum asidrv_run_error {
@@ -20,6 +21,10 @@ enum asidrv_run_error {
ASIDRV_RUN_ERR_ENTER, /* failed to enter ASI */
ASIDRV_RUN_ERR_ACTIVE, /* ASI is not active after entering ASI */
ASIDRV_RUN_ERR_MAP_BUFFER, /* failed to map buffer */
+ ASIDRV_RUN_ERR_NCPUS, /* not enough active cpus */
+ ASIDRV_RUN_ERR_INTR, /* no interrupt received */
+ ASIDRV_RUN_ERR_INTR_ASI_ACTIVE, /* ASI active in interrupt handler */
+ ASIDRV_RUN_ERR_TIMEOUT,
};

#define ASIDRV_IOCTL_RUN_SEQUENCE _IOWR('a', 1, struct asidrv_run_param)
--
2.18.2

2020-05-04 18:43:26

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 12/14] asidrv/asicmd: Add more test sequences for testing ASI

Add more options to the asicmd command to test access to map
or unmapped memory buffer, interrupt, NMI, scheduling while
using ASI.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asicmd.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
index 4d6a347a6d29..849fa09423e6 100644
--- a/drivers/staging/asi/asicmd.c
+++ b/drivers/staging/asi/asicmd.c
@@ -26,6 +26,20 @@ struct asidrv_test {
struct asidrv_test test_list[] = {
{ "nop", ASIDRV_SEQ_NOP, true,
"enter/exit ASI and nothing else" },
+ { "mem", ASIDRV_SEQ_MEM, false,
+ "enter ASI and accessed an unmapped buffer" },
+ { "memmap", ASIDRV_SEQ_MEMMAP, true,
+ "enter ASI and accessed a mapped buffer" },
+ { "intr", ASIDRV_SEQ_INTERRUPT, true,
+ "receive an interruption while running with ASI" },
+ { "nmi", ASIDRV_SEQ_NMI, true,
+ "receive a NMI while running with ASI" },
+ { "intrnmi", ASIDRV_SEQ_INTRNMI, true,
+ "receive a NMI in an interrupt received while running with ASI" },
+ { "sched", ASIDRV_SEQ_SCHED, true,
+ "call schedule() while running with ASI" },
+ { "printk", ASIDRV_SEQ_PRINTK, true,
+ "call printk() while running with ASI" },
};

#define TEST_LIST_SIZE (sizeof(test_list) / sizeof(test_list[0]))
--
2.18.2

2020-05-04 18:43:27

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 13/14] asidrv/asicmd: Add options to manage ASI page faults

Add options to the asicmd CLI to list and clear ASI page faults. Also
add an option to enable/disable displaying stack trace on ASI page
fault.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asicmd.c | 68 ++++++++++++++++++++++++++++++++++--
1 file changed, 65 insertions(+), 3 deletions(-)

diff --git a/drivers/staging/asi/asicmd.c b/drivers/staging/asi/asicmd.c
index 849fa09423e6..4d7bb9df0fcc 100644
--- a/drivers/staging/asi/asicmd.c
+++ b/drivers/staging/asi/asicmd.c
@@ -52,6 +52,10 @@ static void usage(void)
printf("\n");
printf("Commands:\n");
printf(" all - run all tests\n");
+ printf(" fault - list ASI faults\n");
+ printf(" fltclr - clear ASI faults\n");
+ printf(" stkon - show stack on ASI fault\n");
+ printf(" stkoff - do not show stack on ASI fault\n");
printf("\n");
printf("Tests:\n");
for (i = 0; i < TEST_LIST_SIZE; i++)
@@ -91,10 +95,45 @@ static void asidrv_run_test(int fd, struct asidrv_test *test)
printf("TEST OK\n");
}

+static int asidrv_fault_list(int fd)
+{
+ struct asidrv_fault_list *flist;
+ int i, rv;
+
+ flist = malloc(sizeof(*flist) +
+ sizeof(struct asidrv_fault) * 10);
+ if (!flist) {
+ perror("malloc flist");
+ return -1;
+ }
+
+ flist->length = 10;
+ rv = ioctl(fd, ASIDRV_IOCTL_LIST_FAULT, flist);
+ if (rv < 0) {
+ perror("ioctl list fault");
+ return -1;
+ }
+
+ if (!flist->length) {
+ printf("ASI has no fault\n");
+ return 0;
+ }
+
+ printf("%-18s %5s %s\n", "ADDRESS", "COUNT", "SYMBOL");
+ for (i = 0; i < flist->length && i < 10; i++) {
+ printf("%#18llx %5u %s\n",
+ flist->fault[i].addr,
+ flist->fault[i].count,
+ flist->fault[i].symbol);
+ }
+
+ return 0;
+}
+
int main(int argc, char *argv[])
{
bool run_all, run;
- int i, j, fd;
+ int i, j, fd, err;
char *test;

if (argc <= 1) {
@@ -111,10 +150,33 @@ int main(int argc, char *argv[])
for (i = 1; i < argc; i++) {
test = argv[i];

- if (!strcmp(test, "all"))
+ if (!strcmp(test, "fault")) {
+ asidrv_fault_list(fd);
+ continue;
+
+ } else if (!strcmp(test, "fltclr")) {
+ err = ioctl(fd, ASIDRV_IOCTL_CLEAR_FAULT);
+ if (err)
+ perror("ioctl clear fault");
+ continue;
+
+ } else if (!strcmp(test, "stkon")) {
+ err = ioctl(fd, ASIDRV_IOCTL_LOG_FAULT_STACK, true);
+ if (err)
+ perror("ioctl log fault stack");
+ continue;
+
+ } else if (!strcmp(test, "stkoff")) {
+ err = ioctl(fd, ASIDRV_IOCTL_LOG_FAULT_STACK, false);
+ if (err)
+ perror("ioctl log fault sstack");
+ continue;
+
+ } else if (!strcmp(test, "all")) {
run_all = true;
- else
+ } else {
run_all = false;
+ }

run = false;
for (j = 0; j < TEST_LIST_SIZE; j++) {
--
2.18.2

2020-05-04 18:43:36

by Alexandre Chartre

[permalink] [raw]
Subject: [RFC v4][PATCH part-3 10/14] asidrv: Add ioctls to manage ASI mapped VA ranges

Add ioctls to list, add, clear ASI mapped VA ranges.

Signed-off-by: Alexandre Chartre <[email protected]>
---
drivers/staging/asi/asidrv.c | 138 +++++++++++++++++++++++++++++++++++
drivers/staging/asi/asidrv.h | 26 +++++++
2 files changed, 164 insertions(+)

diff --git a/drivers/staging/asi/asidrv.c b/drivers/staging/asi/asidrv.c
index e6edfbe5acea..9d9784629833 100644
--- a/drivers/staging/asi/asidrv.c
+++ b/drivers/staging/asi/asidrv.c
@@ -866,10 +866,137 @@ static int asidrv_ioctl_log_fault_stack(struct asi *asi, bool log_stack)
return 0;
}

+/*
+ * ASI decorated pagetable ioctls
+ */
+
+static int asidrv_ioctl_add_mapping(struct dpt *dpt, unsigned long arg)
+{
+ struct asidrv_mapping_list __user *umlist;
+ struct asidrv_mapping mapping;
+ __u32 umlist_len;
+ int i, err;
+
+ umlist = (struct asidrv_mapping_list *)arg;
+ if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+ return -EFAULT;
+
+ err = 0;
+ for (i = 0; i < umlist_len; i++) {
+ if (copy_from_user(&mapping, &umlist->mapping[i],
+ sizeof(mapping))) {
+ err = -EFAULT;
+ break;
+ }
+
+ pr_debug("add mapping %llx/%llx/%u %s\n",
+ mapping.addr, mapping.size, mapping.level,
+ mapping.percpu ? "percpu" : "");
+
+ if (mapping.percpu) {
+ if (mapping.level != PGT_LEVEL_PTE) {
+ err = -EINVAL;
+ break;
+ }
+ err = dpt_map_percpu(dpt, (void *)mapping.addr,
+ mapping.size);
+ } else {
+ err = dpt_map_range(dpt, (void *)mapping.addr,
+ mapping.size, mapping.level);
+ }
+ if (err)
+ break;
+ }
+
+ if (err)
+ return (i == 0) ? err : i;
+
+ return 0;
+}
+
+static int asidrv_ioctl_clear_mapping(struct dpt *dpt, unsigned long arg)
+{
+ struct asidrv_mapping_list __user *umlist;
+ struct asidrv_mapping mapping;
+ __u32 umlist_len;
+ int err, i;
+
+ umlist = (struct asidrv_mapping_list *)arg;
+ if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+ return -EFAULT;
+
+ err = 0;
+ for (i = 0; i < umlist_len; i++) {
+ if (copy_from_user(&mapping, &umlist->mapping[i],
+ sizeof(mapping))) {
+ err = -EFAULT;
+ break;
+ }
+
+ pr_debug("clear mapping %llx %s\n",
+ mapping.addr, mapping.percpu ? "percpu" : "");
+
+ if (mapping.percpu)
+ dpt_unmap_percpu(dpt, (void *)mapping.addr);
+ else
+ dpt_unmap(dpt, (void *)mapping.addr);
+ }
+
+ if (err)
+ return (i == 0) ? err : i;
+
+ return 0;
+}
+
+static int asidrv_ioctl_list_mapping(struct dpt *dpt, unsigned long arg)
+{
+ struct asidrv_mapping_list __user *umlist;
+ struct asidrv_mapping_list *mlist;
+ struct dpt_range_mapping *range;
+ unsigned long addr;
+ size_t mlist_size;
+ __u32 umlist_len;
+ int i;
+
+ umlist = (struct asidrv_mapping_list *)arg;
+ if (copy_from_user(&umlist_len, &umlist->length, sizeof(umlist_len)))
+ return -EFAULT;
+
+ umlist_len = min_t(unsigned int, umlist_len, 512);
+
+ mlist_size = sizeof(*mlist) +
+ sizeof(struct asidrv_mapping) * umlist_len;
+ mlist = kzalloc(mlist_size, GFP_KERNEL);
+ if (!mlist)
+ return -ENOMEM;
+
+ i = 0;
+ list_for_each_entry(range, &dpt->mapping_list, list) {
+ if (i < umlist_len) {
+ addr = (__u64)range->ptr;
+ mlist->mapping[i].addr = addr;
+ mlist->mapping[i].size = range->size;
+ mlist->mapping[i].level = range->level;
+ }
+ i++;
+ }
+ mlist->length = i;
+
+ if (copy_to_user(umlist, mlist, mlist_size)) {
+ kfree(mlist);
+ return -EFAULT;
+ }
+
+ kfree(mlist);
+
+ return 0;
+}
+
static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct asidrv_test *test = asidrv_test;
struct asi *asi = test->asi;
+ struct dpt *dpt = test->dpt;

switch (cmd) {

@@ -884,6 +1011,17 @@ static long asidrv_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case ASIDRV_IOCTL_LOG_FAULT_STACK:
return asidrv_ioctl_log_fault_stack(asi, arg);

+ /* ASI decorated pagetable ioctls */
+
+ case ASIDRV_IOCTL_LIST_MAPPING:
+ return asidrv_ioctl_list_mapping(dpt, arg);
+
+ case ASIDRV_IOCTL_ADD_MAPPING:
+ return asidrv_ioctl_add_mapping(dpt, arg);
+
+ case ASIDRV_IOCTL_CLEAR_MAPPING:
+ return asidrv_ioctl_clear_mapping(dpt, arg);
+
/* Test ioctls */

case ASIDRV_IOCTL_RUN_SEQUENCE:
diff --git a/drivers/staging/asi/asidrv.h b/drivers/staging/asi/asidrv.h
index 99ab9843e36b..f042106419db 100644
--- a/drivers/staging/asi/asidrv.h
+++ b/drivers/staging/asi/asidrv.h
@@ -48,6 +48,20 @@ enum asidrv_run_error {
#define ASIDRV_IOCTL_CLEAR_FAULT _IO('a', 3)
#define ASIDRV_IOCTL_LOG_FAULT_STACK _IO('a', 4)

+/*
+ * ASIDRV_IOCTL_ADD_MAPPING: add mapping to the ASI.
+ *
+ * User should set 'length' with the number of mapping described in the
+ * 'mapping' array.
+ * Return value:
+ * -1 - error no mapping was added
+ * 0 - no error, all mappings were added
+ * N>0 - error but the first N mappings were added
+ */
+#define ASIDRV_IOCTL_ADD_MAPPING _IOWR('a', 5, struct asidrv_mapping_list)
+#define ASIDRV_IOCTL_CLEAR_MAPPING _IOW('a', 6, struct asidrv_mapping_list)
+#define ASIDRV_IOCTL_LIST_MAPPING _IOWR('a', 7, struct asidrv_mapping_list)
+
#define ASIDRV_KSYM_NAME_LEN 128
/*
* We need KSYM_SYMBOL_LEN to lookup symbol. However it's not part of
@@ -73,4 +87,16 @@ struct asidrv_fault_list {
struct asidrv_fault fault[0];
};

+struct asidrv_mapping {
+ __u64 addr;
+ __u64 size;
+ __u32 level;
+ __u32 percpu;
+};
+
+struct asidrv_mapping_list {
+ __u32 length;
+ struct asidrv_mapping mapping[0];
+};
+
#endif
--
2.18.2