diff options
Diffstat (limited to 'arch/x86/kernel/xsave.c')
-rw-r--r-- | arch/x86/kernel/xsave.c | 118 |
1 files changed, 110 insertions, 8 deletions
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c index a4b451c6addf..940b142cc11f 100644 --- a/arch/x86/kernel/xsave.c +++ b/arch/x86/kernel/xsave.c | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include <linux/bootmem.h> | 9 | #include <linux/bootmem.h> |
10 | #include <linux/compat.h> | 10 | #include <linux/compat.h> |
11 | #include <linux/cpu.h> | ||
11 | #include <asm/i387.h> | 12 | #include <asm/i387.h> |
12 | #include <asm/fpu-internal.h> | 13 | #include <asm/fpu-internal.h> |
13 | #include <asm/sigframe.h> | 14 | #include <asm/sigframe.h> |
@@ -24,7 +25,9 @@ u64 pcntxt_mask; | |||
24 | struct xsave_struct *init_xstate_buf; | 25 | struct xsave_struct *init_xstate_buf; |
25 | 26 | ||
26 | static struct _fpx_sw_bytes fx_sw_reserved, fx_sw_reserved_ia32; | 27 | static struct _fpx_sw_bytes fx_sw_reserved, fx_sw_reserved_ia32; |
27 | static unsigned int *xstate_offsets, *xstate_sizes, xstate_features; | 28 | static unsigned int *xstate_offsets, *xstate_sizes; |
29 | static unsigned int xstate_comp_offsets[sizeof(pcntxt_mask)*8]; | ||
30 | static unsigned int xstate_features; | ||
28 | 31 | ||
29 | /* | 32 | /* |
30 | * If a processor implementation discern that a processor state component is | 33 | * If a processor implementation discern that a processor state component is |
@@ -283,7 +286,7 @@ sanitize_restored_xstate(struct task_struct *tsk, | |||
283 | 286 | ||
284 | if (use_xsave()) { | 287 | if (use_xsave()) { |
285 | /* These bits must be zero. */ | 288 | /* These bits must be zero. */ |
286 | xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0; | 289 | memset(xsave_hdr->reserved, 0, 48); |
287 | 290 | ||
288 | /* | 291 | /* |
289 | * Init the state that is not present in the memory | 292 | * Init the state that is not present in the memory |
@@ -479,6 +482,52 @@ static void __init setup_xstate_features(void) | |||
479 | } | 482 | } |
480 | 483 | ||
481 | /* | 484 | /* |
485 | * This function sets up offsets and sizes of all extended states in | ||
486 | * xsave area. This supports both standard format and compacted format | ||
487 | * of the xsave aread. | ||
488 | * | ||
489 | * Input: void | ||
490 | * Output: void | ||
491 | */ | ||
492 | void setup_xstate_comp(void) | ||
493 | { | ||
494 | unsigned int xstate_comp_sizes[sizeof(pcntxt_mask)*8]; | ||
495 | int i; | ||
496 | |||
497 | /* | ||
498 | * The FP xstates and SSE xstates are legacy states. They are always | ||
499 | * in the fixed offsets in the xsave area in either compacted form | ||
500 | * or standard form. | ||
501 | */ | ||
502 | xstate_comp_offsets[0] = 0; | ||
503 | xstate_comp_offsets[1] = offsetof(struct i387_fxsave_struct, xmm_space); | ||
504 | |||
505 | if (!cpu_has_xsaves) { | ||
506 | for (i = 2; i < xstate_features; i++) { | ||
507 | if (test_bit(i, (unsigned long *)&pcntxt_mask)) { | ||
508 | xstate_comp_offsets[i] = xstate_offsets[i]; | ||
509 | xstate_comp_sizes[i] = xstate_sizes[i]; | ||
510 | } | ||
511 | } | ||
512 | return; | ||
513 | } | ||
514 | |||
515 | xstate_comp_offsets[2] = FXSAVE_SIZE + XSAVE_HDR_SIZE; | ||
516 | |||
517 | for (i = 2; i < xstate_features; i++) { | ||
518 | if (test_bit(i, (unsigned long *)&pcntxt_mask)) | ||
519 | xstate_comp_sizes[i] = xstate_sizes[i]; | ||
520 | else | ||
521 | xstate_comp_sizes[i] = 0; | ||
522 | |||
523 | if (i > 2) | ||
524 | xstate_comp_offsets[i] = xstate_comp_offsets[i-1] | ||
525 | + xstate_comp_sizes[i-1]; | ||
526 | |||
527 | } | ||
528 | } | ||
529 | |||
530 | /* | ||
482 | * setup the xstate image representing the init state | 531 | * setup the xstate image representing the init state |
483 | */ | 532 | */ |
484 | static void __init setup_init_fpu_buf(void) | 533 | static void __init setup_init_fpu_buf(void) |
@@ -496,15 +545,21 @@ static void __init setup_init_fpu_buf(void) | |||
496 | 545 | ||
497 | setup_xstate_features(); | 546 | setup_xstate_features(); |
498 | 547 | ||
548 | if (cpu_has_xsaves) { | ||
549 | init_xstate_buf->xsave_hdr.xcomp_bv = | ||
550 | (u64)1 << 63 | pcntxt_mask; | ||
551 | init_xstate_buf->xsave_hdr.xstate_bv = pcntxt_mask; | ||
552 | } | ||
553 | |||
499 | /* | 554 | /* |
500 | * Init all the features state with header_bv being 0x0 | 555 | * Init all the features state with header_bv being 0x0 |
501 | */ | 556 | */ |
502 | xrstor_state(init_xstate_buf, -1); | 557 | xrstor_state_booting(init_xstate_buf, -1); |
503 | /* | 558 | /* |
504 | * Dump the init state again. This is to identify the init state | 559 | * Dump the init state again. This is to identify the init state |
505 | * of any feature which is not represented by all zero's. | 560 | * of any feature which is not represented by all zero's. |
506 | */ | 561 | */ |
507 | xsave_state(init_xstate_buf, -1); | 562 | xsave_state_booting(init_xstate_buf, -1); |
508 | } | 563 | } |
509 | 564 | ||
510 | static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO; | 565 | static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO; |
@@ -520,6 +575,30 @@ static int __init eager_fpu_setup(char *s) | |||
520 | } | 575 | } |
521 | __setup("eagerfpu=", eager_fpu_setup); | 576 | __setup("eagerfpu=", eager_fpu_setup); |
522 | 577 | ||
578 | |||
579 | /* | ||
580 | * Calculate total size of enabled xstates in XCR0/pcntxt_mask. | ||
581 | */ | ||
582 | static void __init init_xstate_size(void) | ||
583 | { | ||
584 | unsigned int eax, ebx, ecx, edx; | ||
585 | int i; | ||
586 | |||
587 | if (!cpu_has_xsaves) { | ||
588 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); | ||
589 | xstate_size = ebx; | ||
590 | return; | ||
591 | } | ||
592 | |||
593 | xstate_size = FXSAVE_SIZE + XSAVE_HDR_SIZE; | ||
594 | for (i = 2; i < 64; i++) { | ||
595 | if (test_bit(i, (unsigned long *)&pcntxt_mask)) { | ||
596 | cpuid_count(XSTATE_CPUID, i, &eax, &ebx, &ecx, &edx); | ||
597 | xstate_size += eax; | ||
598 | } | ||
599 | } | ||
600 | } | ||
601 | |||
523 | /* | 602 | /* |
524 | * Enable and initialize the xsave feature. | 603 | * Enable and initialize the xsave feature. |
525 | */ | 604 | */ |
@@ -551,8 +630,7 @@ static void __init xstate_enable_boot_cpu(void) | |||
551 | /* | 630 | /* |
552 | * Recompute the context size for enabled features | 631 | * Recompute the context size for enabled features |
553 | */ | 632 | */ |
554 | cpuid_count(XSTATE_CPUID, 0, &eax, &ebx, &ecx, &edx); | 633 | init_xstate_size(); |
555 | xstate_size = ebx; | ||
556 | 634 | ||
557 | update_regset_xstate_info(xstate_size, pcntxt_mask); | 635 | update_regset_xstate_info(xstate_size, pcntxt_mask); |
558 | prepare_fx_sw_frame(); | 636 | prepare_fx_sw_frame(); |
@@ -572,8 +650,9 @@ static void __init xstate_enable_boot_cpu(void) | |||
572 | } | 650 | } |
573 | } | 651 | } |
574 | 652 | ||
575 | pr_info("enabled xstate_bv 0x%llx, cntxt size 0x%x\n", | 653 | pr_info("enabled xstate_bv 0x%llx, cntxt size 0x%x using %s\n", |
576 | pcntxt_mask, xstate_size); | 654 | pcntxt_mask, xstate_size, |
655 | cpu_has_xsaves ? "compacted form" : "standard form"); | ||
577 | } | 656 | } |
578 | 657 | ||
579 | /* | 658 | /* |
@@ -635,3 +714,26 @@ void eager_fpu_init(void) | |||
635 | else | 714 | else |
636 | fxrstor_checking(&init_xstate_buf->i387); | 715 | fxrstor_checking(&init_xstate_buf->i387); |
637 | } | 716 | } |
717 | |||
718 | /* | ||
719 | * Given the xsave area and a state inside, this function returns the | ||
720 | * address of the state. | ||
721 | * | ||
722 | * This is the API that is called to get xstate address in either | ||
723 | * standard format or compacted format of xsave area. | ||
724 | * | ||
725 | * Inputs: | ||
726 | * xsave: base address of the xsave area; | ||
727 | * xstate: state which is defined in xsave.h (e.g. XSTATE_FP, XSTATE_SSE, | ||
728 | * etc.) | ||
729 | * Output: | ||
730 | * address of the state in the xsave area. | ||
731 | */ | ||
732 | void *get_xsave_addr(struct xsave_struct *xsave, int xstate) | ||
733 | { | ||
734 | int feature = fls64(xstate) - 1; | ||
735 | if (!test_bit(feature, (unsigned long *)&pcntxt_mask)) | ||
736 | return NULL; | ||
737 | |||
738 | return (void *)xsave + xstate_comp_offsets[feature]; | ||
739 | } | ||