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