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     elf_errorstatus ret = 0;
273 
274     h = parms->guest_info;
275 #define STAR(h) (elf_access_unsigned(elf, (h), 0, 1))
276     while ( STAR(h) )
277     {
278         elf_memset_unchecked(name, 0, sizeof(name));
279         elf_memset_unchecked(value, 0, sizeof(value));
280         for ( len = 0;; len++, h++ )
281         {
282             if ( len >= sizeof(name)-1 )
283                 break;
284             if ( STAR(h) == '\0' )
285                 break;
286             if ( STAR(h) == ',' )
287             {
288                 h++;
289                 break;
290             }
291             if ( STAR(h) == '=' )
292             {
293                 h++;
294                 for ( len = 0;; len++, h++ )
295                 {
296                     if ( len >= sizeof(value)-1 )
297                         break;
298                     if ( STAR(h) == '\0' )
299                         break;
300                     if ( STAR(h) == ',' )
301                     {
302                         h++;
303                         break;
304                     }
305                     value[len] = STAR(h);
306                 }
307                 break;
308             }
309             name[len] = STAR(h);
310         }
311         elf_msg(elf, "ELF: %s=\"%s\"\n", name, value);
312 
313         /* strings */
314         if ( !strcmp(name, "LOADER") )
315             safe_strcpy(parms->loader, value);
316         if ( !strcmp(name, "GUEST_OS") )
317             safe_strcpy(parms->guest_os, value);
318         if ( !strcmp(name, "GUEST_VER") )
319             safe_strcpy(parms->guest_ver, value);
320         if ( !strcmp(name, "XEN_VER") )
321             safe_strcpy(parms->xen_ver, value);
322         if ( !strcmp(name, "PAE") )
323         {
324             if ( !strcmp(value, "yes[extended-cr3]") )
325                 parms->pae = XEN_PAE_EXTCR3;
326             else if ( !strncmp(value, "yes", 3) )
327                 parms->pae = XEN_PAE_YES;
328         }
329         if ( !strcmp(name, "BSD_SYMTAB") )
330             parms->bsd_symtab = 1;
331 
332         /* longs */
333         if ( !strcmp(name, "VIRT_BASE") )
334             parms->virt_base = strtoull(value, NULL, 0);
335         if ( !strcmp(name, "VIRT_ENTRY") )
336             parms->virt_entry = strtoull(value, NULL, 0);
337         if ( !strcmp(name, "ELF_PADDR_OFFSET") )
338             parms->elf_paddr_offset = strtoull(value, NULL, 0);
339         if ( !strcmp(name, "HYPERCALL_PAGE") )
340             parms->virt_hypercall = strtoull(value, NULL, 0) << 12;
341 
342         /* other */
343         if ( !strcmp(name, "FEATURES") )
344             if ( elf_xen_parse_features(value, parms->f_supported,
345                                         parms->f_required) )
346             {
347                 ret = -1;
348                 break;
349             }
350     }
351 
352     if ( (parms->virt_base != UNSET_ADDR) &&
353          (parms->virt_hypercall != UNSET_ADDR) )
354         parms->virt_hypercall += parms->virt_base;
355 
356     return ret;
357 }
358 
359 /* ------------------------------------------------------------------------ */
360 /* sanity checks                                                            */
361 
elf_xen_note_check(struct elf_binary * elf,struct elf_dom_parms * parms)362 static elf_errorstatus elf_xen_note_check(struct elf_binary *elf,
363                               struct elf_dom_parms *parms)
364 {
365     if ( (ELF_PTRVAL_INVALID(parms->elf_note_start)) &&
366          (ELF_PTRVAL_INVALID(parms->guest_info)) )
367     {
368         unsigned machine = elf_uval(elf, elf->ehdr, e_machine);
369         if ( (machine == EM_386) || (machine == EM_X86_64) )
370         {
371             elf_err(elf, "ERROR: Not a Xen-ELF image: "
372                     "No ELF notes or '__xen_guest' section found\n");
373             return -1;
374         }
375         return 0;
376     }
377 
378     if ( elf_uval(elf, elf->ehdr, e_machine) == EM_ARM )
379     {
380          elf_msg(elf, "ELF: Not bothering with notes on ARM\n");
381          return 0;
382     }
383 
384     /* PVH only requires one ELF note to be set */
385     if ( parms->phys_entry != UNSET_ADDR32 )
386     {
387         elf_msg(elf, "ELF: Found PVH image\n");
388         return 0;
389     }
390 
391     /* Check the contents of the Xen notes or guest string. */
392     if ( ((strlen(parms->loader) == 0) ||
393           strncmp(parms->loader, "generic", 7)) &&
394          ((strlen(parms->guest_os) == 0) ||
395           strncmp(parms->guest_os, "linux", 5)) )
396     {
397         elf_err(elf,
398                 "ERROR: Will only load images built for the generic loader or Linux images"
399                 " (Not '%.*s' and '%.*s') or with PHYS32_ENTRY set\n",
400                 (int)sizeof(parms->loader), parms->loader,
401                 (int)sizeof(parms->guest_os), parms->guest_os);
402         return -1;
403     }
404 
405     if ( (strlen(parms->xen_ver) == 0) ||
406          strncmp(parms->xen_ver, "xen-3.0", 7) )
407     {
408         elf_err(elf, "ERROR: Xen will only load images built for Xen v3.0 "
409                 "(Not '%.*s')\n",
410                 (int)sizeof(parms->xen_ver), parms->xen_ver);
411         return -1;
412     }
413     return 0;
414 }
415 
elf_xen_addr_calc_check(struct elf_binary * elf,struct elf_dom_parms * parms)416 static elf_errorstatus elf_xen_addr_calc_check(struct elf_binary *elf,
417                                    struct elf_dom_parms *parms)
418 {
419     uint64_t virt_offset;
420 
421     if ( (parms->elf_paddr_offset != UNSET_ADDR) &&
422          (parms->virt_base == UNSET_ADDR) )
423     {
424         elf_err(elf, "ERROR: ELF_PADDR_OFFSET set, VIRT_BASE unset\n");
425         return -1;
426     }
427 
428     /* Initial guess for virt_base is 0 if it is not explicitly defined. */
429     if ( parms->virt_base == UNSET_ADDR )
430     {
431         parms->virt_base = 0;
432         elf_msg(elf, "ELF: VIRT_BASE unset, using %#" PRIx64 "\n",
433                 parms->virt_base);
434     }
435 
436     /*
437      * If we are using the legacy __xen_guest section then elf_pa_off
438      * defaults to v_start in order to maintain compatibility with
439      * older hypervisors which set padd in the ELF header to
440      * virt_base.
441      *
442      * If we are using the modern ELF notes interface then the default
443      * is 0.
444      */
445     if ( parms->elf_paddr_offset == UNSET_ADDR )
446     {
447         if ( parms->elf_note_start )
448             parms->elf_paddr_offset = 0;
449         else
450             parms->elf_paddr_offset = parms->virt_base;
451         elf_msg(elf, "ELF_PADDR_OFFSET unset, using %#" PRIx64 "\n",
452                 parms->elf_paddr_offset);
453     }
454 
455     virt_offset = parms->virt_base - parms->elf_paddr_offset;
456     parms->virt_kstart = elf->pstart + virt_offset;
457     parms->virt_kend   = elf->pend   + virt_offset;
458 
459     if ( parms->virt_entry == UNSET_ADDR )
460         parms->virt_entry = elf_uval(elf, elf->ehdr, e_entry);
461 
462     if ( parms->bsd_symtab )
463     {
464         elf_parse_bsdsyms(elf, elf->pend);
465         if ( elf->bsd_symtab_pend )
466             parms->virt_kend = elf->bsd_symtab_pend + virt_offset;
467     }
468 
469     elf_msg(elf, "ELF: addresses:\n");
470     elf_msg(elf, "    virt_base        = 0x%" PRIx64 "\n", parms->virt_base);
471     elf_msg(elf, "    elf_paddr_offset = 0x%" PRIx64 "\n", parms->elf_paddr_offset);
472     elf_msg(elf, "    virt_offset      = 0x%" PRIx64 "\n", virt_offset);
473     elf_msg(elf, "    virt_kstart      = 0x%" PRIx64 "\n", parms->virt_kstart);
474     elf_msg(elf, "    virt_kend        = 0x%" PRIx64 "\n", parms->virt_kend);
475     elf_msg(elf, "    virt_entry       = 0x%" PRIx64 "\n", parms->virt_entry);
476     elf_msg(elf, "    p2m_base         = 0x%" PRIx64 "\n", parms->p2m_base);
477 
478     if ( (parms->virt_kstart > parms->virt_kend) ||
479          (parms->virt_entry < parms->virt_kstart) ||
480          (parms->virt_entry > parms->virt_kend) ||
481          (parms->virt_base > parms->virt_kstart) )
482     {
483         elf_err(elf, "ERROR: ELF start or entries are out of bounds\n");
484         return -1;
485     }
486 
487     if ( (parms->p2m_base != UNSET_ADDR) &&
488          (parms->p2m_base >= parms->virt_kstart) &&
489          (parms->p2m_base < parms->virt_kend) )
490     {
491         elf_err(elf, "ERROR: P->M table base is out of bounds\n");
492         return -1;
493     }
494 
495     return 0;
496 }
497 
498 /* ------------------------------------------------------------------------ */
499 /* glue it all together ...                                                 */
500 
elf_xen_parse(struct elf_binary * elf,struct elf_dom_parms * parms)501 elf_errorstatus elf_xen_parse(struct elf_binary *elf,
502                   struct elf_dom_parms *parms)
503 {
504     ELF_HANDLE_DECL(elf_shdr) shdr;
505     ELF_HANDLE_DECL(elf_phdr) phdr;
506     unsigned xen_elfnotes = 0;
507     unsigned i, count, more_notes;
508     unsigned total_note_count = 0;
509 
510     elf_memset_unchecked(parms, 0, sizeof(*parms));
511     parms->virt_base = UNSET_ADDR;
512     parms->virt_entry = UNSET_ADDR;
513     parms->virt_hypercall = UNSET_ADDR;
514     parms->virt_hv_start_low = UNSET_ADDR;
515     parms->p2m_base = UNSET_ADDR;
516     parms->elf_paddr_offset = UNSET_ADDR;
517     parms->phys_entry = UNSET_ADDR32;
518 
519     /* Find and parse elf notes. */
520     count = elf_phdr_count(elf);
521     for ( i = 0; i < count; i++ )
522     {
523         phdr = elf_phdr_by_index(elf, i);
524         if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(phdr), 1) )
525             /* input has an insane program header count field */
526             break;
527         if ( elf_uval(elf, phdr, p_type) != PT_NOTE )
528             continue;
529 
530         /*
531          * Some versions of binutils do not correctly set p_offset for
532          * note segments.
533          */
534         if (elf_uval(elf, phdr, p_offset) == 0)
535              continue;
536 
537         more_notes = elf_xen_parse_notes(elf, parms,
538                                  elf_segment_start(elf, phdr),
539                                  elf_segment_end(elf, phdr),
540                                  &total_note_count);
541         if ( more_notes == ELF_NOTE_INVALID )
542             return -1;
543 
544         xen_elfnotes += more_notes;
545     }
546 
547     /*
548      * Fall back to any SHT_NOTE sections if no valid note segments
549      * were found.
550      */
551     if ( xen_elfnotes == 0 )
552     {
553         count = elf_shdr_count(elf);
554         for ( i = 1; i < count; i++ )
555         {
556             shdr = elf_shdr_by_index(elf, i);
557             if ( !elf_access_ok(elf, ELF_HANDLE_PTRVAL(shdr), 1) )
558                 /* input has an insane section header count field */
559                 break;
560 
561             if ( elf_uval(elf, shdr, sh_type) != SHT_NOTE )
562                 continue;
563 
564             more_notes = elf_xen_parse_notes(elf, parms,
565                                      elf_section_start(elf, shdr),
566                                      elf_section_end(elf, shdr),
567                                      &total_note_count);
568 
569             if ( more_notes == ELF_NOTE_INVALID )
570                 return -1;
571 
572             if ( xen_elfnotes == 0 && more_notes > 0 )
573                 elf_msg(elf, "ELF: using notes from SHT_NOTE section\n");
574 
575             xen_elfnotes += more_notes;
576         }
577 
578     }
579 
580     /*
581      * Finally fall back to the __xen_guest section.
582      */
583     if ( xen_elfnotes == 0 )
584     {
585         shdr = elf_shdr_by_name(elf, "__xen_guest");
586         if ( ELF_HANDLE_VALID(shdr) )
587         {
588             parms->guest_info = elf_section_start(elf, shdr);
589             parms->elf_note_start = ELF_INVALID_PTRVAL;
590             parms->elf_note_end   = ELF_INVALID_PTRVAL;
591             elf_msg(elf, "ELF: __xen_guest: \"%s\"\n",
592                     elf_strfmt(elf, parms->guest_info));
593             elf_xen_parse_guest_info(elf, parms);
594         }
595     }
596 
597     if ( elf_xen_note_check(elf, parms) != 0 )
598         return -1;
599     if ( elf_xen_addr_calc_check(elf, parms) != 0 )
600         return -1;
601     return 0;
602 }
603 
604 /*
605  * Local variables:
606  * mode: C
607  * c-file-style: "BSD"
608  * c-basic-offset: 4
609  * tab-width: 4
610  * indent-tabs-mode: nil
611  * End:
612  */
613