18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Test module for unwind_for_each_frame 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "test_unwind: " fmt 78c2ecf20Sopenharmony_ci#include <asm/unwind.h> 88c2ecf20Sopenharmony_ci#include <linux/completion.h> 98c2ecf20Sopenharmony_ci#include <linux/kallsyms.h> 108c2ecf20Sopenharmony_ci#include <linux/kthread.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/string.h> 148c2ecf20Sopenharmony_ci#include <linux/kprobes.h> 158c2ecf20Sopenharmony_ci#include <linux/wait.h> 168c2ecf20Sopenharmony_ci#include <asm/irq.h> 178c2ecf20Sopenharmony_ci#include <asm/delay.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define BT_BUF_SIZE (PAGE_SIZE * 4) 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* 228c2ecf20Sopenharmony_ci * To avoid printk line limit split backtrace by lines 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_cistatic void print_backtrace(char *bt) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci char *p; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci while (true) { 298c2ecf20Sopenharmony_ci p = strsep(&bt, "\n"); 308c2ecf20Sopenharmony_ci if (!p) 318c2ecf20Sopenharmony_ci break; 328c2ecf20Sopenharmony_ci pr_err("%s\n", p); 338c2ecf20Sopenharmony_ci } 348c2ecf20Sopenharmony_ci} 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* 378c2ecf20Sopenharmony_ci * Calls unwind_for_each_frame(task, regs, sp) and verifies that the result 388c2ecf20Sopenharmony_ci * contains unwindme_func2 followed by unwindme_func1. 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_cistatic noinline int test_unwind(struct task_struct *task, struct pt_regs *regs, 418c2ecf20Sopenharmony_ci unsigned long sp) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci int frame_count, prev_is_func2, seen_func2_func1; 448c2ecf20Sopenharmony_ci const int max_frames = 128; 458c2ecf20Sopenharmony_ci struct unwind_state state; 468c2ecf20Sopenharmony_ci size_t bt_pos = 0; 478c2ecf20Sopenharmony_ci int ret = 0; 488c2ecf20Sopenharmony_ci char *bt; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci bt = kmalloc(BT_BUF_SIZE, GFP_ATOMIC); 518c2ecf20Sopenharmony_ci if (!bt) { 528c2ecf20Sopenharmony_ci pr_err("failed to allocate backtrace buffer\n"); 538c2ecf20Sopenharmony_ci return -ENOMEM; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci /* Unwind. */ 568c2ecf20Sopenharmony_ci frame_count = 0; 578c2ecf20Sopenharmony_ci prev_is_func2 = 0; 588c2ecf20Sopenharmony_ci seen_func2_func1 = 0; 598c2ecf20Sopenharmony_ci unwind_for_each_frame(&state, task, regs, sp) { 608c2ecf20Sopenharmony_ci unsigned long addr = unwind_get_return_address(&state); 618c2ecf20Sopenharmony_ci char sym[KSYM_SYMBOL_LEN]; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci if (frame_count++ == max_frames) 648c2ecf20Sopenharmony_ci break; 658c2ecf20Sopenharmony_ci if (state.reliable && !addr) { 668c2ecf20Sopenharmony_ci pr_err("unwind state reliable but addr is 0\n"); 678c2ecf20Sopenharmony_ci kfree(bt); 688c2ecf20Sopenharmony_ci return -EINVAL; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci sprint_symbol(sym, addr); 718c2ecf20Sopenharmony_ci if (bt_pos < BT_BUF_SIZE) { 728c2ecf20Sopenharmony_ci bt_pos += snprintf(bt + bt_pos, BT_BUF_SIZE - bt_pos, 738c2ecf20Sopenharmony_ci state.reliable ? " [%-7s%px] %pSR\n" : 748c2ecf20Sopenharmony_ci "([%-7s%px] %pSR)\n", 758c2ecf20Sopenharmony_ci stack_type_name(state.stack_info.type), 768c2ecf20Sopenharmony_ci (void *)state.sp, (void *)state.ip); 778c2ecf20Sopenharmony_ci if (bt_pos >= BT_BUF_SIZE) 788c2ecf20Sopenharmony_ci pr_err("backtrace buffer is too small\n"); 798c2ecf20Sopenharmony_ci } 808c2ecf20Sopenharmony_ci frame_count += 1; 818c2ecf20Sopenharmony_ci if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1")) 828c2ecf20Sopenharmony_ci seen_func2_func1 = 1; 838c2ecf20Sopenharmony_ci prev_is_func2 = str_has_prefix(sym, "unwindme_func2"); 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* Check the results. */ 878c2ecf20Sopenharmony_ci if (unwind_error(&state)) { 888c2ecf20Sopenharmony_ci pr_err("unwind error\n"); 898c2ecf20Sopenharmony_ci ret = -EINVAL; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci if (!seen_func2_func1) { 928c2ecf20Sopenharmony_ci pr_err("unwindme_func2 and unwindme_func1 not found\n"); 938c2ecf20Sopenharmony_ci ret = -EINVAL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci if (frame_count == max_frames) { 968c2ecf20Sopenharmony_ci pr_err("Maximum number of frames exceeded\n"); 978c2ecf20Sopenharmony_ci ret = -EINVAL; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci if (ret) 1008c2ecf20Sopenharmony_ci print_backtrace(bt); 1018c2ecf20Sopenharmony_ci kfree(bt); 1028c2ecf20Sopenharmony_ci return ret; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* State of the task being unwound. */ 1068c2ecf20Sopenharmony_cistruct unwindme { 1078c2ecf20Sopenharmony_ci int flags; 1088c2ecf20Sopenharmony_ci int ret; 1098c2ecf20Sopenharmony_ci struct task_struct *task; 1108c2ecf20Sopenharmony_ci struct completion task_ready; 1118c2ecf20Sopenharmony_ci wait_queue_head_t task_wq; 1128c2ecf20Sopenharmony_ci unsigned long sp; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic struct unwindme *unwindme; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* Values of unwindme.flags. */ 1188c2ecf20Sopenharmony_ci#define UWM_DEFAULT 0x0 1198c2ecf20Sopenharmony_ci#define UWM_THREAD 0x1 /* Unwind a separate task. */ 1208c2ecf20Sopenharmony_ci#define UWM_REGS 0x2 /* Pass regs to test_unwind(). */ 1218c2ecf20Sopenharmony_ci#define UWM_SP 0x4 /* Pass sp to test_unwind(). */ 1228c2ecf20Sopenharmony_ci#define UWM_CALLER 0x8 /* Unwind starting from caller. */ 1238c2ecf20Sopenharmony_ci#define UWM_SWITCH_STACK 0x10 /* Use CALL_ON_STACK. */ 1248c2ecf20Sopenharmony_ci#define UWM_IRQ 0x20 /* Unwind from irq context. */ 1258c2ecf20Sopenharmony_ci#define UWM_PGM 0x40 /* Unwind from program check handler. */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic __always_inline unsigned long get_psw_addr(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci unsigned long psw_addr; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci asm volatile( 1328c2ecf20Sopenharmony_ci "basr %[psw_addr],0\n" 1338c2ecf20Sopenharmony_ci : [psw_addr] "=d" (psw_addr)); 1348c2ecf20Sopenharmony_ci return psw_addr; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBES 1388c2ecf20Sopenharmony_cistatic int pgm_pre_handler(struct kprobe *p, struct pt_regs *regs) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct unwindme *u = unwindme; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci u->ret = test_unwind(NULL, (u->flags & UWM_REGS) ? regs : NULL, 1438c2ecf20Sopenharmony_ci (u->flags & UWM_SP) ? u->sp : 0); 1448c2ecf20Sopenharmony_ci return 0; 1458c2ecf20Sopenharmony_ci} 1468c2ecf20Sopenharmony_ci#endif 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* This function may or may not appear in the backtrace. */ 1498c2ecf20Sopenharmony_cistatic noinline int unwindme_func4(struct unwindme *u) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci if (!(u->flags & UWM_CALLER)) 1528c2ecf20Sopenharmony_ci u->sp = current_frame_address(); 1538c2ecf20Sopenharmony_ci if (u->flags & UWM_THREAD) { 1548c2ecf20Sopenharmony_ci complete(&u->task_ready); 1558c2ecf20Sopenharmony_ci wait_event(u->task_wq, kthread_should_park()); 1568c2ecf20Sopenharmony_ci kthread_parkme(); 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBES 1598c2ecf20Sopenharmony_ci } else if (u->flags & UWM_PGM) { 1608c2ecf20Sopenharmony_ci struct kprobe kp; 1618c2ecf20Sopenharmony_ci int ret; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci unwindme = u; 1648c2ecf20Sopenharmony_ci memset(&kp, 0, sizeof(kp)); 1658c2ecf20Sopenharmony_ci kp.symbol_name = "do_report_trap"; 1668c2ecf20Sopenharmony_ci kp.pre_handler = pgm_pre_handler; 1678c2ecf20Sopenharmony_ci ret = register_kprobe(&kp); 1688c2ecf20Sopenharmony_ci if (ret < 0) { 1698c2ecf20Sopenharmony_ci pr_err("register_kprobe failed %d\n", ret); 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * Trigger operation exception; use insn notation to bypass 1758c2ecf20Sopenharmony_ci * llvm's integrated assembler sanity checks. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci asm volatile( 1788c2ecf20Sopenharmony_ci " .insn e,0x0000\n" /* illegal opcode */ 1798c2ecf20Sopenharmony_ci "0: nopr %%r7\n" 1808c2ecf20Sopenharmony_ci EX_TABLE(0b, 0b) 1818c2ecf20Sopenharmony_ci :); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci unregister_kprobe(&kp); 1848c2ecf20Sopenharmony_ci unwindme = NULL; 1858c2ecf20Sopenharmony_ci return u->ret; 1868c2ecf20Sopenharmony_ci#endif 1878c2ecf20Sopenharmony_ci } else { 1888c2ecf20Sopenharmony_ci struct pt_regs regs; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci memset(®s, 0, sizeof(regs)); 1918c2ecf20Sopenharmony_ci regs.psw.addr = get_psw_addr(); 1928c2ecf20Sopenharmony_ci regs.gprs[15] = current_stack_pointer(); 1938c2ecf20Sopenharmony_ci return test_unwind(NULL, 1948c2ecf20Sopenharmony_ci (u->flags & UWM_REGS) ? ®s : NULL, 1958c2ecf20Sopenharmony_ci (u->flags & UWM_SP) ? u->sp : 0); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci/* This function may or may not appear in the backtrace. */ 2008c2ecf20Sopenharmony_cistatic noinline int unwindme_func3(struct unwindme *u) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci u->sp = current_frame_address(); 2038c2ecf20Sopenharmony_ci return unwindme_func4(u); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/* This function must appear in the backtrace. */ 2078c2ecf20Sopenharmony_cistatic noinline int unwindme_func2(struct unwindme *u) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci unsigned long flags; 2108c2ecf20Sopenharmony_ci int rc; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (u->flags & UWM_SWITCH_STACK) { 2138c2ecf20Sopenharmony_ci local_irq_save(flags); 2148c2ecf20Sopenharmony_ci local_mcck_disable(); 2158c2ecf20Sopenharmony_ci rc = CALL_ON_STACK(unwindme_func3, S390_lowcore.nodat_stack, 1, u); 2168c2ecf20Sopenharmony_ci local_mcck_enable(); 2178c2ecf20Sopenharmony_ci local_irq_restore(flags); 2188c2ecf20Sopenharmony_ci return rc; 2198c2ecf20Sopenharmony_ci } else { 2208c2ecf20Sopenharmony_ci return unwindme_func3(u); 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* This function must follow unwindme_func2 in the backtrace. */ 2258c2ecf20Sopenharmony_cistatic noinline int unwindme_func1(void *u) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci return unwindme_func2((struct unwindme *)u); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic void unwindme_irq_handler(struct ext_code ext_code, 2318c2ecf20Sopenharmony_ci unsigned int param32, 2328c2ecf20Sopenharmony_ci unsigned long param64) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct unwindme *u = READ_ONCE(unwindme); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (u && u->task == current) { 2378c2ecf20Sopenharmony_ci unwindme = NULL; 2388c2ecf20Sopenharmony_ci u->task = NULL; 2398c2ecf20Sopenharmony_ci u->ret = unwindme_func1(u); 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int test_unwind_irq(struct unwindme *u) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci preempt_disable(); 2468c2ecf20Sopenharmony_ci if (register_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler)) { 2478c2ecf20Sopenharmony_ci pr_info("Couldn't register external interrupt handler"); 2488c2ecf20Sopenharmony_ci return -1; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci u->task = current; 2518c2ecf20Sopenharmony_ci unwindme = u; 2528c2ecf20Sopenharmony_ci udelay(1); 2538c2ecf20Sopenharmony_ci unregister_external_irq(EXT_IRQ_CLK_COMP, unwindme_irq_handler); 2548c2ecf20Sopenharmony_ci preempt_enable(); 2558c2ecf20Sopenharmony_ci return u->ret; 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci/* Spawns a task and passes it to test_unwind(). */ 2598c2ecf20Sopenharmony_cistatic int test_unwind_task(struct unwindme *u) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci struct task_struct *task; 2628c2ecf20Sopenharmony_ci int ret; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Initialize thread-related fields. */ 2658c2ecf20Sopenharmony_ci init_completion(&u->task_ready); 2668c2ecf20Sopenharmony_ci init_waitqueue_head(&u->task_wq); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci /* 2698c2ecf20Sopenharmony_ci * Start the task and wait until it reaches unwindme_func4() and sleeps 2708c2ecf20Sopenharmony_ci * in (task_ready, unwind_done] range. 2718c2ecf20Sopenharmony_ci */ 2728c2ecf20Sopenharmony_ci task = kthread_run(unwindme_func1, u, "%s", __func__); 2738c2ecf20Sopenharmony_ci if (IS_ERR(task)) { 2748c2ecf20Sopenharmony_ci pr_err("kthread_run() failed\n"); 2758c2ecf20Sopenharmony_ci return PTR_ERR(task); 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci /* 2788c2ecf20Sopenharmony_ci * Make sure task reaches unwindme_func4 before parking it, 2798c2ecf20Sopenharmony_ci * we might park it before kthread function has been executed otherwise 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ci wait_for_completion(&u->task_ready); 2828c2ecf20Sopenharmony_ci kthread_park(task); 2838c2ecf20Sopenharmony_ci /* Unwind. */ 2848c2ecf20Sopenharmony_ci ret = test_unwind(task, NULL, (u->flags & UWM_SP) ? u->sp : 0); 2858c2ecf20Sopenharmony_ci kthread_stop(task); 2868c2ecf20Sopenharmony_ci return ret; 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic int test_unwind_flags(int flags) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci struct unwindme u; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci u.flags = flags; 2948c2ecf20Sopenharmony_ci if (u.flags & UWM_THREAD) 2958c2ecf20Sopenharmony_ci return test_unwind_task(&u); 2968c2ecf20Sopenharmony_ci else if (u.flags & UWM_IRQ) 2978c2ecf20Sopenharmony_ci return test_unwind_irq(&u); 2988c2ecf20Sopenharmony_ci else 2998c2ecf20Sopenharmony_ci return unwindme_func1(&u); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int test_unwind_init(void) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci int ret = 0; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci#define TEST(flags) \ 3078c2ecf20Sopenharmony_cido { \ 3088c2ecf20Sopenharmony_ci pr_info("[ RUN ] " #flags "\n"); \ 3098c2ecf20Sopenharmony_ci if (!test_unwind_flags((flags))) { \ 3108c2ecf20Sopenharmony_ci pr_info("[ OK ] " #flags "\n"); \ 3118c2ecf20Sopenharmony_ci } else { \ 3128c2ecf20Sopenharmony_ci pr_err("[ FAILED ] " #flags "\n"); \ 3138c2ecf20Sopenharmony_ci ret = -EINVAL; \ 3148c2ecf20Sopenharmony_ci } \ 3158c2ecf20Sopenharmony_ci} while (0) 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci TEST(UWM_DEFAULT); 3188c2ecf20Sopenharmony_ci TEST(UWM_SP); 3198c2ecf20Sopenharmony_ci TEST(UWM_REGS); 3208c2ecf20Sopenharmony_ci TEST(UWM_SWITCH_STACK); 3218c2ecf20Sopenharmony_ci TEST(UWM_SP | UWM_REGS); 3228c2ecf20Sopenharmony_ci TEST(UWM_CALLER | UWM_SP); 3238c2ecf20Sopenharmony_ci TEST(UWM_CALLER | UWM_SP | UWM_REGS); 3248c2ecf20Sopenharmony_ci TEST(UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); 3258c2ecf20Sopenharmony_ci TEST(UWM_THREAD); 3268c2ecf20Sopenharmony_ci TEST(UWM_THREAD | UWM_SP); 3278c2ecf20Sopenharmony_ci TEST(UWM_THREAD | UWM_CALLER | UWM_SP); 3288c2ecf20Sopenharmony_ci TEST(UWM_IRQ); 3298c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_SWITCH_STACK); 3308c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_SP); 3318c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_REGS); 3328c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_SP | UWM_REGS); 3338c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_CALLER | UWM_SP); 3348c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS); 3358c2ecf20Sopenharmony_ci TEST(UWM_IRQ | UWM_CALLER | UWM_SP | UWM_REGS | UWM_SWITCH_STACK); 3368c2ecf20Sopenharmony_ci#ifdef CONFIG_KPROBES 3378c2ecf20Sopenharmony_ci TEST(UWM_PGM); 3388c2ecf20Sopenharmony_ci TEST(UWM_PGM | UWM_SP); 3398c2ecf20Sopenharmony_ci TEST(UWM_PGM | UWM_REGS); 3408c2ecf20Sopenharmony_ci TEST(UWM_PGM | UWM_SP | UWM_REGS); 3418c2ecf20Sopenharmony_ci#endif 3428c2ecf20Sopenharmony_ci#undef TEST 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci return ret; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void test_unwind_exit(void) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cimodule_init(test_unwind_init); 3528c2ecf20Sopenharmony_cimodule_exit(test_unwind_exit); 3538c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 354