1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023 Huawei Device Co., Ltd.
4 */
5
6#include <asm/asm-offsets.h>
7#include <asm/ptrace.h>
8#include <linux/irqflags.h>
9
10/* The members of arrays below are corresponding to the enum defined in pointer_auth_context.h:
11 * enum pac_pt_regs {
12 *   REGS_X16 = 0,
13 *   REGS_X17,
14 *   REGS_LR,
15 *   REGS_SP,
16 *   REGS_PC,
17 *   REGS_PSTATE,
18 * };
19 *
20 * compat_regs_offset_array[]:
21 *   S_X14: the offset of compat_lr
22 *   S_X13: the offset of compat_sp
23 */
24static off_t compat_regs_offset_array[] = {0, 0, S_X14, S_X13, S_PC, S_PSTATE};
25static off_t regs_offset_array[] = {S_X16, S_X17, S_LR, S_SP, S_PC, S_PSTATE};
26
27int set_compat_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val)
28{
29	switch (regs_enum) {
30	case REGS_LR:
31	case REGS_SP:
32	case REGS_PC:
33	case REGS_PSTATE:
34		return set_compat_exception_context_register_asm(regs, compat_regs_offset_array[regs_enum], val);
35	default:
36		return -EINVAL;
37	}
38}
39
40int set_exception_context_register(void *regs, enum pac_pt_regs regs_enum, u64 val)
41{
42	if (compat_user_mode((struct pt_regs *)regs)) {
43		return set_compat_exception_context_register(regs, regs_enum, val);
44	} else {
45		switch (regs_enum) {
46		case REGS_X16:
47		case REGS_X17:
48		case REGS_LR:
49		case REGS_SP:
50		case REGS_PC:
51		case REGS_PSTATE:
52			return set_exception_context_register_asm(regs, regs_offset_array[regs_enum], val);
53		default:
54			return -EINVAL;
55		}
56	}
57}
58
59void set_compat_exception_context_register_index(struct pt_regs *regs, int index, uint64_t val)
60{
61	/* 14 means the index of compat_lr */
62	if (index == 14) {
63		set_compat_exception_context_register_asm(regs, S_X14, val);
64	/* 13 means the index of compat_sp */
65	} else if (index == 13) {
66		set_compat_exception_context_register_asm(regs, S_X13, val);
67	} else {
68		regs->regs[index] = val;
69	}
70}
71
72void set_exception_context_register_index(struct pt_regs *regs, int index, uint64_t val)
73{
74	off_t offset;
75
76	if (compat_user_mode(regs)) {
77		set_compat_exception_context_register_index(regs, index, val);
78	} else {
79		switch (index) {
80		/* 16 means the index of regs[16] */
81		case 16:
82		/* 17 means the index of regs[17] */
83		case 17:
84		/* 30 means the index of regs[30] */
85		case 30:
86			offset = offsetof(struct pt_regs, regs[index]);
87			set_exception_context_register_asm(regs, offset, val);
88			break;
89		default:
90			regs->regs[index] = val;
91		}
92	}
93}
94
95void sign_compat_exception_context(void *regs)
96{
97	unsigned long irq_flags;
98	local_irq_save(irq_flags);
99	sign_compat_exception_context_asm(regs);
100	local_irq_restore(irq_flags);
101}
102
103void auth_compat_exception_context(void *regs)
104{
105	unsigned long irq_flags;
106	local_irq_save(irq_flags);
107	auth_compat_exception_context_asm(regs);
108	local_irq_restore(irq_flags);
109}
110
111void sign_exception_context(void *regs)
112{
113	unsigned long irq_flags;
114	local_irq_save(irq_flags);
115	if (compat_user_mode((struct pt_regs *)regs)) {
116		sign_compat_exception_context_asm(regs);
117	} else {
118		sign_exception_context_asm(regs);
119	}
120	local_irq_restore(irq_flags);
121}
122
123void auth_exception_context(void *regs)
124{
125	unsigned long irq_flags;
126	local_irq_save(irq_flags);
127	if (compat_user_mode((struct pt_regs *)regs)) {
128		auth_compat_exception_context_asm(regs);
129	} else {
130		auth_exception_context_asm(regs);
131	}
132	local_irq_restore(irq_flags);
133}
134