1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   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  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  23  */
  24 
  25 #include <stdio.h>
  26 #include <errno.h>
  27 #include <assert.h>
  28 #include <unistd.h>
  29 #include <libintl.h>
  30 #include <sys/multiboot.h>
  31 #include <sys/sysmacros.h>
  32 
  33 #include "bblk_einfo.h"
  34 #include "boot_utils.h"
  35 #include "mboot_extra.h"
  36 
  37 /*
  38  * Common functions to deal with the fake-multiboot encapsulation of the
  39  * bootblock and the location of the extra information area.
  40  */
  41 
  42 /* mboot checksum routine. */
  43 uint32_t
  44 compute_checksum(char *data, uint32_t size)
  45 {
  46         uint32_t        *ck_ptr;
  47         uint32_t        cksum = 0;
  48         int             i;
  49 
  50         ck_ptr = (uint32_t *)data;
  51         for (i = 0; i < size; i += sizeof (uint32_t))
  52                 cksum += *ck_ptr++;
  53 
  54         return (-cksum);
  55 }
  56 
  57 /* Given a buffer, look for a multiboot header within it. */
  58 int
  59 find_multiboot(char *buffer, uint32_t buf_size, uint32_t *mboot_off)
  60 {
  61         multiboot_header_t      *mboot;
  62         uint32_t                *iter;
  63         uint32_t                cksum;
  64         uint32_t                boundary;
  65         int                     i = 0;
  66 
  67         iter = (uint32_t *)buffer;
  68         *mboot_off = 0;
  69         /* multiboot header has to be within the first 32K. */
  70         boundary = MBOOT_SCAN_SIZE;
  71         if (boundary > buf_size)
  72                 boundary = buf_size;
  73 
  74         boundary = boundary - sizeof (multiboot_header_t);
  75 
  76         for (i = 0; i < boundary; i += 4, iter++) {
  77 
  78                 mboot = (multiboot_header_t *)iter;
  79                 if (mboot->magic != MB_HEADER_MAGIC)
  80                         continue;
  81 
  82                 /* Found magic signature -- check checksum. */
  83                 cksum = -(mboot->flags + mboot->magic);
  84                 if (mboot->checksum != cksum) {
  85                         BOOT_DEBUG("multiboot magic found at %p, but checksum "
  86                             "mismatches (is %x, should be %x)\n", mboot,
  87                             mboot->checksum, cksum);
  88                         continue;
  89                 } else {
  90                         if (!(mboot->flags & BB_MBOOT_AOUT_FLAG)) {
  91                                 BOOT_DEBUG("multiboot structure found, but no "
  92                                     "AOUT kludge specified, skipping.\n");
  93                                 continue;
  94                         } else {
  95                                 /* proper multiboot structure found. */
  96                                 *mboot_off = i;
  97                                 return (BC_SUCCESS);
  98                         }
  99                 }
 100         }
 101 
 102         return (BC_ERROR);
 103 }
 104 
 105 /*
 106  * Given a pointer to the extra information area (a sequence of bb_header_ext_t
 107  * + payload chunks), find the extended information structure.
 108  */
 109 bblk_einfo_t *
 110 find_einfo(char *extra, uint32_t size)
 111 {
 112         bb_header_ext_t         *ext_header;
 113         bblk_einfo_t            *einfo;
 114         uint32_t                cksum;
 115 
 116         assert(extra != NULL);
 117 
 118         ext_header = (bb_header_ext_t *)extra;
 119         if (ext_header->size > size) {
 120                 BOOT_DEBUG("Unable to find extended versioning information, "
 121                     "data size too big\n");
 122                 return (NULL);
 123         }
 124 
 125         cksum = compute_checksum(extra + sizeof (bb_header_ext_t),
 126             ext_header->size);
 127         BOOT_DEBUG("Extended information header checksum is %x\n", cksum);
 128 
 129         if (cksum != ext_header->checksum) {
 130                 BOOT_DEBUG("Unable to find extended versioning information, "
 131                     "data looks corrupted\n");
 132                 return (NULL);
 133         }
 134 
 135         /*
 136          * Currently we only have one extra header so it must be encapsulating
 137          * the extended information structure.
 138          */
 139         einfo = (bblk_einfo_t *)(extra + sizeof (bb_header_ext_t));
 140         if (memcmp(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0) {
 141                 BOOT_DEBUG("Unable to read stage2 extended versioning "
 142                     "information, wrong magic identifier\n");
 143                 BOOT_DEBUG("Found %s, expected %s\n", einfo->magic,
 144                     EINFO_MAGIC);
 145                 return (NULL);
 146         }
 147 
 148         return (einfo);
 149 }
 150 
 151 /*
 152  * Given a pointer to the extra area, add the extended information structure
 153  * encapsulated by a bb_header_ext_t structure.
 154  */
 155 void
 156 add_einfo(char *extra, char *updt_str, bblk_hs_t *hs, uint32_t avail_space)
 157 {
 158         bb_header_ext_t *ext_hdr;
 159         uint32_t        used_space;
 160         unsigned char   *dest;
 161         int             ret;
 162 
 163         assert(extra != NULL);
 164 
 165         if (updt_str == NULL) {
 166                 BOOT_DEBUG("WARNING: no update string passed to "
 167                     "add_stage2_einfo()\n");
 168                 return;
 169         }
 170 
 171         /* Reserve space for the extra header. */
 172         ext_hdr = (bb_header_ext_t *)extra;
 173         dest = (unsigned char *)extra + sizeof (*ext_hdr);
 174         /* Place the extended information structure. */
 175         ret = prepare_and_write_einfo(dest, updt_str, hs, avail_space,
 176             &used_space);
 177         if (ret != 0) {
 178                 (void) fprintf(stderr, gettext("Unable to write the extended "
 179                     "versioning information\n"));
 180                 return;
 181         }
 182 
 183         /* Fill the extended information associated header. */
 184         ext_hdr->size = P2ROUNDUP(used_space, 8);
 185         ext_hdr->checksum = compute_checksum((char *)dest, ext_hdr->size);
 186 }