1 /*
2  * parse xen-specific informations out of elf kernel binaries.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation;
7  * version 2.1 of the License.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "libelf-private.h"
19 
20 /* ------------------------------------------------------------------------ */
21 /* xen features                                                             */
22 
23 static const char *const elf_xen_feature_names[] = {
24     [XENFEAT_writable_page_tables] = "writable_page_tables",
25     [XENFEAT_writable_descriptor_tables] = "writable_descriptor_tables",
26     [XENFEAT_auto_translated_physmap] = "auto_translated_physmap",
27     [XENFEAT_supervisor_mode_kernel] = "supervisor_mode_kernel",
28     [XENFEAT_pae_pgdir_above_4gb] = "pae_pgdir_above_4gb",
29     [XENFEAT_hvm_callback_vector] = "hvm_callback_vector",
30     [XENFEAT_dom0] = "dom0"
31 };
32 static const unsigned elf_xen_features =
33 sizeof(elf_xen_feature_names) / sizeof(elf_xen_feature_names[0]);
34 
elf_xen_parse_features(const char * features,uint32_t * supported,uint32_t * required)35 elf_errorstatus elf_xen_parse_features(const char *features,
36                            uint32_t *supported,
37                            uint32_t *required)
38 {
39     unsigned char feature[64];
40     unsigned pos, len, i;
41 
42     if ( features == NULL )
43         return 0;
44 
45     for ( pos = 0; features[pos] != '\0'; pos += len )
46     {
47         elf_memset_unchecked(feature, 0, sizeof(feature));
48         for ( len = 0;; len++ )
49         {
50             if ( len >= sizeof(feature)-1 )
51                 break;
52             if ( features[pos + len] == '\0' )
53                 break;
54             if ( features[pos + len] == '|' )
55             {
56                 len++;
57                 break;
58             }
59             feature[len] = features[pos + len];
60         }
61 
62         for ( i = 0; i < elf_xen_features; i++ )
63         {
64             if ( !elf_xen_feature_names[i] )
65                 continue;
66             if ( feature[0] == '!' )
67             {
68                 /* required */
69                 if ( !strcmp(feature + 1, elf_xen_feature_names[i]) )
70                 {
71                     elf_xen_feature_set(i, supported);
72                     if ( required )
73                         elf_xen_feature_set(i, required);
74                     break;
75                 }
76             }
77             else
78             {
79                 /* supported */
80                 if ( !strcmp(feature, elf_xen_feature_names[i]) )
81                 {
82                     elf_xen_feature_set(i, supported);
83                     break;
84                 }
85             }
86         }
87         if ( i == elf_xen_features && required && feature[0] == '!' )
88             return -1;
89     }
90 
91     return 0;
92 }
93 
94 /* ------------------------------------------------------------------------ */
95 /* xen elf notes                                                            */
96 
elf_xen_parse_note(struct elf_binary * elf,struct elf_dom_parms * parms,ELF_HANDLE_DECL (elf_note)note)97 elf_errorstatus elf_xen_parse_note(struct elf_binary *elf,
98                        struct elf_dom_parms *parms,
99                        ELF_HANDLE_DECL(elf_note) note)
100 {
101 /* *INDENT-OFF* */
102     static const struct {
103         char *name;
104         bool str;
105     } note_desc[] = {
106         [XEN_ELFNOTE_ENTRY] = { "ENTRY", 0},
107         [XEN_ELFNOTE_HYPERCALL_PAGE] = { "HYPERCALL_PAGE", 0},
108         [XEN_ELFNOTE_VIRT_BASE] = { "VIRT_BASE", 0},
109         [XEN_ELFNOTE_INIT_P2M] = { "INIT_P2M", 0},
110         [XEN_ELFNOTE_PADDR_OFFSET] = { "PADDR_OFFSET", 0},
111         [XEN_ELFNOTE_HV_START_LOW] = { "HV_START_LOW", 0},
112         [XEN_ELFNOTE_XEN_VERSION] = { "XEN_VERSION", 1},
113         [XEN_ELFNOTE_GUEST_OS] = { "GUEST_OS", 1},
114         [XEN_ELFNOTE_GUEST_VERSION] = { "GUEST_VERSION", 1},
115         [XEN_ELFNOTE_LOADER] = { "LOADER", 1},
116         [XEN_ELFNOTE_PAE_MODE] = { "PAE_MODE", 1},
117         [XEN_ELFNOTE_FEATURES] = { "FEATURES", 1},
118         [XEN_ELFNOTE_SUPPORTED_FEATURES] = { "SUPPORTED_FEATURES", 0},
119         [XEN_ELFNOTE_BSD_SYMTAB] = { "BSD_SYMTAB", 1},
120         [XEN_ELFNOTE_SUSPEND_CANCEL] = { "SUSPEND_CANCEL", 0 },
121         [XEN_ELFNOTE_MOD_START_PFN] = { "MOD_START_PFN", 0 },
122         [XEN_ELFNOTE_PHYS32_ENTRY] = { "PHYS32_ENTRY", 0 },
123     };
124 /* *INDENT-ON* */
125 
126     const char *str = NULL;
127     uint64_t val = 0;
128     unsigned int i;
129     unsigned type = elf_uval(elf, note, type);
130 
131     if ( (type >= sizeof(note_desc) / sizeof(note_desc[0])) ||
132          (note_desc[type].name == NULL) )
133     {
134         elf_msg(elf, "ELF: note: unknown (%#x)\n", type);
135         return 0;
136     }
137 
138     if ( note_desc[type].str )
139     {
140         str = elf_strval(elf, elf_note_desc(elf, note));
141         if (str == NULL)
142             /* elf_strval will mark elf broken if it fails so no need to log */
143             return 0;
144         elf_msg(elf, "ELF: note: %s = \"%s\"\n", note_desc[type].name, str);
145         parms->elf_notes[type].type = XEN_ENT_STR;
146         parms->elf_notes[type].data.str = str;
147     }
148     else
149     {
150         val = elf_note_numeric(elf, note);
151         elf_msg(elf, "ELF: note: %s = %#" PRIx64 "\n", note_desc[type].name, val);
152         parms->elf_notes[type].type = XEN_ENT_LONG;
153         parms->elf_notes[type].data.num = val;
154     }
155     parms->elf_notes[type].name = note_desc[type].name;
156 
157     switch ( type )
158     {
159     case XEN_ELFNOTE_LOADER:
160         safe_strcpy(parms->loader, str);
161         break;
162     case XEN_ELFNOTE_GUEST_OS:
163         safe_strcpy(parms->guest_os, str);
164         break;
165     case XEN_ELFNOTE_GUEST_VERSION:
166         safe_strcpy(parms->guest_ver, str);
167         break;
168     case XEN_ELFNOTE_XEN_VERSION:
169         safe_strcpy(parms->xen_ver, str);
170         break;
171     case XEN_ELFNOTE_PAE_MODE:
172         if ( !strcmp(str, "yes") )
173             parms->pae = XEN_PAE_EXTCR3;
174         if ( strstr(str, "bimodal") )
175             parms->pae = XEN_PAE_BIMODAL;
176         break;
177     case XEN_ELFNOTE_BSD_SYMTAB:
178         if ( !strcmp(str, "yes") )
179             parms->bsd_symtab = 1;
180         break;
181 
182     case XEN_ELFNOTE_VIRT_BASE:
183         parms->virt_base = val;
184         break;
185     case XEN_ELFNOTE_ENTRY:
186         parms->virt_entry = val;
187         break;
188     case XEN_ELFNOTE_INIT_P2M:
189         parms->p2m_base = val;
190         break;
191     case XEN_ELFNOTE_MOD_START_PFN:
192         parms->unmapped_initrd = !!val;
193         break;
194     case XEN_ELFNOTE_PADDR_OFFSET:
195         parms->elf_paddr_offset = val;
196         break;
197     case XEN_ELFNOTE_HYPERCALL_PAGE:
198         parms->virt_hypercall = val;
199         break;
200     case XEN_ELFNOTE_HV_START_LOW:
201         parms->virt_hv_start_low = val;
202         break;
203 
204     case XEN_ELFNOTE_FEATURES:
205         if ( elf_xen_parse_features(str, parms->f_supported,
206                                     parms->f_required) )
207             return -1;
208         break;
209 
210     case XEN_ELFNOTE_SUPPORTED_FEATURES:
211         for ( i = 0; i < XENFEAT_NR_SUBMAPS; ++i )
212             parms->f_supported[i] |= elf_note_numeric_array(
213                 elf, note, sizeof(*parms->f_supported), i);
214         break;
215 
216     case XEN_ELFNOTE_PHYS32_ENTRY:
217         parms->phys_entry = val;
218         break;
219     }
220     return 0;
221 }
222 
223 #define ELF_NOTE_INVALID (~0U)
224 
elf_xen_parse_notes(struct elf_binary * elf,struct elf_dom_parms * parms,elf_ptrval start,elf_ptrval end,unsigned * total_note_count)225 static unsigned elf_xen_parse_notes(struct elf_binary *elf,
226                                struct elf_dom_parms *parms,
227                                elf_ptrval start,
228                                elf_ptrval end,
229                                unsigned *total_note_count)
230 {
231     unsigned xen_elfnotes = 0;
232     ELF_HANDLE_DECL(elf_note) note;
233     const char *note_name;
234 
235     parms->elf_note_start = start;
236     parms->elf_note_end   = end;
237     for ( note = ELF_MAKE_HANDLE(elf_note, parms->elf_note_start);
238           ELF_HANDLE_PTRVAL(note) < parms->elf_note_end;
239           note = elf_note_next(elf, note) )
240     {
241 #ifdef __XEN__
242         process_pending_softirqs();
243 #endif
244 
245         if ( *total_note_count >= ELF_MAX_TOTAL_NOTE_COUNT )
246         {
247             elf_mark_broken(elf, "too many ELF notes");
248             break;
249         }
250         (*total_note_count)++;
251         note_name = elf_note_name(elf, note);
252         if ( note_name == NULL )
253             continue;
254         if ( strcmp(note_name, "Xen") )
255             continue;
256         if ( elf_xen_parse_note(elf, parms, note) )
257             return ELF_NOTE_INVALID;
258         xen_elfnotes++;
259     }
260     return xen_elfnotes;
261 }
262 
263 /* ------------------------------------------------------------------------ */
264 /* __xen_guest section                                                      */
265 
elf_xen_parse_guest_info(struct elf_binary * elf,struct elf_dom_parms * parms)266 elf_errorstatus elf_xen_parse_guest_info(struct elf_binary *elf,
267                              struct elf_dom_parms *parms)
268 {
269     elf_ptrval h;
270     unsigned char name[32], value[128];
271     unsigned len;
272 
273     h = parms->guest_info;
274 #define STAR(h) (elf_access_unsigned(elf, (h), 0, 1))
275     while ( STAR(h) )
276     {
277         elf_memset_unchecked(name, 0, sizeof(name));
278         elf_memset_unchecked(value, 0, sizeof(value));
279         for ( len = 0;; len++, h++ )
280         {
281             if ( len >= sizeof(name)-1 )
282                 break;
283             if ( STAR(h) == '\0' )
284                 break;
285             if ( STAR(h) == ',' )
286             {
287                 h++;
288                 break;
289             }
290             if ( STAR(h) == '=' )
291             {
292                 h++;
293                 for ( len = 0;; len++, h++ )
294                 {
295                     if ( len >= sizeof(value)-1 )
296                         break;
297                     if ( STAR(h) == '\0' )
298                         break;
299                     if ( STAR(h) == ',' )
300                     {
301                         h++;
302                         break;
303                     }
304                     value[len] = STAR(h);
305                 }
306                 break;
307             }
308             name[len] = STAR(h);
309         }
310         elf_msg(elf, "ELF: %s=\"%s\"\n", name, value);
311 
312         /* strings */
313         if ( !strcmp(name, "LOADER") )
314             safe_strcpy(parms->loader, value);
315         if ( !strcmp(name, "GUEST_OS") )
316             safe_strcpy(parms->guest_os, value);
317         if ( !strcmp(name, "GUEST_VER") )
318             safe_strcpy(parms->guest_ver, value);
319         if ( !strcmp(name, "XEN_VER") )
320             safe_strcpy(parms->xen_ver, value);
321         if ( !strcmp(name, "PAE") )
322         {
323             if ( !strcmp(value, "yes[extended-cr3]") )
324                 parms->pae = XEN_PAE_EXTCR3;
325             else if ( !strncmp(value, "yes", 3) )
326                 parms->pae = XEN_PAE_YES;
327         }
328         if ( !strcmp(name, "BSD_SYMTAB") )
329             parms->bsd_symtab = 1;
330 
331         /* longs */
332         if ( !strcmp(name, "VIRT_BASE") )
333             parms->virt_base = strtoull(value, NULL, 0);
334         if ( !strcmp(name, "VIRT_ENTRY") )
335             parms->virt_entry = strtoull(value, NULL, 0);
336         if ( !strcmp(name, "ELF_PADDR_OFFSET") )
337             parms->elf_paddr_offset = strtoull(value, NULL, 0);
338         if ( !strcmp(name, "HYPERCALL_PAGE") )
339             parms->virt_hypercall = (strtoull(value, NULL, 0) << 12) +
340                 parms->virt_base;
341 
342         /* other */
343         if ( !strcmp(name, "FEATURES") )
344             if ( elf_xen_parse_features(value, parms->f_supported,
345                                         parms->f_required) )
346                 return -1;
347     }
348     return 0;
349 }
350 
351 /* ------------------------------------------------------------------------ */
352 /* sanity checks                                                            */
353 
elf_xen_note_check(struct elf_binary * elf,struct elf_dom_parms * parms)354 static elf_errorstatus elf_xen_note_check(struct elf_binary *elf,
355                               struct elf_dom_parms *parms)
356 {
357     if ( (ELF_PTRVAL_INVALID(parms->elf_note_start)) &&
358          (ELF_PTRVAL_INVALID(parms->guest_info)) )
359     {
360         unsigned machine = elf_uval(elf, elf->ehdr, e_machine);
361         if ( (machine == EM_386) || (machine == EM_X86_64) )
362         {
363             elf_err(elf, "ERROR: Not a Xen-ELF image: "
364                     "No ELF notes or '__xen_guest' section found\n");
365             return -1;
366         }
367         return 0;
368     }
369 
370     if ( elf_uval(elf, elf->ehdr, e_machine) == EM_ARM )
371     {
372          elf_msg(elf, "ELF: Not bothering with notes on ARM\n");
373          return 0;
374     }
375 
376     /* PVH only requires one ELF note to be set */
377     if ( parms->phys_entry != UNSET_ADDR32 )
378     {
379         elf_msg(elf, "ELF: Found PVH image\n");
380         return 0;
381     }
382 
383     /* Check the contents of the Xen notes or guest string. */
384     if ( ((strlen(parms->loader) == 0) ||
385           strncmp(parms->loader, "generic", 7)) &&
386          ((strlen(parms->guest_os) == 0) ||
387           strncmp(parms->guest_os, "linux", 5)) )
388     {
389         elf_err(elf,
390                 "ERROR: Will only load images built for the generic loader or Linux images"
391                 " (Not '%.*s' and '%.*s') or with PHYS32_ENTRY set\n",
392                 (int)sizeof(parms->loader), parms->loader,
393                 (int)sizeof(parms->guest_os), parms->guest_os);
394         return -1;
395     }
396 
397     if ( (strlen(parms->xen_ver) == 0) ||
398          strncmp(parms->xen_ver, "xen-3.0", 7) )
399     {
400         elf_err(elf, "ERROR: Xen will only load images built for Xen v3.0 "
401                 "(Not '%.*s')\n",
402                 (int)sizeof(parms->xen_ver), parms->xen_ver);
403         return -1;
404     }
405     return 0;
406 }
407 
elf_xen_addr_calc_check(struct elf_binary * elf,struct elf_dom_parms * parms)408 static elf_errorstatus elf_xen_addr_calc_check(struct elf_binary *elf,
409                                    struct elf_dom_parms *parms)
410 {
411     uint64_t virt_offset;
412 
413     if ( (parms->elf_paddr_offset != UNSET_ADDR) &&
414          (parms->virt_base == UNSET_ADDR) )
415     {
416         elf_err(elf, "ERROR: ELF_PADDR_OFFSET set, VIRT_BASE unset\n");
417         return -1;
418     }
419 
420     /* Initial guess for virt_base is 0 if it is not explicitly defined. */
421     if ( parms->virt_base == UNSET_ADDR )
422     {
423         parms->virt_base = 0;
424         elf_msg(elf, "ELF: VIRT_BASE unset, using %#" PRIx64 "\n",
425                 parms->virt_base);
426     }
427 
428     /*
429      * If we are using the legacy __xen_guest section then elf_pa_off
430      * defaults to v_start in order to maintain compatibility with
431      * older hypervisors which set padd in the ELF header to
432      * virt_base.
433      *
434      * If we are using the modern ELF notes interface then the default
435      * is 0.
436      */
437     if ( parms->elf_paddr_offset == UNSET_ADDR )
438     {
439         if ( parms->elf_note_start )
440             parms->elf_paddr_offset = 0;
441         else
442             parms->elf_paddr_offset = parms->virt_base;
443         elf_msg(elf, "ELF_PADDR_OFFSET unset, using %#" PRIx64 "\n",
444                 parms->elf_paddr_offset);
445     }
446 
447     virt_offset = parms->virt_base - parms->elf_paddr_offset;
448     parms->virt_kstart = elf->pstart + virt_offset;
449     parms->virt_kend   = elf->pend   + virt_offset;
450 
451     if ( parms->virt_entry == UNSET_ADDR )
452         parms->virt_entry = elf_uval(elf, elf->ehdr, e_entry);
453 
454     if ( parms->bsd_symtab )
455     {
456         elf_parse_bsdsyms(elf, elf->pend);
457         if ( elf->bsd_symtab_pend )
458             parms->virt_kend = elf->bsd_symtab_pend + virt_offset;
459     }
460 
461     elf_msg(elf, "ELF: addresses:\n");
462     elf_msg(elf, "    virt_base        = 0x%" PRIx64 "\n", parms->virt_base);
463     elf_msg(elf, "    elf_paddr_offset = 0x%" PRIx64 "\n", parms->elf_paddr_offset);
464     elf_msg(elf, "    virt_offset      = 0x%" PRIx64 "\n", virt_offset);
465     elf_msg(elf, "    virt_kstart      = 0x%" PRIx64 "\n", parms->virt_kstart);
466     elf_msg(elf, "    virt_kend        = 0x%" PRIx64 "\n", parms->virt_kend);
467     elf_msg(elf, "    virt_entry       = 0x%" PRIx64 "\n", parms->virt_entry);
468     elf_msg(elf, "    p2m_base         = 0x%" PRIx64 "\n", parms->p2m_base);
469 
470     if ( (parms->virt_kstart > parms->virt_kend) ||
471          (parms->virt_entry < parms->virt_kstart) ||
472          (parms->virt_entry > parms->virt_kend) ||
473          (parms->virt_base > parms->virt_kstart) )
474     {
475         elf_err(elf, "ERROR: ELF start or entries are out of bounds\n");
476         return -1;
477     }
478 
479     if ( (parms->p2m_base != UNSET_ADDR) &&
480          (parms->p2m_base >= parms->virt_kstart) &&
481          (parms->p2m_base < parms->virt_kend) )
482     {
483         elf_err(elf, "ERROR: P->M table base is out of bounds\n");
484         return -1;
485     }
486 
487     return 0;
488 }
489 
490 /* ------------------------------------------------------------------------ */
491 /* glue it all together ...                                                 */
492 
elf_xen_parse(struct elf_binary * elf,struct elf_dom_parms * parms)493 elf_errorstatus elf_xen_parse(struct elf_binary *elf,
494                   struct elf_dom_parms *parms)
495 {
496     ELF_HANDLE_DECL(elf_shdr) shdr;
497     ELF_HANDLE_DECL(elf_phdr) phdr;
498     unsigned xen_elfnotes = 0;
499     unsigned i, count, more_notes;
500     unsigned total_note_count = 0;
501 
502     elf_memset_unchecked(parms, 0, sizeof(*parms));
503     parms->virt_base = UNSET_ADDR;
504     parms->virt_entry = UNSET_ADDR;
505     parms->virt_hypercall = UNSET_ADDR;
506     parms->virt_hv_start_low = UNSET_ADDR;
507     parms->p2m_base = UNSET_ADDR;
508     parms->elf_paddr_offset = UNSET_ADDR;
509     parms->phys_entry = UNSET_ADDR32;
510 
511     /* Find and parse elf notes. */
512     count = elf_phdr_count(elf);
513     for ( i = 0; i < count; i++ )
514     {
515         phdr = elf_phdr_by_index(elf, i);
516         if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) )
517             /* input has an insane program header count field */
518             break;
519         if ( elf_uval(elf, phdr, p_type) != PT_NOTE )
520             continue;
521 
522         /*
523          * Some versions of binutils do not correctly set p_offset for
524          * note segments.
525          */
526         if (elf_uval(elf, phdr, p_offset) == 0)
527              continue;
528 
529         more_notes = elf_xen_parse_notes(elf, parms,
530                                  elf_segment_start(elf, phdr),
531                                  elf_segment_end(elf, phdr),
532                                  &total_note_count);
533         if ( more_notes == ELF_NOTE_INVALID )
534             return -1;
535 
536         xen_elfnotes += more_notes;
537     }
538 
539     /*
540      * Fall back to any SHT_NOTE sections if no valid note segments
541      * were found.
542      */
543     if ( xen_elfnotes == 0 )
544     {
545         count = elf_shdr_count(elf);
546         for ( i = 1; i < count; i++ )
547         {
548             shdr = elf_shdr_by_index(elf, i);
549             if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) )
550                 /* input has an insane section header count field */
551                 break;
552 
553             if ( elf_uval(elf, shdr, sh_type) != SHT_NOTE )
554                 continue;
555 
556             more_notes = elf_xen_parse_notes(elf, parms,
557                                      elf_section_start(elf, shdr),
558                                      elf_section_end(elf, shdr),
559                                      &total_note_count);
560 
561             if ( more_notes == ELF_NOTE_INVALID )
562                 return -1;
563 
564             if ( xen_elfnotes == 0 && more_notes > 0 )
565                 elf_msg(elf, "ELF: using notes from SHT_NOTE section\n");
566 
567             xen_elfnotes += more_notes;
568         }
569 
570     }
571 
572     /*
573      * Finally fall back to the __xen_guest section.
574      */
575     if ( xen_elfnotes == 0 )
576     {
577         shdr = elf_shdr_by_name(elf, "__xen_guest");
578         if ( ELF_HANDLE_VALID(shdr) )
579         {
580             parms->guest_info = elf_section_start(elf, shdr);
581             parms->elf_note_start = ELF_INVALID_PTRVAL;
582             parms->elf_note_end   = ELF_INVALID_PTRVAL;
583             elf_msg(elf, "ELF: __xen_guest: \"%s\"\n",
584                     elf_strfmt(elf, parms->guest_info));
585             elf_xen_parse_guest_info(elf, parms);
586         }
587     }
588 
589     if ( elf_xen_note_check(elf, parms) != 0 )
590         return -1;
591     if ( elf_xen_addr_calc_check(elf, parms) != 0 )
592         return -1;
593     return 0;
594 }
595 
596 /*
597  * Local variables:
598  * mode: C
599  * c-file-style: "BSD"
600  * c-basic-offset: 4
601  * tab-width: 4
602  * indent-tabs-mode: nil
603  * End:
604  */
605