Skip to content

Commit 8dca182

Browse files
jimmyzhekartben
authored andcommitted
driver: interrupt_controller: intc_clic: fixed access CLIC w/o privileged
Temporarily disabled PMP stack guard to allow access to CLIC M-mode register, because U-mode load/store (mstatus.MPRV=0x1,MPP=0x0) is restricted. Signed-off-by: Jimmy Zheng <jimmyzhe@andestech.com>
1 parent 9349d54 commit 8dca182

File tree

1 file changed

+93
-20
lines changed

1 file changed

+93
-20
lines changed

drivers/interrupt_controller/intc_clic.c

Lines changed: 93 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,95 @@ struct clic_config {
3232
mem_addr_t base;
3333
};
3434

35+
struct pmp_stack_guard_key_t {
36+
unsigned long mstatus;
37+
unsigned int irq_key;
38+
};
39+
40+
/*
41+
* M-mode CLIC memory-mapped registers are accessible only in M-mode.
42+
* Temporarily disable the PMP stack guard (set mstatus.MPRV = 0) to configure
43+
* CLIC registers, then restore the PMP stack guard using these functions.
44+
*/
45+
static ALWAYS_INLINE void disable_pmp_stack_guard(struct pmp_stack_guard_key_t *key)
46+
{
47+
if (IS_ENABLED(CONFIG_PMP_STACK_GUARD)) {
48+
key->irq_key = irq_lock();
49+
key->mstatus = csr_read_clear(mstatus, MSTATUS_MPRV);
50+
} else {
51+
ARG_UNUSED(key);
52+
}
53+
}
54+
55+
static ALWAYS_INLINE void restore_pmp_stack_guard(struct pmp_stack_guard_key_t key)
56+
{
57+
if (IS_ENABLED(CONFIG_PMP_STACK_GUARD)) {
58+
csr_write(mstatus, key.mstatus);
59+
irq_unlock(key.irq_key);
60+
} else {
61+
ARG_UNUSED(key);
62+
}
63+
}
64+
65+
static ALWAYS_INLINE void write_clic32(const struct device *dev, uint32_t offset, uint32_t value)
66+
{
67+
const struct clic_config *config = dev->config;
68+
mem_addr_t reg_addr = config->base + offset;
69+
struct pmp_stack_guard_key_t key;
70+
71+
disable_pmp_stack_guard(&key);
72+
sys_write32(value, reg_addr);
73+
restore_pmp_stack_guard(key);
74+
}
75+
76+
static ALWAYS_INLINE uint32_t read_clic32(const struct device *dev, uint32_t offset)
77+
{
78+
const struct clic_config *config = dev->config;
79+
mem_addr_t reg_addr = config->base + offset;
80+
struct pmp_stack_guard_key_t key;
81+
uint32_t reg;
82+
83+
disable_pmp_stack_guard(&key);
84+
reg = sys_read32(reg_addr);
85+
restore_pmp_stack_guard(key);
86+
87+
return reg;
88+
}
89+
90+
static ALWAYS_INLINE void write_clic8(const struct device *dev, uint32_t offset, uint8_t value)
91+
{
92+
const struct clic_config *config = dev->config;
93+
mem_addr_t reg_addr = config->base + offset;
94+
struct pmp_stack_guard_key_t key;
95+
96+
disable_pmp_stack_guard(&key);
97+
sys_write8(value, reg_addr);
98+
restore_pmp_stack_guard(key);
99+
}
100+
101+
static ALWAYS_INLINE uint8_t read_clic8(const struct device *dev, uint32_t offset)
102+
{
103+
const struct clic_config *config = dev->config;
104+
mem_addr_t reg_addr = config->base + offset;
105+
struct pmp_stack_guard_key_t key;
106+
uint32_t reg;
107+
108+
disable_pmp_stack_guard(&key);
109+
reg = sys_read8(reg_addr);
110+
restore_pmp_stack_guard(key);
111+
112+
return reg;
113+
}
114+
35115
/**
36116
* @brief Enable interrupt
37117
*/
38118
void riscv_clic_irq_enable(uint32_t irq)
39119
{
40120
const struct device *dev = DEVICE_DT_INST_GET(0);
41-
const struct clic_config *config = dev->config;
42121
union CLICINTIE clicintie = {.b = {.IE = 0x1}};
43122

44-
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
123+
write_clic8(dev, CLIC_INTIE(irq), clicintie.w);
45124
}
46125

47126
/**
@@ -50,10 +129,9 @@ void riscv_clic_irq_enable(uint32_t irq)
50129
void riscv_clic_irq_disable(uint32_t irq)
51130
{
52131
const struct device *dev = DEVICE_DT_INST_GET(0);
53-
const struct clic_config *config = dev->config;
54132
union CLICINTIE clicintie = {.b = {.IE = 0x0}};
55133

56-
sys_write8(clicintie.w, config->base + CLIC_INTIE(irq));
134+
write_clic8(dev, CLIC_INTIE(irq), clicintie.w);
57135
}
58136

59137
/**
@@ -62,8 +140,7 @@ void riscv_clic_irq_disable(uint32_t irq)
62140
int riscv_clic_irq_is_enabled(uint32_t irq)
63141
{
64142
const struct device *dev = DEVICE_DT_INST_GET(0);
65-
const struct clic_config *config = dev->config;
66-
union CLICINTIE clicintie = {.w = sys_read8(config->base + CLIC_INTIE(irq))};
143+
union CLICINTIE clicintie = {.w = read_clic8(dev, CLIC_INTIE(irq))};
67144

68145
return clicintie.b.IE;
69146
}
@@ -74,7 +151,6 @@ int riscv_clic_irq_is_enabled(uint32_t irq)
74151
void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
75152
{
76153
const struct device *dev = DEVICE_DT_INST_GET(0);
77-
const struct clic_config *config = dev->config;
78154
const struct clic_data *data = dev->data;
79155

80156
/*
@@ -96,12 +172,12 @@ void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
96172
(MIN(pri, max_level) << (8U - data->nlbits)) |
97173
BIT_MASK(8U - data->intctlbits);
98174

99-
sys_write8(intctrl, config->base + CLIC_INTCTRL(irq));
175+
write_clic8(dev, CLIC_INTCTRL(irq), intctrl);
100176

101177
/* Set the IRQ operates in machine mode, non-vectoring and the trigger type. */
102178
union CLICINTATTR clicattr = {.b = {.mode = 0x3, .shv = 0x0, .trg = flags & BIT_MASK(3)}};
103179

104-
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
180+
write_clic8(dev, CLIC_INTATTR(irq), clicattr.w);
105181
}
106182

107183
/**
@@ -110,12 +186,11 @@ void riscv_clic_irq_priority_set(uint32_t irq, uint32_t pri, uint32_t flags)
110186
void riscv_clic_irq_vector_set(uint32_t irq)
111187
{
112188
const struct device *dev = DEVICE_DT_INST_GET(0);
113-
const struct clic_config *config = dev->config;
114-
union CLICINTATTR clicattr = {.w = sys_read8(config->base + CLIC_INTATTR(irq))};
189+
union CLICINTATTR clicattr = {.w = read_clic8(dev, CLIC_INTATTR(irq))};
115190

116191
/* Set Selective Hardware Vectoring. */
117192
clicattr.b.shv = 1;
118-
sys_write8(clicattr.w, config->base + CLIC_INTATTR(irq));
193+
write_clic8(dev, CLIC_INTATTR(irq), clicattr.w);
119194
}
120195

121196
/**
@@ -124,25 +199,23 @@ void riscv_clic_irq_vector_set(uint32_t irq)
124199
void riscv_clic_irq_set_pending(uint32_t irq)
125200
{
126201
const struct device *dev = DEVICE_DT_INST_GET(0);
127-
const struct clic_config *config = dev->config;
128202
union CLICINTIP clicintip = {.b = {.IP = 0x1}};
129203

130-
sys_write8(clicintip.w, config->base + CLIC_INTIP(irq));
204+
write_clic8(dev, CLIC_INTIP(irq), clicintip.w);
131205
}
132206

133207
static int clic_init(const struct device *dev)
134208
{
135-
const struct clic_config *config = dev->config;
136209
struct clic_data *data = dev->data;
137210

138211
if (IS_ENABLED(CONFIG_NUCLEI_ECLIC)) {
139212
/* Configure the interrupt level threshold. */
140213
union CLICMTH clicmth = {.b = {.mth = 0x0}};
141214

142-
sys_write32(clicmth.qw, config->base + CLIC_MTH);
215+
write_clic32(dev, CLIC_MTH, clicmth.qw);
143216

144217
/* Detect the number of bits for the clicintctl register. */
145-
union CLICINFO clicinfo = {.qw = sys_read32(config->base + CLIC_INFO)};
218+
union CLICINFO clicinfo = {.qw = read_clic32(dev, CLIC_INFO)};
146219

147220
data->intctlbits = clicinfo.b.intctlbits;
148221

@@ -151,18 +224,18 @@ static int clic_init(const struct device *dev)
151224
}
152225

153226
/* Configure the number of bits assigned to interrupt levels. */
154-
union CLICCFG cliccfg = {.qw = sys_read32(config->base + CLIC_CFG)};
227+
union CLICCFG cliccfg = {.qw = read_clic32(dev, CLIC_CFG)};
155228

156229
cliccfg.w.nlbits = data->nlbits;
157-
sys_write32(cliccfg.qw, config->base + CLIC_CFG);
230+
write_clic32(dev, CLIC_CFG, cliccfg.qw);
158231
} else {
159232
/* Configure the interrupt level threshold by CSR mintthresh. */
160233
csr_write(CSR_MINTTHRESH, 0x0);
161234
}
162235

163236
/* Reset all interrupt control register */
164237
for (int i = 0; i < CONFIG_NUM_IRQS; i++) {
165-
sys_write32(0, config->base + CLIC_CTRL(i));
238+
write_clic32(dev, CLIC_CTRL(i), 0);
166239
}
167240

168241
return 0;

0 commit comments

Comments
 (0)