|
7 | 7 | * Copyright (C) 2017 SiFive |
8 | 8 | */ |
9 | 9 |
|
| 10 | +#include <linux/bitfield.h> |
10 | 11 | #include <linux/cpu.h> |
11 | 12 | #include <linux/kernel.h> |
12 | 13 | #include <linux/sched.h> |
@@ -180,6 +181,10 @@ void flush_thread(void) |
180 | 181 | memset(¤t->thread.vstate, 0, sizeof(struct __riscv_v_ext_state)); |
181 | 182 | clear_tsk_thread_flag(current, TIF_RISCV_V_DEFER_RESTORE); |
182 | 183 | #endif |
| 184 | +#ifdef CONFIG_RISCV_ISA_SUPM |
| 185 | + if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) |
| 186 | + envcfg_update_bits(current, ENVCFG_PMM, ENVCFG_PMM_PMLEN_0); |
| 187 | +#endif |
183 | 188 | } |
184 | 189 |
|
185 | 190 | void arch_release_task_struct(struct task_struct *tsk) |
@@ -242,3 +247,89 @@ void __init arch_task_cache_init(void) |
242 | 247 | { |
243 | 248 | riscv_v_setup_ctx_cache(); |
244 | 249 | } |
| 250 | + |
| 251 | +#ifdef CONFIG_RISCV_ISA_SUPM |
| 252 | +enum { |
| 253 | + PMLEN_0 = 0, |
| 254 | + PMLEN_7 = 7, |
| 255 | + PMLEN_16 = 16, |
| 256 | +}; |
| 257 | + |
| 258 | +static bool have_user_pmlen_7; |
| 259 | +static bool have_user_pmlen_16; |
| 260 | + |
| 261 | +long set_tagged_addr_ctrl(struct task_struct *task, unsigned long arg) |
| 262 | +{ |
| 263 | + unsigned long valid_mask = PR_PMLEN_MASK; |
| 264 | + struct thread_info *ti = task_thread_info(task); |
| 265 | + unsigned long pmm; |
| 266 | + u8 pmlen; |
| 267 | + |
| 268 | + if (is_compat_thread(ti)) |
| 269 | + return -EINVAL; |
| 270 | + |
| 271 | + if (arg & ~valid_mask) |
| 272 | + return -EINVAL; |
| 273 | + |
| 274 | + /* |
| 275 | + * Prefer the smallest PMLEN that satisfies the user's request, |
| 276 | + * in case choosing a larger PMLEN has a performance impact. |
| 277 | + */ |
| 278 | + pmlen = FIELD_GET(PR_PMLEN_MASK, arg); |
| 279 | + if (pmlen == PMLEN_0) |
| 280 | + pmm = ENVCFG_PMM_PMLEN_0; |
| 281 | + else if (pmlen <= PMLEN_7 && have_user_pmlen_7) |
| 282 | + pmm = ENVCFG_PMM_PMLEN_7; |
| 283 | + else if (pmlen <= PMLEN_16 && have_user_pmlen_16) |
| 284 | + pmm = ENVCFG_PMM_PMLEN_16; |
| 285 | + else |
| 286 | + return -EINVAL; |
| 287 | + |
| 288 | + envcfg_update_bits(task, ENVCFG_PMM, pmm); |
| 289 | + |
| 290 | + return 0; |
| 291 | +} |
| 292 | + |
| 293 | +long get_tagged_addr_ctrl(struct task_struct *task) |
| 294 | +{ |
| 295 | + struct thread_info *ti = task_thread_info(task); |
| 296 | + long ret = 0; |
| 297 | + |
| 298 | + if (is_compat_thread(ti)) |
| 299 | + return -EINVAL; |
| 300 | + |
| 301 | + switch (task->thread.envcfg & ENVCFG_PMM) { |
| 302 | + case ENVCFG_PMM_PMLEN_7: |
| 303 | + ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_7); |
| 304 | + break; |
| 305 | + case ENVCFG_PMM_PMLEN_16: |
| 306 | + ret = FIELD_PREP(PR_PMLEN_MASK, PMLEN_16); |
| 307 | + break; |
| 308 | + } |
| 309 | + |
| 310 | + return ret; |
| 311 | +} |
| 312 | + |
| 313 | +static bool try_to_set_pmm(unsigned long value) |
| 314 | +{ |
| 315 | + csr_set(CSR_ENVCFG, value); |
| 316 | + return (csr_read_clear(CSR_ENVCFG, ENVCFG_PMM) & ENVCFG_PMM) == value; |
| 317 | +} |
| 318 | + |
| 319 | +static int __init tagged_addr_init(void) |
| 320 | +{ |
| 321 | + if (!riscv_has_extension_unlikely(RISCV_ISA_EXT_SUPM)) |
| 322 | + return 0; |
| 323 | + |
| 324 | + /* |
| 325 | + * envcfg.PMM is a WARL field. Detect which values are supported. |
| 326 | + * Assume the supported PMLEN values are the same on all harts. |
| 327 | + */ |
| 328 | + csr_clear(CSR_ENVCFG, ENVCFG_PMM); |
| 329 | + have_user_pmlen_7 = try_to_set_pmm(ENVCFG_PMM_PMLEN_7); |
| 330 | + have_user_pmlen_16 = try_to_set_pmm(ENVCFG_PMM_PMLEN_16); |
| 331 | + |
| 332 | + return 0; |
| 333 | +} |
| 334 | +core_initcall(tagged_addr_init); |
| 335 | +#endif /* CONFIG_RISCV_ISA_SUPM */ |
0 commit comments