Print this page
3364 dboot should check boot archive integrity
Reviewed by: Hans Rosenfeld <hans.rosenfeld@nexenta.com>
Reviewed by: Dan McDonald <danmcd@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Reviewed by: Garrett D'Amore <garrett@damore.org>


   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.


  25  */
  26 
  27 
  28 #include <sys/types.h>
  29 #include <sys/machparam.h>
  30 #include <sys/x86_archext.h>
  31 #include <sys/systm.h>
  32 #include <sys/mach_mmu.h>
  33 #include <sys/multiboot.h>

  34 
  35 #if defined(__xpv)
  36 
  37 #include <sys/hypervisor.h>
  38 uintptr_t xen_virt_start;
  39 pfn_t *mfn_to_pfn_mapping;
  40 
  41 #else /* !__xpv */
  42 
  43 extern multiboot_header_t mb_header;
  44 extern int have_cpuid(void);
  45 
  46 #endif /* !__xpv */
  47 
  48 #include <sys/inttypes.h>
  49 #include <sys/bootinfo.h>
  50 #include <sys/mach_mmu.h>
  51 #include <sys/boot_console.h>
  52 
  53 #include "dboot_asm.h"
  54 #include "dboot_printf.h"
  55 #include "dboot_xboot.h"
  56 #include "dboot_elfload.h"
  57 


  58 /*
  59  * This file contains code that runs to transition us from either a multiboot
  60  * compliant loader (32 bit non-paging) or a XPV domain loader to
  61  * regular kernel execution. Its task is to setup the kernel memory image
  62  * and page tables.
  63  *
  64  * The code executes as:
  65  *      - 32 bits under GRUB (for 32 or 64 bit Solaris)
  66  *      - a 32 bit program for the 32-bit PV hypervisor
  67  *      - a 64 bit program for the 64-bit PV hypervisor (at least for now)
  68  *
  69  * Under the PV hypervisor, we must create mappings for any memory beyond the
  70  * initial start of day allocation (such as the kernel itself).
  71  *
  72  * When on the metal, the mapping between maddr_t and paddr_t is 1:1.
  73  * Since we are running in real mode, so all such memory is accessible.
  74  */
  75 
  76 /*
  77  * Standard bits used in PTE (page level) and PTP (internal levels)


 750         /*
 751          * build bios reserved memlists
 752          */
 753         build_rsvdmemlists();
 754 
 755         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 756                 /*
 757                  * build PCI Memory list
 758                  */
 759                 map.nr_entries = MAXMAPS;
 760                 /*LINTED: constant in conditional context*/
 761                 set_xen_guest_handle(map.buffer, map_buffer);
 762                 if (HYPERVISOR_memory_op(XENMEM_machine_memory_map, &map) != 0)
 763                         dboot_panic("getting XENMEM_machine_memory_map failed");
 764                 build_pcimemlists(map_buffer, map.nr_entries);
 765         }
 766 }
 767 
 768 #else   /* !__xpv */
 769 


























 770 /*

































































































 771  * During memory allocation, find the highest address not used yet.
 772  */
 773 static void
 774 check_higher(paddr_t a)
 775 {
 776         if (a < next_avail_addr)
 777                 return;
 778         next_avail_addr = RNDUP(a + 1, MMU_PAGESIZE);
 779         DBG(next_avail_addr);
 780 }
 781 
 782 /*
 783  * Walk through the module information finding the last used address.
 784  * The first available address will become the top level page table.
 785  *
 786  * We then build the phys_install memlist from the multiboot information.
 787  */
 788 static void
 789 init_mem_alloc(void)
 790 {


 796         extern char _end[];
 797         int i;
 798 
 799         DBG_MSG("Entered init_mem_alloc()\n");
 800         DBG((uintptr_t)mb_info);
 801 
 802         if (mb_info->mods_count > MAX_MODULES) {
 803                 dboot_panic("Too many modules (%d) -- the maximum is %d.",
 804                     mb_info->mods_count, MAX_MODULES);
 805         }
 806         /*
 807          * search the modules to find the last used address
 808          * we'll build the module list while we're walking through here
 809          */
 810         DBG_MSG("\nFinding Modules\n");
 811         check_higher((paddr_t)(uintptr_t)&_end);
 812         for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0;
 813             i < mb_info->mods_count;
 814             ++mod, ++i) {
 815                 if (prom_debug) {
 816                         dboot_printf("\tmodule #%d: %s at: 0x%lx, len 0x%lx\n",
 817                             i, (char *)(mod->mod_name),
 818                             (ulong_t)mod->mod_start, (ulong_t)mod->mod_end);
 819                 }
 820                 modules[i].bm_addr = mod->mod_start;
 821                 if (mod->mod_start > mod->mod_end) {
 822                         dboot_panic("module[%d]: Invalid module start address "
 823                             "(0x%llx)", i, (uint64_t)mod->mod_start);
 824                 }
 825                 modules[i].bm_size = mod->mod_end - mod->mod_start;
 826 
 827                 check_higher(mod->mod_end);
 828         }
 829         bi->bi_modules = (native_ptr_t)(uintptr_t)modules;
 830         DBG(bi->bi_modules);
 831         bi->bi_module_cnt = mb_info->mods_count;
 832         DBG(bi->bi_module_cnt);
 833 


 834         /*
 835          * Walk through the memory map from multiboot and build our memlist
 836          * structures. Note these will have native format pointers.
 837          */
 838         DBG_MSG("\nFinding Memory Map\n");
 839         DBG(mb_info->flags);
 840         max_mem = 0;
 841         if (mb_info->flags & 0x40) {
 842                 int cnt = 0;
 843 
 844                 DBG(mb_info->mmap_addr);
 845                 DBG(mb_info->mmap_length);
 846                 check_higher(mb_info->mmap_addr + mb_info->mmap_length);
 847 
 848                 for (mmap = (mb_memory_map_t *)mb_info->mmap_addr;
 849                     (uint32_t)mmap < mb_info->mmap_addr + mb_info->mmap_length;
 850                     mmap = (mb_memory_map_t *)((uint32_t)mmap + mmap->size
 851                     + sizeof (mmap->size))) {
 852                         ++cnt;
 853                         start = ((uint64_t)mmap->base_addr_high << 32) +




   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 
  22 /*
  23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * Copyright 2012 Joyent, Inc.  All rights reserved.
  27  */
  28 
  29 
  30 #include <sys/types.h>
  31 #include <sys/machparam.h>
  32 #include <sys/x86_archext.h>
  33 #include <sys/systm.h>
  34 #include <sys/mach_mmu.h>
  35 #include <sys/multiboot.h>
  36 #include <sys/sha1.h>
  37 
  38 #if defined(__xpv)
  39 
  40 #include <sys/hypervisor.h>
  41 uintptr_t xen_virt_start;
  42 pfn_t *mfn_to_pfn_mapping;
  43 
  44 #else /* !__xpv */
  45 
  46 extern multiboot_header_t mb_header;
  47 extern int have_cpuid(void);
  48 
  49 #endif /* !__xpv */
  50 
  51 #include <sys/inttypes.h>
  52 #include <sys/bootinfo.h>
  53 #include <sys/mach_mmu.h>
  54 #include <sys/boot_console.h>
  55 
  56 #include "dboot_asm.h"
  57 #include "dboot_printf.h"
  58 #include "dboot_xboot.h"
  59 #include "dboot_elfload.h"
  60 
  61 #define SHA1_ASCII_LENGTH       (SHA1_DIGEST_LENGTH * 2)
  62 
  63 /*
  64  * This file contains code that runs to transition us from either a multiboot
  65  * compliant loader (32 bit non-paging) or a XPV domain loader to
  66  * regular kernel execution. Its task is to setup the kernel memory image
  67  * and page tables.
  68  *
  69  * The code executes as:
  70  *      - 32 bits under GRUB (for 32 or 64 bit Solaris)
  71  *      - a 32 bit program for the 32-bit PV hypervisor
  72  *      - a 64 bit program for the 64-bit PV hypervisor (at least for now)
  73  *
  74  * Under the PV hypervisor, we must create mappings for any memory beyond the
  75  * initial start of day allocation (such as the kernel itself).
  76  *
  77  * When on the metal, the mapping between maddr_t and paddr_t is 1:1.
  78  * Since we are running in real mode, so all such memory is accessible.
  79  */
  80 
  81 /*
  82  * Standard bits used in PTE (page level) and PTP (internal levels)


 755         /*
 756          * build bios reserved memlists
 757          */
 758         build_rsvdmemlists();
 759 
 760         if (DOMAIN_IS_INITDOMAIN(xen_info)) {
 761                 /*
 762                  * build PCI Memory list
 763                  */
 764                 map.nr_entries = MAXMAPS;
 765                 /*LINTED: constant in conditional context*/
 766                 set_xen_guest_handle(map.buffer, map_buffer);
 767                 if (HYPERVISOR_memory_op(XENMEM_machine_memory_map, &map) != 0)
 768                         dboot_panic("getting XENMEM_machine_memory_map failed");
 769                 build_pcimemlists(map_buffer, map.nr_entries);
 770         }
 771 }
 772 
 773 #else   /* !__xpv */
 774 
 775 static uint8_t
 776 dboot_a2h(char v)
 777 {
 778         if (v >= 'a')
 779                 return (v - 'a' + 0xa);
 780         else if (v >= 'A')
 781                 return (v - 'A' + 0xa);
 782         else if (v >= '0')
 783                 return (v - '0');
 784         else
 785                 dboot_panic("bad ASCII hex character %c\n", v);
 786 
 787         return (0);
 788 }
 789 
 790 static void
 791 digest_a2h(const char *ascii, uint8_t *digest)
 792 {
 793         unsigned int i;
 794 
 795         for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
 796                 digest[i] = dboot_a2h(ascii[i * 2]) << 4;
 797                 digest[i] |= dboot_a2h(ascii[i * 2 + 1]);
 798         }
 799 }
 800 
 801 /*
 802  * Generate a SHA-1 hash of the first len bytes of image, and compare it with
 803  * the ASCII-format hash found in the 40-byte buffer at ascii.  If they
 804  * match, return 0, otherwise -1.  This works only for images smaller than
 805  * 4 GB, which should not be a problem.
 806  */
 807 static int
 808 check_image_hash(const char *ascii, const void *image, size_t len)
 809 {
 810         SHA1_CTX ctx;
 811         uint8_t digest[SHA1_DIGEST_LENGTH];
 812         uint8_t baseline[SHA1_DIGEST_LENGTH];
 813         unsigned int i;
 814 
 815         digest_a2h(ascii, baseline);
 816 
 817         SHA1Init(&ctx);
 818         SHA1Update(&ctx, image, len);
 819         SHA1Final(digest, &ctx);
 820 
 821         for (i = 0; i < SHA1_DIGEST_LENGTH; i++) {
 822                 if (digest[i] != baseline[i])
 823                         return (-1);
 824         }
 825 
 826         return (0);
 827 }
 828 
 829 static void
 830 check_images(void)
 831 {
 832         int i;
 833         char *hashes;
 834         mb_module_t *mod, *hashmod;
 835         char *hash;
 836         char displayhash[SHA1_ASCII_LENGTH + 1];
 837         size_t hashlen;
 838         size_t len;
 839 
 840         /*
 841          * A brief note on lengths and sizes: GRUB, for reasons unknown, passes
 842          * the address of the last valid byte in a module plus 1 as mod_end.
 843          * This is of course a bug; the multiboot specification simply states
 844          * that mod_start and mod_end "contain the start and end addresses of
 845          * the boot module itself" which is pretty obviously not what GRUB is
 846          * doing.  However, fixing it requires that not only this code be
 847          * changed but also that other code consuming this value and values
 848          * derived from it be fixed, and that the kernel and GRUB must either
 849          * both have the bug or neither.  While there are a lot of combinations
 850          * that will work, there are also some that won't, so for simplicity
 851          * we'll just cope with the bug.  That means we won't actually hash the
 852          * byte at mod_end, and we will expect that mod_end for the hash file
 853          * itself is one greater than some multiple of 41 (40 bytes of ASCII
 854          * hash plus a newline for each module).
 855          */
 856 
 857         if (mb_info->mods_count > 1) {
 858                 mod = (mb_module_t *)mb_info->mods_addr;
 859                 hashmod = mod + (mb_info->mods_count - 1);
 860                 hashes = (char *)hashmod->mod_start;
 861                 hashlen = (size_t)(hashmod->mod_end - hashmod->mod_start);
 862                 hash = hashes;
 863                 if (prom_debug) {
 864                         dboot_printf("Hash module found at %lx size %lx\n",
 865                             (ulong_t)hashes, (ulong_t)hashlen);
 866                 }
 867         } else {
 868                 DBG_MSG("Skipping hash check; no hash module found.\n");
 869                 return;
 870         }
 871 
 872         for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0;
 873             i < mb_info->mods_count - 1; ++mod, ++i) {
 874                 if ((hash - hashes) + SHA1_ASCII_LENGTH + 1 > hashlen) {
 875                         dboot_printf("Short hash module of length 0x%lx bytes; "
 876                             "skipping hash checks\n", (ulong_t)hashlen);
 877                         break;
 878                 }
 879 
 880                 (void) memcpy(displayhash, hash, SHA1_ASCII_LENGTH);
 881                 displayhash[SHA1_ASCII_LENGTH] = '\0';
 882                 if (prom_debug) {
 883                         dboot_printf("Checking hash for module %d [%s]: ",
 884                             i, displayhash);
 885                 }
 886 
 887                 len = mod->mod_end - mod->mod_start;      /* see above */
 888                 if (check_image_hash(hash, (void *)mod->mod_start, len) != 0) {
 889                         dboot_panic("SHA-1 hash mismatch on %s; expected %s\n",
 890                             (char *)mod->mod_name, displayhash);
 891                 } else {
 892                         DBG_MSG("OK\n");
 893                 }
 894                 hash += SHA1_ASCII_LENGTH + 1;
 895         }
 896 }
 897 
 898 /*
 899  * During memory allocation, find the highest address not used yet.
 900  */
 901 static void
 902 check_higher(paddr_t a)
 903 {
 904         if (a < next_avail_addr)
 905                 return;
 906         next_avail_addr = RNDUP(a + 1, MMU_PAGESIZE);
 907         DBG(next_avail_addr);
 908 }
 909 
 910 /*
 911  * Walk through the module information finding the last used address.
 912  * The first available address will become the top level page table.
 913  *
 914  * We then build the phys_install memlist from the multiboot information.
 915  */
 916 static void
 917 init_mem_alloc(void)
 918 {


 924         extern char _end[];
 925         int i;
 926 
 927         DBG_MSG("Entered init_mem_alloc()\n");
 928         DBG((uintptr_t)mb_info);
 929 
 930         if (mb_info->mods_count > MAX_MODULES) {
 931                 dboot_panic("Too many modules (%d) -- the maximum is %d.",
 932                     mb_info->mods_count, MAX_MODULES);
 933         }
 934         /*
 935          * search the modules to find the last used address
 936          * we'll build the module list while we're walking through here
 937          */
 938         DBG_MSG("\nFinding Modules\n");
 939         check_higher((paddr_t)(uintptr_t)&_end);
 940         for (mod = (mb_module_t *)(mb_info->mods_addr), i = 0;
 941             i < mb_info->mods_count;
 942             ++mod, ++i) {
 943                 if (prom_debug) {
 944                         dboot_printf("\tmodule #%d: %s at: 0x%lx, end 0x%lx\n",
 945                             i, (char *)(mod->mod_name),
 946                             (ulong_t)mod->mod_start, (ulong_t)mod->mod_end);
 947                 }
 948                 modules[i].bm_addr = mod->mod_start;
 949                 if (mod->mod_start > mod->mod_end) {
 950                         dboot_panic("module[%d]: Invalid module start address "
 951                             "(0x%llx)", i, (uint64_t)mod->mod_start);
 952                 }
 953                 modules[i].bm_size = mod->mod_end - mod->mod_start;
 954 
 955                 check_higher(mod->mod_end);
 956         }
 957         bi->bi_modules = (native_ptr_t)(uintptr_t)modules;
 958         DBG(bi->bi_modules);
 959         bi->bi_module_cnt = mb_info->mods_count;
 960         DBG(bi->bi_module_cnt);
 961 
 962         check_images();
 963 
 964         /*
 965          * Walk through the memory map from multiboot and build our memlist
 966          * structures. Note these will have native format pointers.
 967          */
 968         DBG_MSG("\nFinding Memory Map\n");
 969         DBG(mb_info->flags);
 970         max_mem = 0;
 971         if (mb_info->flags & 0x40) {
 972                 int cnt = 0;
 973 
 974                 DBG(mb_info->mmap_addr);
 975                 DBG(mb_info->mmap_length);
 976                 check_higher(mb_info->mmap_addr + mb_info->mmap_length);
 977 
 978                 for (mmap = (mb_memory_map_t *)mb_info->mmap_addr;
 979                     (uint32_t)mmap < mb_info->mmap_addr + mb_info->mmap_length;
 980                     mmap = (mb_memory_map_t *)((uint32_t)mmap + mmap->size
 981                     + sizeof (mmap->size))) {
 982                         ++cnt;
 983                         start = ((uint64_t)mmap->base_addr_high << 32) +