summaryrefslogtreecommitdiff
path: root/xen/lib/x86/msr.c
blob: e04b9ca0130228f275943a1a8c0888d3fc949076 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "private.h"

#include <xen/lib/x86/cpu-policy.h>

/*
 * Copy a single MSR into the provided msr_entry_buffer_t buffer, performing a
 * boundary check against the buffer size.
 */
static int copy_msr_to_buffer(uint32_t idx, uint64_t val,
                              msr_entry_buffer_t msrs,
                              uint32_t *curr_entry, const uint32_t nr_entries)
{
    const xen_msr_entry_t ent = { .idx = idx, .val = val };

    if ( *curr_entry == nr_entries )
        return -ENOBUFS;

    if ( copy_to_buffer_offset(msrs, *curr_entry, &ent, 1) )
        return -EFAULT;

    ++*curr_entry;

    return 0;
}

int x86_msr_copy_to_buffer(const struct cpu_policy *p,
                           msr_entry_buffer_t msrs, uint32_t *nr_entries_p)
{
    const uint32_t nr_entries = *nr_entries_p;
    uint32_t curr_entry = 0;

#define COPY_MSR(idx, val)                                      \
    ({                                                          \
        int ret;                                                \
                                                                \
        if ( (ret = copy_msr_to_buffer(                         \
                  idx, val, msrs, &curr_entry, nr_entries)) )   \
            return ret;                                         \
    })

    COPY_MSR(MSR_INTEL_PLATFORM_INFO, p->platform_info.raw);
    COPY_MSR(MSR_ARCH_CAPABILITIES,   p->arch_caps.raw);

#undef COPY_MSR

    *nr_entries_p = curr_entry;

    return 0;
}

int x86_msr_copy_from_buffer(struct cpu_policy *p,
                             const msr_entry_buffer_t msrs, uint32_t nr_entries,
                             uint32_t *err_msr)
{
    unsigned int i;
    xen_msr_entry_t data;
    int rc;

    if ( err_msr )
        *err_msr = -1;

    /*
     * A well formed caller is expected to pass an array with entries in
     * order, and without any repetitions.  However, due to per-vendor
     * differences, and in the case of upgrade or levelled scenarios, we
     * typically expect fewer than MAX entries to be passed.
     *
     * Detecting repeated entries is prohibitively complicated, so we don't
     * bother.  That said, one way or another if more than MAX entries are
     * passed, something is wrong.
     */
    if ( nr_entries > MSR_MAX_SERIALISED_ENTRIES )
        return -E2BIG;

    for ( i = 0; i < nr_entries; i++ )
    {
        if ( copy_from_buffer_offset(&data, msrs, i, 1) )
            return -EFAULT;

        if ( data.flags ) /* .flags MBZ */
        {
            rc = -EINVAL;
            goto err;
        }

        switch ( data.idx )
        {
            /*
             * Assign data.val to p->field, checking for truncation if the
             * backing storage for field is smaller than uint64_t
             */
#define ASSIGN(field)                             \
({                                                \
    if ( (typeof(p->field))data.val != data.val ) \
    {                                             \
        rc = -EOVERFLOW;                          \
        goto err;                                 \
    }                                             \
    p->field = data.val;                          \
})

        case MSR_INTEL_PLATFORM_INFO: ASSIGN(platform_info.raw); break;
        case MSR_ARCH_CAPABILITIES:   ASSIGN(arch_caps.raw);     break;

#undef ASSIGN

        default:
            rc = -ERANGE;
            goto err;
        }
    }

    return 0;

 err:
    if ( err_msr )
        *err_msr = data.idx;

    return rc;
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */