2N/A/*
2N/A * GRUB -- GRand Unified Bootloader
2N/A * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A *
2N/A * GRUB is free software; you can redistribute it and/or modify
2N/A * it under the terms of the GNU General Public License as published by
2N/A * the Free Software Foundation; either version 3 of the License, or
2N/A * (at your option) any later version.
2N/A *
2N/A * GRUB is distributed in the hope that it will be useful,
2N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
2N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2N/A * GNU General Public License for more details.
2N/A *
2N/A * You should have received a copy of the GNU General Public License
2N/A * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
2N/A */
2N/A
2N/A#define BBLK_EINFO_VERSION (1)
2N/A#define EINFO_MAGIC "EXTINFO"
2N/A#define EINFO_MAGIC_SIZE (7)
2N/A
2N/Aenum bblk_hash_types_t {
2N/A BBLK_NO_HASH = 0,
2N/A BBLK_HASH_MD5
2N/A};
2N/A
2N/A/* Extra header preceding the payloads at the end of the bootblock. */
2N/Atypedef struct _bb_extra_header {
2N/A uint32_t size;
2N/A uint32_t checksum;
2N/A} bb_header_ext_t;
2N/A
2N/A#pragma pack(1)
2N/Atypedef struct _extended_info {
2N/A char magic[EINFO_MAGIC_SIZE];
2N/A uint8_t version;
2N/A uint8_t flags;
2N/A uint32_t str_off;
2N/A uint16_t str_size;
2N/A uint8_t hash_type;
2N/A uint32_t hash_off;
2N/A uint16_t hash_size;
2N/A char rsvd[32];
2N/A} bblk_einfo_t;
2N/A#pragma pack()
2N/A
2N/Atypedef struct _hashing_function {
2N/A unsigned int type;
2N/A unsigned int size;
2N/A char name[16];
2N/A void (*compute_hash)(void *, const void *, unsigned int);
2N/A} bblk_hash_t;
2N/A
2N/Atypedef struct _hashing_source {
2N/A unsigned char *src_buf;
2N/A unsigned int src_size;
2N/A} bblk_hs_t;
2N/A
2N/A
2N/Auint32_t compute_checksum(char *, uint32_t);
2N/Aint add_einfo(char *, char *, bblk_hs_t *, uint32_t, int, uint32_t *);
2N/Aint prepare_and_write_einfo(unsigned char *, char *, bblk_hs_t *,
2N/A uint32_t, uint32_t *, int);
2N/A
2N/A/*************************************************************************/
2N/A
2N/Avoid
2N/Agrub_md5_calc(void *output, const void *input, unsigned int inlen)
2N/A{
2N/A grub_uint8_t ctx[GRUB_MD_MD5->contextsize];
2N/A unsigned char *digest;
2N/A
2N/A GRUB_MD_MD5->init (ctx);
2N/A GRUB_MD_MD5->write (ctx, input, inlen);
2N/A digest = GRUB_MD_MD5->read (ctx);
2N/A GRUB_MD_MD5->final (ctx);
2N/A memcpy(output, digest, 0x10);
2N/A}
2N/A
2N/A/*************************************************************************/
2N/A
2N/Abblk_hash_t bblk_no_hash = {BBLK_NO_HASH, 0, "(no hash)", NULL};
2N/Abblk_hash_t bblk_md5_hash = {BBLK_HASH_MD5, 0x10, "MD5", grub_md5_calc};
2N/A
2N/Astatic int
2N/Acompute_hash(bblk_hs_t *hs, unsigned char *dest, bblk_hash_t *hash)
2N/A{
2N/A if (hs == NULL || dest == NULL || hash == NULL)
2N/A return (-1);
2N/A
2N/A hash->compute_hash(dest, hs->src_buf, hs->src_size);
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Aprepare_and_write_einfo(unsigned char *dest, char *infostr, bblk_hs_t *hs,
2N/A uint32_t maxsize, uint32_t *used_space, int doit)
2N/A{
2N/A uint16_t hash_size;
2N/A uint32_t hash_off;
2N/A unsigned char *data;
2N/A bblk_einfo_t *einfo = (bblk_einfo_t *)dest;
2N/A bblk_hash_t *hashinfo = &bblk_md5_hash;
2N/A
2N/A /*
2N/A * 'dest' might be both containing the buffer we want to hash and
2N/A * containing our einfo structure: delay any update of it after the
2N/A * hashing has been calculated.
2N/A */
2N/A hash_size = hashinfo->size;
2N/A hash_off = sizeof (bblk_einfo_t);
2N/A
2N/A if (hash_off + hash_size > maxsize) {
2N/A (void) fprintf(stderr, gettext("Unable to add extended info, "
2N/A "not enough space\n"));
2N/A return (-1);
2N/A }
2N/A
2N/A if (doit) {
2N/A data = dest + hash_off;
2N/A
2N/A if (compute_hash(hs, data, hashinfo) < 0) {
2N/A (void) fprintf(stderr, gettext("%s hash operation failed\n"),
2N/A hashinfo->name);
2N/A einfo->hash_type = bblk_no_hash.type;
2N/A einfo->hash_size = bblk_no_hash.size;
2N/A } else {
2N/A einfo->hash_type = hashinfo->type;
2N/A einfo->hash_size = hashinfo->size;
2N/A }
2N/A
2N/A (void) memcpy(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE);
2N/A einfo->version = BBLK_EINFO_VERSION;
2N/A einfo->flags = 0;
2N/A einfo->hash_off = hash_off;
2N/A einfo->hash_size = hash_size;
2N/A einfo->str_off = einfo->hash_off + einfo->hash_size + 1;
2N/A }
2N/A
2N/A if (infostr == NULL) {
2N/A (void) fprintf(stderr, gettext("Unable to add extended info, "
2N/A "string is empty\n"));
2N/A return (-1);
2N/A }
2N/A
2N/A if (doit)
2N/A einfo->str_size = strlen(infostr);
2N/A
2N/A if (einfo->str_off + einfo->str_size > maxsize) {
2N/A (void) fprintf(stderr, gettext("Unable to add extended info, "
2N/A "not enough space\n"));
2N/A return (-1);
2N/A }
2N/A
2N/A if (!doit) {
2N/A *used_space = hash_off + hash_size + 1 + strlen(infostr);
2N/A return (0);
2N/A }
2N/A
2N/A data = dest + einfo->str_off;
2N/A (void) memcpy(data, infostr, einfo->str_size);
2N/A *used_space = einfo->str_off + einfo->str_size;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*************************************************************************/
2N/A
2N/A/*
2N/A * Common functions to deal with the fake-multiboot encapsulation of the
2N/A * bootblock and the location of the extra information area.
2N/A */
2N/A
2N/A/* mboot checksum routine. */
2N/Auint32_t
2N/Acompute_checksum(char *data, uint32_t size)
2N/A{
2N/A uint32_t *ck_ptr;
2N/A uint32_t cksum = 0;
2N/A int i;
2N/A
2N/A ck_ptr = (uint32_t *)data;
2N/A for (i = 0; i < size; i += sizeof (uint32_t))
2N/A cksum += *ck_ptr++;
2N/A
2N/A return (-cksum);
2N/A}
2N/A
2N/A/*
2N/A * Given a pointer to the extra area, add the extended information structure
2N/A * encapsulated by a bb_header_ext_t structure.
2N/A */
2N/Aint
2N/Aadd_einfo(char *extra, char *updt_str, bblk_hs_t *hs, uint32_t avail_space,
2N/A int doit, uint32_t *space_usedp)
2N/A{
2N/A bb_header_ext_t *ext_hdr;
2N/A unsigned char *dest;
2N/A uint32_t used_space;
2N/A int ret;
2N/A
2N/A assert(extra != NULL);
2N/A
2N/A if (updt_str == NULL) {
2N/A return (1);
2N/A }
2N/A
2N/A /* Reserve space for the extra header. */
2N/A ext_hdr = (bb_header_ext_t *)extra;
2N/A dest = (unsigned char *)extra + sizeof (*ext_hdr);
2N/A /* Place the extended information structure. */
2N/A ret = prepare_and_write_einfo(dest, updt_str, hs, avail_space,
2N/A &used_space, doit);
2N/A if (ret != 0) {
2N/A if (doit)
2N/A (void) fprintf(stderr, gettext("Unable to write the extended "
2N/A "versioning information\n"));
2N/A return(1);
2N/A }
2N/A
2N/A if (!doit) {
2N/A if (space_usedp)
2N/A *space_usedp = ALIGN_UP(used_space, 8);
2N/A return (0);
2N/A }
2N/A
2N/A /* Fill the extended information associated header. */
2N/A ext_hdr->size = ALIGN_UP(used_space, 8);
2N/A ext_hdr->checksum = compute_checksum((char *)dest, ext_hdr->size);
2N/A return (0);
2N/A}
2N/A
2N/A/*************************************************************************/
2N/Astatic void
2N/Agrub_solaris_get_size_with_versioning(char *core_img, size_t core_size,
2N/A size_t buflen, char *vers_str, size_t *new_core_size)
2N/A{
2N/A bblk_hs_t hs;
2N/A uint32_t avail_space;
2N/A bb_header_ext_t *ext_hdr;
2N/A uint32_t space = 0;
2N/A
2N/A hs.src_buf = (unsigned char *)core_img;
2N/A hs.src_size = core_size;
2N/A
2N/A avail_space = buflen - ALIGN_UP(core_size, 8);
2N/A ext_hdr = (bb_header_ext_t *)(core_img + ALIGN_UP(core_size, 8));
2N/A if (add_einfo((char *)ext_hdr, vers_str, &hs, avail_space, 0, &space)
2N/A == 0) {
2N/A *new_core_size = ALIGN_UP(core_size, 8) + space +
2N/A sizeof (*ext_hdr);
2N/A if (verbosity)
2N/A printf("Size of versioning info: %d\n",
2N/A *new_core_size - core_size);
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Agrub_solaris_add_versioning_payload(char *core_img, size_t core_size,
2N/A size_t buflen, char *vers_str)
2N/A{
2N/A bblk_hs_t hs;
2N/A uint32_t avail_space;
2N/A bb_header_ext_t *ext_hdr;
2N/A
2N/A hs.src_buf = (unsigned char *)core_img;
2N/A hs.src_size = core_size;
2N/A
2N/A avail_space = buflen - ALIGN_UP(core_size, 8);
2N/A ext_hdr = (bb_header_ext_t *)(core_img + ALIGN_UP(core_size, 8));
2N/A if (add_einfo((char *)ext_hdr, vers_str, &hs, avail_space, 1, NULL)
2N/A == 0) {
2N/A if (verbosity)
2N/A printf("Size before versioning: %d, after: %d\n",
2N/A core_size, ALIGN_UP(core_size, 8) + ext_hdr->size +
2N/A sizeof (*ext_hdr));
2N/A }
2N/A}
2N/A
2N/A