1 /*
2 * Copyright (C) 2016 Citrix Systems R&D Ltd.
3 */
4
5 #include <xen/errno.h>
6 #include <xen/init.h>
7 #include <xen/lib.h>
8 #include <xen/mm.h>
9 #include <xen/pfn.h>
10 #include <xen/vmap.h>
11 #include <xen/livepatch_elf.h>
12 #include <xen/livepatch.h>
13
14 #include <asm/nmi.h>
15 #include <asm/livepatch.h>
16
arch_livepatch_quiesce(void)17 int arch_livepatch_quiesce(void)
18 {
19 /* Disable WP to allow changes to read-only pages. */
20 write_cr0(read_cr0() & ~X86_CR0_WP);
21
22 return 0;
23 }
24
arch_livepatch_revive(void)25 void arch_livepatch_revive(void)
26 {
27 /* Reinstate WP. */
28 write_cr0(read_cr0() | X86_CR0_WP);
29 }
30
arch_livepatch_verify_func(const struct livepatch_func * func)31 int arch_livepatch_verify_func(const struct livepatch_func *func)
32 {
33 /* If NOPing.. */
34 if ( !func->new_addr )
35 {
36 /* Only do up to maximum amount we can put in the ->opaque. */
37 if ( func->new_size > sizeof(func->opaque) )
38 return -EOPNOTSUPP;
39
40 if ( func->old_size < func->new_size )
41 return -EINVAL;
42 }
43 else if ( func->old_size < ARCH_PATCH_INSN_SIZE )
44 return -EINVAL;
45
46 return 0;
47 }
48
49 /*
50 * "noinline" to cause control flow change and thus invalidate I$ and
51 * cause refetch after modification.
52 */
arch_livepatch_apply(struct livepatch_func * func)53 void noinline arch_livepatch_apply(struct livepatch_func *func)
54 {
55 uint8_t *old_ptr;
56 uint8_t insn[sizeof(func->opaque)];
57 unsigned int len;
58
59 old_ptr = func->old_addr;
60 len = livepatch_insn_len(func);
61 if ( !len )
62 return;
63
64 memcpy(func->opaque, old_ptr, len);
65 if ( func->new_addr )
66 {
67 int32_t val;
68
69 BUILD_BUG_ON(ARCH_PATCH_INSN_SIZE != (1 + sizeof(val)));
70
71 insn[0] = 0xe9; /* Relative jump. */
72 val = func->new_addr - func->old_addr - ARCH_PATCH_INSN_SIZE;
73
74 memcpy(&insn[1], &val, sizeof(val));
75 }
76 else
77 add_nops(insn, len);
78
79 memcpy(old_ptr, insn, len);
80 }
81
82 /*
83 * "noinline" to cause control flow change and thus invalidate I$ and
84 * cause refetch after modification.
85 */
arch_livepatch_revert(const struct livepatch_func * func)86 void noinline arch_livepatch_revert(const struct livepatch_func *func)
87 {
88 memcpy(func->old_addr, func->opaque, livepatch_insn_len(func));
89 }
90
91 /*
92 * "noinline" to cause control flow change and thus invalidate I$ and
93 * cause refetch after modification.
94 */
arch_livepatch_post_action(void)95 void noinline arch_livepatch_post_action(void)
96 {
97 }
98
99 static nmi_callback_t *saved_nmi_callback;
100 /*
101 * Note that because of this NOP code the do_nmi is not safely patchable.
102 * Also if we do receive 'real' NMIs we have lost them.
103 */
mask_nmi_callback(const struct cpu_user_regs * regs,int cpu)104 static int mask_nmi_callback(const struct cpu_user_regs *regs, int cpu)
105 {
106 /* TODO: Handle missing NMI/MCE.*/
107 return 1;
108 }
109
arch_livepatch_mask(void)110 void arch_livepatch_mask(void)
111 {
112 saved_nmi_callback = set_nmi_callback(mask_nmi_callback);
113 }
114
arch_livepatch_unmask(void)115 void arch_livepatch_unmask(void)
116 {
117 set_nmi_callback(saved_nmi_callback);
118 }
119
arch_livepatch_verify_elf(const struct livepatch_elf * elf)120 int arch_livepatch_verify_elf(const struct livepatch_elf *elf)
121 {
122
123 const Elf_Ehdr *hdr = elf->hdr;
124
125 if ( hdr->e_machine != EM_X86_64 ||
126 hdr->e_ident[EI_CLASS] != ELFCLASS64 ||
127 hdr->e_ident[EI_DATA] != ELFDATA2LSB )
128 {
129 dprintk(XENLOG_ERR, LIVEPATCH "%s: Unsupported ELF Machine type!\n",
130 elf->name);
131 return -EOPNOTSUPP;
132 }
133
134 return 0;
135 }
136
arch_livepatch_symbol_ok(const struct livepatch_elf * elf,const struct livepatch_elf_sym * sym)137 bool arch_livepatch_symbol_ok(const struct livepatch_elf *elf,
138 const struct livepatch_elf_sym *sym)
139 {
140 /* No special checks on x86. */
141 return true;
142 }
143
arch_livepatch_symbol_deny(const struct livepatch_elf * elf,const struct livepatch_elf_sym * sym)144 bool arch_livepatch_symbol_deny(const struct livepatch_elf *elf,
145 const struct livepatch_elf_sym *sym)
146 {
147 /* No special checks on x86. */
148 return false;
149 }
150
arch_livepatch_perform_rel(struct livepatch_elf * elf,const struct livepatch_elf_sec * base,const struct livepatch_elf_sec * rela)151 int arch_livepatch_perform_rel(struct livepatch_elf *elf,
152 const struct livepatch_elf_sec *base,
153 const struct livepatch_elf_sec *rela)
154 {
155 dprintk(XENLOG_ERR, LIVEPATCH "%s: SHT_REL relocation unsupported\n",
156 elf->name);
157 return -EOPNOTSUPP;
158 }
159
arch_livepatch_perform_rela(struct livepatch_elf * elf,const struct livepatch_elf_sec * base,const struct livepatch_elf_sec * rela)160 int arch_livepatch_perform_rela(struct livepatch_elf *elf,
161 const struct livepatch_elf_sec *base,
162 const struct livepatch_elf_sec *rela)
163 {
164 unsigned int i;
165
166 for ( i = 0; i < (rela->sec->sh_size / rela->sec->sh_entsize); i++ )
167 {
168 const Elf_RelA *r = rela->data + i * rela->sec->sh_entsize;
169 unsigned int symndx = ELF64_R_SYM(r->r_info);
170 uint8_t *dest = base->load_addr + r->r_offset;
171 uint64_t val;
172
173 if ( symndx == STN_UNDEF )
174 {
175 dprintk(XENLOG_ERR, LIVEPATCH "%s: Encountered STN_UNDEF\n",
176 elf->name);
177 return -EOPNOTSUPP;
178 }
179 else if ( symndx >= elf->nsym )
180 {
181 dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation wants symbol@%u which is past end!\n",
182 elf->name, symndx);
183 return -EINVAL;
184 }
185 else if ( !elf->sym[symndx].sym )
186 {
187 dprintk(XENLOG_ERR, LIVEPATCH "%s: No symbol@%u\n",
188 elf->name, symndx);
189 return -EINVAL;
190 }
191
192 val = r->r_addend + elf->sym[symndx].sym->st_value;
193
194 switch ( ELF64_R_TYPE(r->r_info) )
195 {
196 case R_X86_64_NONE:
197 break;
198
199 case R_X86_64_64:
200 if ( r->r_offset >= base->sec->sh_size ||
201 (r->r_offset + sizeof(uint64_t)) > base->sec->sh_size )
202 goto bad_offset;
203
204 *(uint64_t *)dest = val;
205 break;
206
207 case R_X86_64_PLT32:
208 /*
209 * Xen uses -fpic which normally uses PLT relocations
210 * except that it sets visibility to hidden which means
211 * that they are not used. However, when gcc cannot
212 * inline memcpy it emits memcpy with default visibility
213 * which then creates a PLT relocation. It can just be
214 * treated the same as R_X86_64_PC32.
215 */
216 case R_X86_64_PC32:
217 if ( r->r_offset >= base->sec->sh_size ||
218 (r->r_offset + sizeof(uint32_t)) > base->sec->sh_size )
219 goto bad_offset;
220
221 val -= (uint64_t)dest;
222 *(int32_t *)dest = val;
223 if ( (int64_t)val != *(int32_t *)dest )
224 {
225 dprintk(XENLOG_ERR, LIVEPATCH "%s: Overflow in relocation %u in %s for %s!\n",
226 elf->name, i, rela->name, base->name);
227 return -EOVERFLOW;
228 }
229 break;
230
231 default:
232 dprintk(XENLOG_ERR, LIVEPATCH "%s: Unhandled relocation %lu\n",
233 elf->name, ELF64_R_TYPE(r->r_info));
234 return -EOPNOTSUPP;
235 }
236 }
237
238 return 0;
239
240 bad_offset:
241 dprintk(XENLOG_ERR, LIVEPATCH "%s: Relative relocation offset is past %s section!\n",
242 elf->name, base->name);
243 return -EINVAL;
244 }
245
246 /*
247 * Once the resolving symbols, performing relocations, etc is complete
248 * we secure the memory by putting in the proper page table attributes
249 * for the desired type.
250 */
arch_livepatch_secure(const void * va,unsigned int pages,enum va_type type)251 int arch_livepatch_secure(const void *va, unsigned int pages, enum va_type type)
252 {
253 unsigned long start = (unsigned long)va;
254 unsigned int flag;
255
256 ASSERT(va);
257 ASSERT(pages);
258
259 if ( type == LIVEPATCH_VA_RX )
260 flag = PAGE_HYPERVISOR_RX;
261 else if ( type == LIVEPATCH_VA_RW )
262 flag = PAGE_HYPERVISOR_RW;
263 else
264 flag = PAGE_HYPERVISOR_RO;
265
266 return modify_xen_mappings(start, start + pages * PAGE_SIZE, flag);
267 }
268
arch_livepatch_init(void)269 void __init arch_livepatch_init(void)
270 {
271 void *start, *end;
272
273 start = (void *)xen_virt_end;
274 end = (void *)(XEN_VIRT_END - NR_CPUS * PAGE_SIZE);
275
276 BUG_ON(end <= start);
277
278 vm_init_type(VMAP_XEN, start, end);
279 }
280 /*
281 * Local variables:
282 * mode: C
283 * c-file-style: "BSD"
284 * c-basic-offset: 4
285 * tab-width: 4
286 * indent-tabs-mode: nil
287 * End:
288 */
289