1 /******************************************************************************
2 * arch/x86/pv/misc-hypercalls.c
3 *
4 * Misc hypercall handlers
5 *
6 * Modifications to Linux original are copyright (c) 2002-2004, K A Fraser
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include <xen/hypercall.h>
23
24 #include <asm/debugreg.h>
25
do_set_debugreg(int reg,unsigned long value)26 long do_set_debugreg(int reg, unsigned long value)
27 {
28 return set_debugreg(current, reg, value);
29 }
30
do_get_debugreg(int reg)31 unsigned long do_get_debugreg(int reg)
32 {
33 unsigned long val;
34 int res = x86emul_read_dr(reg, &val, NULL);
35
36 return res == X86EMUL_OKAY ? val : -ENODEV;
37 }
38
do_fpu_taskswitch(int set)39 long do_fpu_taskswitch(int set)
40 {
41 struct vcpu *v = current;
42
43 if ( set )
44 {
45 v->arch.pv.ctrlreg[0] |= X86_CR0_TS;
46 stts();
47 }
48 else
49 {
50 v->arch.pv.ctrlreg[0] &= ~X86_CR0_TS;
51 if ( v->fpu_dirtied )
52 clts();
53 }
54
55 return 0;
56 }
57
58 /*
59 * Used by hypercalls and the emulator.
60 * -ENODEV => #UD
61 * -EINVAL => #GP Invalid bit
62 * -EPERM => #GP Valid bit, but not permitted to use
63 */
set_debugreg(struct vcpu * v,unsigned int reg,unsigned long value)64 long set_debugreg(struct vcpu *v, unsigned int reg, unsigned long value)
65 {
66 struct vcpu *curr = current;
67
68 switch ( reg )
69 {
70 case 0 ... 3:
71 if ( !access_ok(value, sizeof(long)) )
72 return -EPERM;
73
74 v->arch.dr[reg] = value;
75 if ( v == curr )
76 {
77 switch ( reg )
78 {
79 case 0: write_debugreg(0, value); break;
80 case 1: write_debugreg(1, value); break;
81 case 2: write_debugreg(2, value); break;
82 case 3: write_debugreg(3, value); break;
83 }
84 }
85 break;
86
87 case 4:
88 if ( v->arch.pv.ctrlreg[4] & X86_CR4_DE )
89 return -ENODEV;
90
91 /* Fallthrough */
92 case 6:
93 /* The upper 32 bits are strictly reserved. */
94 if ( value != (uint32_t)value )
95 return -EINVAL;
96
97 /*
98 * DR6: Bits 4-11,16-31 reserved (set to 1).
99 * Bit 12 reserved (set to 0).
100 */
101 value &= ~DR_STATUS_RESERVED_ZERO; /* reserved bits => 0 */
102 value |= DR_STATUS_RESERVED_ONE; /* reserved bits => 1 */
103
104 v->arch.dr6 = value;
105 if ( v == curr )
106 write_debugreg(6, value);
107 break;
108
109 case 5:
110 if ( v->arch.pv.ctrlreg[4] & X86_CR4_DE )
111 return -ENODEV;
112
113 /* Fallthrough */
114 case 7:
115 /* The upper 32 bits are strictly reserved. */
116 if ( value != (uint32_t)value )
117 return -EINVAL;
118
119 /*
120 * DR7: Bit 10 reserved (set to 1).
121 * Bits 11-12,14-15 reserved (set to 0).
122 */
123 value &= ~DR_CONTROL_RESERVED_ZERO; /* reserved bits => 0 */
124 value |= DR_CONTROL_RESERVED_ONE; /* reserved bits => 1 */
125 /*
126 * Privileged bits:
127 * GD (bit 13): must be 0.
128 */
129 if ( value & DR_GENERAL_DETECT )
130 return -EPERM;
131
132 /* DR7.{G,L}E = 0 => debugging disabled for this domain. */
133 if ( value & DR7_ACTIVE_MASK )
134 {
135 unsigned int i, io_enable = 0;
136
137 for ( i = DR_CONTROL_SHIFT; i < 32; i += DR_CONTROL_SIZE )
138 {
139 if ( ((value >> i) & 3) == DR_IO )
140 {
141 if ( !(v->arch.pv.ctrlreg[4] & X86_CR4_DE) )
142 return -EPERM;
143 io_enable |= value & (3 << ((i - 16) >> 1));
144 }
145 }
146
147 v->arch.pv.dr7_emul = io_enable;
148 value &= ~io_enable;
149
150 /*
151 * If DR7 was previously clear then we need to load all other
152 * debug registers at this point as they were not restored during
153 * context switch. Updating DR7 itself happens later.
154 */
155 if ( (v == curr) && !(v->arch.dr7 & DR7_ACTIVE_MASK) )
156 activate_debugregs(v);
157 }
158 else
159 /* Zero the emulated controls if %dr7 isn't active. */
160 v->arch.pv.dr7_emul = 0;
161
162 v->arch.dr7 = value;
163 if ( v == curr )
164 write_debugreg(7, value);
165 break;
166
167 default:
168 return -ENODEV;
169 }
170
171 return 0;
172 }
173
174 /*
175 * Local variables:
176 * mode: C
177 * c-file-style: "BSD"
178 * c-basic-offset: 4
179 * tab-width: 4
180 * indent-tabs-mode: nil
181 * End:
182 */
183