|
20 | 20 | #include <linux/arm-smccc.h> |
21 | 21 | #include <linux/cpu.h> |
22 | 22 | #include <linux/device.h> |
| 23 | +#include <linux/nospec.h> |
23 | 24 | #include <linux/prctl.h> |
24 | 25 |
|
25 | 26 | #include <asm/spectre.h> |
@@ -318,3 +319,120 @@ void spectre_v2_enable_mitigation(const struct arm64_cpu_capabilities *__unused) |
318 | 319 |
|
319 | 320 | update_mitigation_state(&spectre_v2_state, state); |
320 | 321 | } |
| 322 | + |
| 323 | +/* Spectre v4 prctl */ |
| 324 | +static void ssbd_ssbs_enable(struct task_struct *task) |
| 325 | +{ |
| 326 | + u64 val = is_compat_thread(task_thread_info(task)) ? |
| 327 | + PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; |
| 328 | + |
| 329 | + task_pt_regs(task)->pstate |= val; |
| 330 | +} |
| 331 | + |
| 332 | +static void ssbd_ssbs_disable(struct task_struct *task) |
| 333 | +{ |
| 334 | + u64 val = is_compat_thread(task_thread_info(task)) ? |
| 335 | + PSR_AA32_SSBS_BIT : PSR_SSBS_BIT; |
| 336 | + |
| 337 | + task_pt_regs(task)->pstate &= ~val; |
| 338 | +} |
| 339 | + |
| 340 | +/* |
| 341 | + * prctl interface for SSBD |
| 342 | + */ |
| 343 | +static int ssbd_prctl_set(struct task_struct *task, unsigned long ctrl) |
| 344 | +{ |
| 345 | + int state = arm64_get_ssbd_state(); |
| 346 | + |
| 347 | + /* Unsupported */ |
| 348 | + if (state == ARM64_SSBD_UNKNOWN) |
| 349 | + return -ENODEV; |
| 350 | + |
| 351 | + /* Treat the unaffected/mitigated state separately */ |
| 352 | + if (state == ARM64_SSBD_MITIGATED) { |
| 353 | + switch (ctrl) { |
| 354 | + case PR_SPEC_ENABLE: |
| 355 | + return -EPERM; |
| 356 | + case PR_SPEC_DISABLE: |
| 357 | + case PR_SPEC_FORCE_DISABLE: |
| 358 | + return 0; |
| 359 | + } |
| 360 | + } |
| 361 | + |
| 362 | + /* |
| 363 | + * Things are a bit backward here: the arm64 internal API |
| 364 | + * *enables the mitigation* when the userspace API *disables |
| 365 | + * speculation*. So much fun. |
| 366 | + */ |
| 367 | + switch (ctrl) { |
| 368 | + case PR_SPEC_ENABLE: |
| 369 | + /* If speculation is force disabled, enable is not allowed */ |
| 370 | + if (state == ARM64_SSBD_FORCE_ENABLE || |
| 371 | + task_spec_ssb_force_disable(task)) |
| 372 | + return -EPERM; |
| 373 | + task_clear_spec_ssb_disable(task); |
| 374 | + clear_tsk_thread_flag(task, TIF_SSBD); |
| 375 | + ssbd_ssbs_enable(task); |
| 376 | + break; |
| 377 | + case PR_SPEC_DISABLE: |
| 378 | + if (state == ARM64_SSBD_FORCE_DISABLE) |
| 379 | + return -EPERM; |
| 380 | + task_set_spec_ssb_disable(task); |
| 381 | + set_tsk_thread_flag(task, TIF_SSBD); |
| 382 | + ssbd_ssbs_disable(task); |
| 383 | + break; |
| 384 | + case PR_SPEC_FORCE_DISABLE: |
| 385 | + if (state == ARM64_SSBD_FORCE_DISABLE) |
| 386 | + return -EPERM; |
| 387 | + task_set_spec_ssb_disable(task); |
| 388 | + task_set_spec_ssb_force_disable(task); |
| 389 | + set_tsk_thread_flag(task, TIF_SSBD); |
| 390 | + ssbd_ssbs_disable(task); |
| 391 | + break; |
| 392 | + default: |
| 393 | + return -ERANGE; |
| 394 | + } |
| 395 | + |
| 396 | + return 0; |
| 397 | +} |
| 398 | + |
| 399 | +int arch_prctl_spec_ctrl_set(struct task_struct *task, unsigned long which, |
| 400 | + unsigned long ctrl) |
| 401 | +{ |
| 402 | + switch (which) { |
| 403 | + case PR_SPEC_STORE_BYPASS: |
| 404 | + return ssbd_prctl_set(task, ctrl); |
| 405 | + default: |
| 406 | + return -ENODEV; |
| 407 | + } |
| 408 | +} |
| 409 | + |
| 410 | +static int ssbd_prctl_get(struct task_struct *task) |
| 411 | +{ |
| 412 | + switch (arm64_get_ssbd_state()) { |
| 413 | + case ARM64_SSBD_UNKNOWN: |
| 414 | + return -ENODEV; |
| 415 | + case ARM64_SSBD_FORCE_ENABLE: |
| 416 | + return PR_SPEC_DISABLE; |
| 417 | + case ARM64_SSBD_KERNEL: |
| 418 | + if (task_spec_ssb_force_disable(task)) |
| 419 | + return PR_SPEC_PRCTL | PR_SPEC_FORCE_DISABLE; |
| 420 | + if (task_spec_ssb_disable(task)) |
| 421 | + return PR_SPEC_PRCTL | PR_SPEC_DISABLE; |
| 422 | + return PR_SPEC_PRCTL | PR_SPEC_ENABLE; |
| 423 | + case ARM64_SSBD_FORCE_DISABLE: |
| 424 | + return PR_SPEC_ENABLE; |
| 425 | + default: |
| 426 | + return PR_SPEC_NOT_AFFECTED; |
| 427 | + } |
| 428 | +} |
| 429 | + |
| 430 | +int arch_prctl_spec_ctrl_get(struct task_struct *task, unsigned long which) |
| 431 | +{ |
| 432 | + switch (which) { |
| 433 | + case PR_SPEC_STORE_BYPASS: |
| 434 | + return ssbd_prctl_get(task); |
| 435 | + default: |
| 436 | + return -ENODEV; |
| 437 | + } |
| 438 | +} |
0 commit comments