# HG changeset patch # User Enrico Perla - Sun Microsystems # Date 1280357517 25200 # Node ID f5f96e09bf4930ae0de90ffffa937cb49b713762 # Parent ebf6da4d3fca0501d963026d1ee5dc49f72963d3 PSARC/2010/113 Boot Block Downgrade Avoidance PSARC/2010/271 EOF and removal of installgrub(1m) floppy support 6944352 prevent downgrading the boot block on update on multi-BE environments 6972371 EOF and removal of installgrub(1m) floppy support [PSARC/2010/271] diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/Makefile --- a/usr/src/cmd/boot/Makefile Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/cmd/boot/Makefile Wed Jul 28 15:51:57 2010 -0700 @@ -19,8 +19,7 @@ # CDDL HEADER END # # -# Copyright 2008 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # include $(SRC)/cmd/Makefile.cmd @@ -38,6 +37,9 @@ mbr \ symdef +sparc_SUBDIRS= \ + installboot + COMMON_LINTSUBDIRS= \ bootadm @@ -46,6 +48,9 @@ installgrub \ symdef +sparc_LINTSUBDIRS= \ + installboot + COMMON_MSGSUBDIRS= \ bootadm \ fiocompress @@ -53,6 +58,9 @@ i386_MSGSUBDIRS= \ installgrub +sparc_MSGSUBDIRS= \ + installboot + all:= TARGET= all install:= TARGET= install diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/bblk_einfo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/bblk_einfo.c Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,414 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bblk_einfo.h" +#include "boot_utils.h" + +bblk_hash_t bblk_no_hash = {BBLK_NO_HASH, 0, "(no hash)", NULL}; +bblk_hash_t bblk_md5_hash = {BBLK_HASH_MD5, 0x10, "MD5", md5_calc}; + +bblk_hash_t *bblk_hash_list[BBLK_HASH_TOT] = { + &bblk_no_hash, + &bblk_md5_hash +}; + +/* + * einfo_compare_dotted_version() + * Compares two strings with an arbitrary long number of dot-separated numbers. + * Returns: 0 - if the version numbers are equal + * 1 - if str1 version number is more recent than str2 + * 2 - if str2 version number is more recent than str1 + * -1 - if an error occurred + * + * Comparison is done field by field, by retrieving an unsigned integer value, + * (missing fields are assumed as 0, but explict zeroes take precedence) so: + * 4.1.2.11 > 4.1.2.2 > 4.1.2.0 > 4.1.2 + * + * where ">" means "more recent than". + */ +static int +einfo_compare_dotted_version(const char *str1, const char *str2) +{ + int retval = 0; + char *verstr1, *verstr2, *freeptr1, *freeptr2; + char *parsep1, *parsep2; + unsigned int val_str1, val_str2; + + freeptr1 = verstr1 = strdup(str1); + freeptr2 = verstr2 = strdup(str2); + if (verstr1 == NULL || verstr2 == NULL) { + retval = -1; + goto out; + } + + while (verstr1 != NULL && verstr2 != NULL) { + parsep1 = strsep(&verstr1, "."); + parsep2 = strsep(&verstr2, "."); + + val_str1 = atoi(parsep1); + val_str2 = atoi(parsep2); + + if (val_str1 > val_str2) { + retval = 1; + goto out; + } + + if (val_str2 > val_str1) { + retval = 2; + goto out; + } + } + + /* Common portion of the version string is equal. */ + if (verstr1 == NULL && verstr2 != NULL) + retval = 2; + if (verstr2 == NULL && verstr1 != NULL) + retval = 1; + +out: + free(freeptr1); + free(freeptr2); + return (retval); +} + +/* + * einfo_compare_timestamps() + * Currently, timestamp is in %Y%m%dT%H%M%SZ format in UTC, which means that + * we can simply do a lexicographic comparison to know which one is the most + * recent. + * + * Returns: 0 - if timestamps coincide + * 1 - if the timestamp in str1 is more recent + * 2 - if the timestamp in str2 is more recent + */ +static int +einfo_compare_timestamps(const char *str1, const char *str2) +{ + int retval; + + retval = strcmp(str1, str2); + if (retval > 0) + retval = 1; + if (retval < 0) + retval = 2; + + return (retval); +} + +/* + * einfo_compare_version() + * Given two extended versions, compare the two and returns which one is more + * "recent". Comparison is based on dotted version number fields and a + * timestamp. + * + * Returns: -1 - on error + * 0 - if the two versions coincide + * 1 - if the version in str1 is more recent + * 2 - if the version in str2 is more recent + */ +static int +einfo_compare_version(const char *str1, const char *str2) +{ + int retval = 0; + char *verstr1, *verstr2, *freeptr1, *freeptr2; + char *parsep1, *parsep2; + + freeptr1 = verstr1 = strdup(str1); + freeptr2 = verstr2 = strdup(str2); + if (verstr1 == NULL || verstr2 == NULL) { + retval = -1; + goto out; + } + + parsep1 = verstr1; + parsep2 = verstr2; + + while (parsep1 != NULL && parsep2 != NULL) { + parsep1 = strsep(&verstr1, ",:-"); + parsep2 = strsep(&verstr2, ",:-"); + + /* verstr1 or verstr2 will be NULL before parsep1 or parsep2. */ + if (verstr1 == NULL || verstr2 == NULL) { + retval = einfo_compare_timestamps(parsep1, parsep2); + goto out; + } + + retval = einfo_compare_dotted_version(parsep1, parsep2); + if (retval == 0) + continue; + else + goto out; + } +out: + free(freeptr1); + free(freeptr2); + return (retval); +} + +/* + * print_einfo() + * + * Print the extended information contained into the pointed structure. + * 'bufsize' specifies the real size of the structure, since str_off and + * hash_off need to point somewhere past the header. + */ +void +print_einfo(uint8_t flags, bblk_einfo_t *einfo, unsigned long bufsize) +{ + int i = 0; + char *version; + boolean_t has_hash = B_FALSE; + unsigned char *hash; + + if (einfo->str_off + einfo->str_size > bufsize) { + (void) fprintf(stdout, gettext("String offset %d is beyond the " + "buffer size\n"), einfo->str_off); + return; + } + + version = (char *)einfo + einfo->str_off; + if (einfo->hash_type != BBLK_NO_HASH && + einfo->hash_type < BBLK_HASH_TOT) { + if (einfo->hash_off + einfo->hash_size > bufsize) { + (void) fprintf(stdout, gettext("Warning: hashing " + "present but hash offset %d is beyond the buffer " + "size\n"), einfo->hash_off); + has_hash = B_FALSE; + } else { + hash = (unsigned char *)einfo + einfo->hash_off; + has_hash = B_TRUE; + } + } + + if (flags & EINFO_PRINT_HEADER) { + (void) fprintf(stdout, "Boot Block Extended Info Header:\n"); + (void) fprintf(stdout, "\tmagic: "); + for (i = 0; i < EINFO_MAGIC_SIZE; i++) + (void) fprintf(stdout, "%c", einfo->magic[i]); + (void) fprintf(stdout, "\n"); + (void) fprintf(stdout, "\tversion: %d\n", einfo->version); + (void) fprintf(stdout, "\tflags: %x\n", einfo->flags); + (void) fprintf(stdout, "\textended version string offset: %d\n", + einfo->str_off); + (void) fprintf(stdout, "\textended version string size: %d\n", + einfo->str_size); + (void) fprintf(stdout, "\thashing type: %d (%s)\n", + einfo->hash_type, has_hash ? + bblk_hash_list[einfo->hash_type]->name : "nil"); + (void) fprintf(stdout, "\thash offset: %d\n", einfo->hash_off); + (void) fprintf(stdout, "\thash size: %d\n", einfo->hash_size); + } + + if (flags & EINFO_EASY_PARSE) { + (void) fprintf(stdout, "%s\n", version); + } else { + (void) fprintf(stdout, "Extended version string: %s\n", + version); + if (has_hash) { + (void) fprintf(stdout, "%s hash: ", + bblk_hash_list[einfo->hash_type]->name); + } else { + (void) fprintf(stdout, "No hashing available\n"); + } + } + + if (has_hash) { + for (i = 0; i < einfo->hash_size; i++) { + (void) fprintf(stdout, "%02x", hash[i]); + } + (void) fprintf(stdout, "\n"); + } +} + +static int +compute_hash(bblk_hs_t *hs, unsigned char *dest, bblk_hash_t *hash) +{ + if (hs == NULL || dest == NULL || hash == NULL) + return (-1); + + hash->compute_hash(dest, hs->src_buf, hs->src_size); + return (0); +} + +int +prepare_and_write_einfo(unsigned char *dest, char *infostr, bblk_hs_t *hs, + uint32_t maxsize, uint32_t *used_space) +{ + uint16_t hash_size; + uint32_t hash_off; + unsigned char *data; + bblk_einfo_t *einfo = (bblk_einfo_t *)dest; + bblk_hash_t *hashinfo = bblk_hash_list[BBLK_DEFAULT_HASH]; + + /* + * 'dest' might be both containing the buffer we want to hash and + * containing our einfo structure: delay any update of it after the + * hashing has been calculated. + */ + hash_size = hashinfo->size; + hash_off = sizeof (bblk_einfo_t); + + if (hash_off + hash_size > maxsize) { + (void) fprintf(stderr, gettext("Unable to add extended info, " + "not enough space\n")); + return (-1); + } + + data = dest + hash_off; + if (compute_hash(hs, data, hashinfo) < 0) { + (void) fprintf(stderr, gettext("%s hash operation failed\n"), + hashinfo->name); + einfo->hash_type = bblk_no_hash.type; + einfo->hash_size = bblk_no_hash.size; + } else { + einfo->hash_type = hashinfo->type; + einfo->hash_size = hashinfo->size; + } + + (void) memcpy(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE); + einfo->version = BBLK_EINFO_VERSION; + einfo->flags = 0; + einfo->hash_off = hash_off; + einfo->hash_size = hash_size; + einfo->str_off = einfo->hash_off + einfo->hash_size + 1; + + if (infostr == NULL) { + (void) fprintf(stderr, gettext("Unable to add extended info, " + "string is empty\n")); + return (-1); + } + einfo->str_size = strlen(infostr); + + if (einfo->str_off + einfo->str_size > maxsize) { + (void) fprintf(stderr, gettext("Unable to add extended info, " + "not enough space\n")); + return (-1); + } + + data = dest + einfo->str_off; + (void) memcpy(data, infostr, einfo->str_size); + *used_space = einfo->str_off + einfo->str_size; + + return (0); +} + +/* + * einfo_should_update() + * Given information on the boot block currently on disk (disk_einfo) and + * information on the supplied boot block (hs for hashing, verstr as the + * associated version string) decide if an update of the on-disk boot block + * is necessary or not. + */ +boolean_t +einfo_should_update(bblk_einfo_t *disk_einfo, bblk_hs_t *hs, char *verstr) +{ + bblk_hash_t *hashing; + unsigned char *disk_hash; + unsigned char *local_hash; + char *disk_version; + int retval; + + if (disk_einfo == NULL) + return (B_TRUE); + + if (memcmp(disk_einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0) + return (B_TRUE); + + if (disk_einfo->version < BBLK_EINFO_VERSION) + return (B_TRUE); + + disk_version = einfo_get_string(disk_einfo); + retval = einfo_compare_version(verstr, disk_version); + /* + * If something goes wrong or if the on-disk version is more recent + * do not update the bootblock. + */ + if (retval == -1 || retval == 2) + return (B_FALSE); + + /* + * If we got here it means that the two version strings are either + * equal or the new bootblk binary is more recent. In order to save + * some needless writes let's use the hash to determine if an update + * is really necessary. + */ + if (disk_einfo->hash_type == bblk_no_hash.type) + return (B_TRUE); + + if (disk_einfo->hash_type >= BBLK_HASH_TOT) + return (B_TRUE); + + hashing = bblk_hash_list[disk_einfo->hash_type]; + + local_hash = malloc(hashing->size); + if (local_hash == NULL) + return (B_TRUE); + + /* + * Failure in computing the hash may mean something wrong + * with the boot block file. Better be conservative here. + */ + if (compute_hash(hs, local_hash, hashing) < 0) { + free(local_hash); + return (B_FALSE); + } + + disk_hash = (unsigned char *)einfo_get_hash(disk_einfo); + + if (memcmp(local_hash, disk_hash, disk_einfo->hash_size) == 0) { + free(local_hash); + return (B_FALSE); + } + + free(local_hash); + return (B_TRUE); +} + +char * +einfo_get_string(bblk_einfo_t *einfo) +{ + if (einfo == NULL) + return (NULL); + + return ((char *)einfo + einfo->str_off); +} + +char * +einfo_get_hash(bblk_einfo_t *einfo) +{ + if (einfo == NULL) + return (NULL); + + return ((char *)einfo + einfo->hash_off); +} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/bblk_einfo.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/bblk_einfo.h Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,94 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _BBLKEINFO_H +#define _BBLKEINFO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#define BBLK_EINFO_VERSION (1) + +#define EINFO_MAGIC "EXTINFO" +#define EINFO_MAGIC_SIZE (7) + +#pragma pack(1) +typedef struct _extended_info { + char magic[EINFO_MAGIC_SIZE]; + uint8_t version; + uint8_t flags; + uint32_t str_off; + uint16_t str_size; + uint8_t hash_type; + uint32_t hash_off; + uint16_t hash_size; + char rsvd[32]; +} bblk_einfo_t; +#pragma pack() + +enum bblk_hash_types_t { + BBLK_NO_HASH = 0, + BBLK_HASH_MD5, + BBLK_HASH_TOT +}; + +#define EINFO_PRINT_HEADER 0x01 +#define EINFO_EASY_PARSE 0x02 + +typedef struct _hashing_function { + unsigned int type; + unsigned int size; + char name[16]; + void (*compute_hash)(void *, const void *, unsigned int); +} bblk_hash_t; + +typedef struct _hashing_source { + unsigned char *src_buf; + unsigned int src_size; +} bblk_hs_t; + +#define BBLK_DEFAULT_HASH BBLK_HASH_MD5 + +extern bblk_hash_t bblk_no_hash; +extern bblk_hash_t bblk_md5_hash; +extern bblk_hash_t *bblk_hash_list[BBLK_HASH_TOT]; + +void print_einfo(uint8_t, bblk_einfo_t *, unsigned long); +int prepare_and_write_einfo(unsigned char *, char *, bblk_hs_t *, + uint32_t, uint32_t *); +boolean_t einfo_should_update(bblk_einfo_t *, bblk_hs_t *, char *); +char *einfo_get_string(bblk_einfo_t *); +char *einfo_get_hash(bblk_einfo_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* _BBLKEINFO_H */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/boot_utils.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/boot_utils.c Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,116 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include "bblk_einfo.h" +#include "boot_utils.h" + +boolean_t boot_debug = B_FALSE; +boolean_t nowrite = B_FALSE; + +void +boot_gdebug(const char *funcname, char *format, ...) +{ + va_list ap; + + if (boot_debug == B_FALSE) + return; + + (void) fprintf(stdout, "%s(): ", funcname); + + va_start(ap, format); + /* LINTED: E_SEC_PRINTF_VAR_FMT */ + (void) vfprintf(stdout, format, ap); + va_end(ap); +} + +/* + * Common functions to write out and read in block-sized data to a file + * descriptor. + */ +int +write_out(int fd, void *buffer, size_t size, off_t off) +{ + int ret; + char *buf = buffer; + + if (size % SECTOR_SIZE != 0) + BOOT_DEBUG("Expected block-sized data, got: %d\n", size); + + /* Dry run. */ + if (nowrite) + return (BC_SUCCESS); + + for (;;) { + again: + ret = pwrite(fd, buf, size, off); + if (ret == -1) { + if (errno == EAGAIN) + goto again; + else + return (BC_ERROR); + } + if (ret < size) { + size -= ret; + off += ret; + buf += ret; + } else { + break; + } + } + return (BC_SUCCESS); +} + +int +read_in(int fd, void *buffer, size_t size, off_t off) +{ + int ret; + char *buf = buffer; + + if (size % SECTOR_SIZE != 0) + BOOT_DEBUG("Expected block-sized data, got: %d\n", size); + + for (;;) { + again: + ret = pread(fd, buf, size, off); + if (ret == -1) { + if (errno == EAGAIN) + goto again; + else + return (BC_ERROR); + } + if (ret < size) { + size -= ret; + off += ret; + buf += ret; + } else { + break; + } + } + return (BC_SUCCESS); +} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/boot_utils.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/boot_utils.h Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,59 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _BOOT_UTILS_H +#define _BOOT_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "bblk_einfo.h" + +/* Common return values for various operations. */ +#define BC_SUCCESS (0) +#define BC_ERROR (1) +#define BC_NOUPDT (4) +#define BC_NOEXTRA (5) +#define BC_NOEINFO (6) + +#define SECTOR_SIZE (512) + +extern boolean_t boot_debug; +extern boolean_t nowrite; + +#define BOOT_DEBUG(...) boot_gdebug(__func__, __VA_ARGS__) + +void boot_gdebug(const char *, char *, ...); + +int write_out(int, void *, size_t, off_t); +int read_in(int, void *, size_t, off_t); + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOT_UTILS_H */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/mboot_extra.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/mboot_extra.c Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,180 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "bblk_einfo.h" +#include "boot_utils.h" +#include "mboot_extra.h" + +/* + * Common functions to deal with the fake-multiboot encapsulation of the + * bootblock and the location of the extra information area. + */ + +/* mboot checksum routine. */ +uint32_t +compute_checksum(char *data, uint32_t size) +{ + uint32_t *ck_ptr; + uint32_t cksum = 0; + int i; + + ck_ptr = (uint32_t *)data; + for (i = 0; i < size; i += sizeof (uint32_t)) + cksum += *ck_ptr++; + + return (-cksum); +} + +/* Given a buffer, look for a multiboot header within it. */ +int +find_multiboot(char *buffer, uint32_t buf_size, uint32_t *mboot_off) +{ + multiboot_header_t *mboot; + uint32_t *iter; + uint32_t cksum; + uint32_t boundary; + int i = 0; + + iter = (uint32_t *)buffer; + *mboot_off = 0; + /* multiboot header has to be within the first 32K. */ + boundary = MBOOT_SCAN_SIZE; + if (boundary > buf_size) + boundary = buf_size; + + boundary = boundary - sizeof (multiboot_header_t); + + for (i = 0; i < boundary; i += 4, iter++) { + + mboot = (multiboot_header_t *)iter; + if (mboot->magic != MB_HEADER_MAGIC) + continue; + + /* Found magic signature -- check checksum. */ + cksum = -(mboot->flags + mboot->magic); + if (mboot->checksum != cksum) { + BOOT_DEBUG("multiboot magic found at %p, but checksum " + "mismatches (is %x, should be %x)\n", mboot, + mboot->checksum, cksum); + continue; + } else { + if (!(mboot->flags & BB_MBOOT_AOUT_FLAG)) { + BOOT_DEBUG("multiboot structure found, but no " + "AOUT kludge specified, skipping.\n"); + continue; + } else { + /* proper multiboot structure found. */ + *mboot_off = i; + return (BC_SUCCESS); + } + } + } + + return (BC_ERROR); +} + +/* + * Given a pointer to the extra information area (a sequence of bb_header_ext_t + * + payload chunks), find the extended information structure. + */ +bblk_einfo_t * +find_einfo(char *extra) +{ + bb_header_ext_t *ext_header; + bblk_einfo_t *einfo; + uint32_t cksum; + + assert(extra != NULL); + + ext_header = (bb_header_ext_t *)extra; + cksum = compute_checksum(extra + sizeof (bb_header_ext_t), + ext_header->size); + BOOT_DEBUG("Extended information header checksum is %x\n", cksum); + + if (cksum != ext_header->checksum) { + BOOT_DEBUG("Unable to find extended versioning information, " + "data looks corrupted\n"); + return (NULL); + } + + /* + * Currently we only have one extra header so it must be encapsulating + * the extended information structure. + */ + einfo = (bblk_einfo_t *)(extra + sizeof (bb_header_ext_t)); + if (memcmp(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0) { + BOOT_DEBUG("Unable to read stage2 extended versioning " + "information, wrong magic identifier\n"); + BOOT_DEBUG("Found %s, expected %s\n", einfo->magic, + EINFO_MAGIC); + return (NULL); + } + + return (einfo); +} + +/* + * Given a pointer to the extra area, add the extended information structure + * encapsulated by a bb_header_ext_t structure. + */ +void +add_einfo(char *extra, char *updt_str, bblk_hs_t *hs, uint32_t avail_space) +{ + bb_header_ext_t *ext_hdr; + uint32_t used_space; + unsigned char *dest; + int ret; + + assert(extra != NULL); + + if (updt_str == NULL) { + BOOT_DEBUG("WARNING: no update string passed to " + "add_stage2_einfo()\n"); + return; + } + + /* Reserve space for the extra header. */ + ext_hdr = (bb_header_ext_t *)extra; + dest = (unsigned char *)extra + sizeof (*ext_hdr); + /* Place the extended information structure. */ + ret = prepare_and_write_einfo(dest, updt_str, hs, avail_space, + &used_space); + if (ret != 0) { + (void) fprintf(stderr, gettext("Unable to write the extended " + "versioning information\n")); + return; + } + + /* Fill the extended information associated header. */ + ext_hdr->size = P2ROUNDUP(used_space, 8); + ext_hdr->checksum = compute_checksum((char *)dest, ext_hdr->size); +} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/common/mboot_extra.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/common/mboot_extra.h Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,58 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _MBOOT_EXTRA_H +#define _MBOOT_EXTRA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "bblk_einfo.h" + +/* multiboot header needs to be located in the first 32KB. */ +#define MBOOT_SCAN_SIZE (32 * 1024 * 1024) + +/* multiboot header AOUT_KLUDGE flag. */ +#define BB_MBOOT_AOUT_FLAG (0x00010000) + +/* Extra header preceeding the payloads at the end of the bootblock. */ +typedef struct _bb_extra_header { + uint32_t size; + uint32_t checksum; +} bb_header_ext_t; + +uint32_t compute_checksum(char *, uint32_t); +bblk_einfo_t *find_einfo(char *); +int find_multiboot(char *, uint32_t, uint32_t *); +void add_einfo(char *, char *, bblk_hs_t *, uint32_t); +int compare_bootblocks(char *, char *, char **); + +#ifdef __cplusplus +} +#endif + +#endif /* _MBOOT_EXTRA_H */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installboot/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/installboot/Makefile Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,68 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# + +PROG= installboot + +EINFO_SRC= ./../common/bblk_einfo.c +UTILS_SRC =./../common/boot_utils.c +EXTRA_SRC =./../common/mboot_extra.c + +OBJS= installboot.o bblk_einfo.o boot_utils.o mboot_extra.o +SRCS= installboot.c $(UTILS_SRC) $(EINFO_SRC) $(EXTRA_SRC) + +include ../Makefile.com + +CPPFLAGS += -I$(SRC)/uts/common + +LDLIBS += -lmd5 + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +LINTFLAGS += -erroff=E_BAD_PTR_CAST_ALIGN + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) -o $@ $(OBJS) $(LDLIBS) + $(POST_PROCESS) + +boot_utils.o: $(UTILS_SRC) + $(COMPILE.c) -o $@ $(UTILS_SRC) + +mboot_extra.o: $(EXTRA_SRC) + $(COMPILE.c) -o $@ $(EXTRA_SRC) + +bblk_einfo.o: $(EINFO_SRC) + $(COMPILE.c) -o $@ $(EINFO_SRC) + +install: all $(ROOTUSRSBINPROG) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +include ../Makefile.targ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installboot/installboot.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/installboot/installboot.c Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,949 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "installboot.h" +#include "./../common/bblk_einfo.h" +#include "./../common/boot_utils.h" +#include "./../common/mboot_extra.h" + +#ifndef TEXT_DOMAIN +#define TEXT_DOMAIN "SUNW_OST_OSCMD" +#endif + +/* + * SPARC bootblock installation: + * + * The bootblock resides in blocks 1 to 15 (disk label is at block 0). + * The ZFS boot block is larger than what will fit into these first 7.5K so we + * break it up and write the remaining portion into the ZFS provided boot block + * region at offset 512K. If versioning is requested, we add a multiboot + * header at the end of the bootblock, followed by the extra payload area and + * place the extended information structure within the latter. + */ + +static boolean_t force_update = B_FALSE; +static boolean_t do_getinfo = B_FALSE; +static boolean_t do_version = B_FALSE; +static boolean_t do_mirror_bblk = B_FALSE; +static boolean_t strip = B_FALSE; +static boolean_t verbose_dump = B_FALSE; + +static char *update_str; +static int tgt_fs_type = TARGET_IS_UFS; +char mboot_scan[MBOOT_SCAN_SIZE]; + +/* Function prototypes. */ +static int read_bootblock_from_file(char *, ib_data_t *data); +static int read_bootblock_from_disk(int, ib_bootblock_t *); +static void add_bootblock_einfo(ib_bootblock_t *, char *); +static int prepare_bootblock(ib_data_t *, char *); +static int write_zfs_bootblock(ib_data_t *); +static int write_bootblock(ib_data_t *); +static int open_device(ib_device_t *); +static int init_device(ib_device_t *, char *); +static void cleanup_device(ib_device_t *); +static int commit_to_disk(ib_data_t *, char *); +static int handle_install(char *, char **); +static int handle_getinfo(char *, char **); +static int handle_mirror(char *, char **); +static boolean_t is_update_necessary(ib_data_t *, char *); +static int propagate_bootblock(ib_data_t *, ib_data_t *, char *); +static void usage(char *); + +static int +read_bootblock_from_file(char *file, ib_data_t *data) +{ + ib_device_t *device = &data->device; + ib_bootblock_t *bblock = &data->bootblock; + struct stat sb; + uint32_t buf_size; + int fd = -1; + int retval = BC_ERROR; + + assert(data != NULL); + assert(file != NULL); + + fd = open(file, O_RDONLY); + if (fd == -1) { + BOOT_DEBUG("Error opening %s\n", file); + perror("open"); + goto out; + } + + if (fstat(fd, &sb) == -1) { + BOOT_DEBUG("Error getting information (stat) about %s", file); + perror("stat"); + goto outfd; + } + + bblock->file_size = sb.st_size; + BOOT_DEBUG("bootblock file size is %x\n", bblock->file_size); + + /* UFS and HSFS bootblocks need to fit in the reserved 7.5K. */ + if (!is_zfs(device->type)) { + buf_size = P2ROUNDUP(bblock->file_size, SECTOR_SIZE); + if (buf_size > BBLK_DATA_RSVD_SIZE) { + BOOT_DEBUG("boot block size is bigger than allowed\n"); + goto outfd; + } + } else { + buf_size = P2ROUNDUP(bblock->file_size + SECTOR_SIZE, + SECTOR_SIZE); + if (buf_size > BBLK_DATA_RSVD_SIZE + MBOOT_SCAN_SIZE) { + (void) fprintf(stderr, gettext("WARNING, bootblock size" + " does not allow to place extended versioning " + "information.. skipping\n")); + do_version = B_FALSE; + } + } + + bblock->buf_size = buf_size; + BOOT_DEBUG("bootblock in-memory buffer size is %x\n", + bblock->buf_size); + + bblock->buf = malloc(buf_size); + if (bblock->buf == NULL) { + perror(gettext("Memory allocation failure")); + goto outbuf; + } + bblock->file = bblock->buf; + + if (read(fd, bblock->file, bblock->file_size) != bblock->file_size) { + BOOT_DEBUG("Read from %s failed\n", file); + perror("read"); + goto outfd; + } + + /* If not on ZFS, we are done here. */ + if (!is_zfs(device->type)) { + BOOT_DEBUG("Reading of the bootblock done\n"); + retval = BC_SUCCESS; + goto outfd; + } + /* + * We place the multiboot header right after the file, followed by + * the extended information structure. + */ + bblock->mboot = (multiboot_header_t *)(bblock->file + + P2ROUNDUP(bblock->file_size, 8)); + bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t); + BOOT_DEBUG("mboot at %p, extra at %p, buf=%p (size=%d)\n", + bblock->mboot, bblock->extra, bblock->buf, bblock->buf_size); + + (void) close(fd); + return (BC_SUCCESS); + +outbuf: + (void) free(bblock->buf); + bblock->buf = NULL; +outfd: + (void) close(fd); +out: + return (retval); +} + +static int +read_bootblock_from_disk(int dev_fd, ib_bootblock_t *bblock) +{ + char *dest; + uint32_t size; + uint32_t buf_size; + uint32_t mboot_off; + multiboot_header_t *mboot; + + assert(bblock != NULL); + assert(dev_fd != -1); + + /* + * The ZFS bootblock is divided in two parts, but the fake multiboot + * header can only be in the second part (the one contained in the ZFS + * reserved area). + */ + if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), + BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) { + BOOT_DEBUG("Error reading ZFS reserved area\n"); + perror("read"); + return (BC_ERROR); + } + + /* No multiboot means no chance of knowing bootblock size */ + if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off) + != BC_SUCCESS) { + BOOT_DEBUG("Unable to find multiboot header\n"); + return (BC_NOEXTRA); + } + mboot = (multiboot_header_t *)(mboot_scan + mboot_off); + + /* + * Currently, the amount of space reserved for extra information + * is "fixed". We may have to scan for the terminating extra payload + * in the future. + */ + size = mboot->load_end_addr - mboot->load_addr; + buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE); + bblock->file_size = size; + + bblock->buf = malloc(buf_size); + if (bblock->buf == NULL) { + BOOT_DEBUG("Unable to allocate enough memory to read" + " the extra bootblock from the disk\n"); + perror(gettext("Memory allocation failure")); + return (BC_ERROR); + } + bblock->buf_size = buf_size; + + dest = bblock->buf; + size = BBLK_DATA_RSVD_SIZE; + + if (read_in(dev_fd, dest, size, SECTOR_SIZE) != BC_SUCCESS) { + BOOT_DEBUG("Error reading first %d bytes of the bootblock\n", + size); + (void) free(bblock->buf); + bblock->buf = NULL; + return (BC_ERROR); + } + + dest += BBLK_DATA_RSVD_SIZE; + size = bblock->buf_size - BBLK_DATA_RSVD_SIZE; + + if (read_in(dev_fd, dest, size, BBLK_ZFS_EXTRA_OFF) != BC_SUCCESS) { + BOOT_DEBUG("Error reading ZFS reserved area the second time\n"); + (void) free(bblock->buf); + bblock->buf = NULL; + return (BC_ERROR); + } + + /* Update pointers. */ + bblock->file = bblock->buf; + bblock->mboot_off = mboot_off; + bblock->mboot = (multiboot_header_t *)(bblock->buf + bblock->mboot_off + + BBLK_DATA_RSVD_SIZE); + bblock->extra = (char *)bblock->mboot + sizeof (multiboot_header_t); + return (BC_SUCCESS); +} + +static boolean_t +is_update_necessary(ib_data_t *data, char *updt_str) +{ + bblk_einfo_t *einfo; + bblk_hs_t bblock_hs; + ib_bootblock_t bblock_disk; + ib_bootblock_t *bblock_file = &data->bootblock; + ib_device_t *device = &data->device; + int dev_fd = device->fd; + + assert(data != NULL); + assert(device->fd != -1); + + /* Nothing to do if we are not updating a ZFS bootblock. */ + if (!is_zfs(device->type)) + return (B_TRUE); + + bzero(&bblock_disk, sizeof (ib_bootblock_t)); + + if (read_bootblock_from_disk(dev_fd, &bblock_disk) != BC_SUCCESS) { + BOOT_DEBUG("Unable to read bootblock from %s\n", device->path); + return (B_TRUE); + } + + einfo = find_einfo(bblock_disk.extra); + if (einfo == NULL) { + BOOT_DEBUG("No extended information available\n"); + return (B_TRUE); + } + + if (!do_version || updt_str == NULL) { + (void) fprintf(stdout, "WARNING: target device %s has a " + "versioned bootblock that is going to be overwritten by a " + "non versioned one\n", device->path); + return (B_TRUE); + } + + if (force_update) { + BOOT_DEBUG("Forcing update of %s bootblock\n", device->path); + return (B_TRUE); + } + + BOOT_DEBUG("Ready to check installed version vs %s\n", updt_str); + + bblock_hs.src_buf = (unsigned char *)bblock_file->file; + bblock_hs.src_size = bblock_file->file_size; + + return (einfo_should_update(einfo, &bblock_hs, updt_str)); +} + +static void +add_bootblock_einfo(ib_bootblock_t *bblock, char *updt_str) +{ + bblk_hs_t hs; + uint32_t avail_space; + + assert(bblock != NULL); + + if (updt_str == NULL) { + BOOT_DEBUG("WARNING: no update string passed to " + "add_bootblock_einfo()\n"); + return; + } + + /* Fill bootblock hashing source information. */ + hs.src_buf = (unsigned char *)bblock->file; + hs.src_size = bblock->file_size; + /* How much space for the extended information structure? */ + avail_space = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8); + /* Place the extended information structure. */ + add_einfo(bblock->extra, updt_str, &hs, avail_space); +} + + +static int +prepare_bootblock(ib_data_t *data, char *updt_str) +{ + ib_device_t *device = &data->device; + ib_bootblock_t *bblock = &data->bootblock; + multiboot_header_t *mboot; + + assert(data != NULL); + + /* Nothing to do if we are not on ZFS. */ + if (!is_zfs(device->type)) + return (BC_SUCCESS); + + /* + * Write the fake multiboot structure followed by the extra information + * data. Both mboot and extra pointers have already been filled up to + * point to the right location in the buffer. We prepare the fake + * multiboot regardless if versioning was requested or not because + * we need it for mirroring support. + */ + assert(bblock->mboot != NULL); + assert(bblock->extra != NULL); + + mboot = bblock->mboot; + + mboot->magic = MB_HEADER_MAGIC; + mboot->flags = MB_HEADER_FLAGS_64; + mboot->checksum = -(mboot->flags + mboot->magic); + /* + * Flags include the AOUT_KLUDGE and we use the extra members to specify + * the size of the bootblock. + */ + mboot->header_addr = bblock->mboot_off; + mboot->load_addr = 0; + mboot->load_end_addr = bblock->file_size; + + /* + * Now that we have the mboot header in place, we can add the extended + * versioning information. Since the multiboot header has been placed + * after the file image, the hashing will still reflect the one of the + * file on the disk. + */ + if (do_version) + add_bootblock_einfo(bblock, updt_str); + + return (BC_SUCCESS); +} + +static int +write_zfs_bootblock(ib_data_t *data) +{ + ib_device_t *device = &data->device; + ib_bootblock_t *bblock = &data->bootblock; + char *bufptr; + uint32_t size; + + assert(data != NULL); + assert(device->fd != -1); + + /* + * In the ZFS case we actually perform two different steps: + * - write the first 15 blocks of the bootblock to the reserved disk + * blocks. + * - write the remaining blocks in the ZFS reserved area at offset + * 512K. + */ + bufptr = bblock->buf; + size = BBLK_DATA_RSVD_SIZE; + + if (write_out(device->fd, bufptr, size, SECTOR_SIZE) != BC_SUCCESS) { + BOOT_DEBUG("Error writing first 15 blocks of %s\n", + device->path); + perror("write"); + return (BC_ERROR); + } + + bufptr += BBLK_DATA_RSVD_SIZE; + size = bblock->buf_size - BBLK_DATA_RSVD_SIZE; + + if (write_out(device->fd, bufptr, size, BBLK_ZFS_EXTRA_OFF) + != BC_SUCCESS) { + BOOT_DEBUG("Error writing the second part of ZFS bootblock " + "to %s at offset %d\n", device->path, BBLK_ZFS_EXTRA_OFF); + return (BC_ERROR); + } + return (BC_SUCCESS); +} + +static int +write_bootblock(ib_data_t *data) +{ + ib_device_t *device = &data->device; + ib_bootblock_t *bblock = &data->bootblock; + int ret; + + assert(data != NULL); + + /* + * If we are on UFS or HSFS we simply write out to the reserved + * blocks (1 to 15) the boot block. + */ + if (!is_zfs(device->type)) { + if (write_out(device->fd, bblock->buf, bblock->buf_size, + SECTOR_SIZE) != BC_SUCCESS) { + BOOT_DEBUG("Error writing bootblock to %s\n", + device->path); + return (BC_ERROR); + } else { + return (BC_SUCCESS); + } + } else { + ret = write_zfs_bootblock(data); + return (ret); + } +} + +static int +open_device(ib_device_t *device) +{ + struct stat statbuf; + + device->fd = open(device->path, O_RDWR); + if (device->fd == -1) { + BOOT_DEBUG("Unable to open %s\n", device->path); + perror("open"); + return (BC_ERROR); + } + + if (fstat(device->fd, &statbuf) != 0) { + BOOT_DEBUG("Unable to stat %s\n", device->path); + perror("stat"); + (void) close(device->fd); + return (BC_ERROR); + } + + if (S_ISCHR(statbuf.st_mode) == 0) { + (void) fprintf(stderr, gettext("%s: Not a character device\n"), + device->path); + return (BC_ERROR); + } + + return (BC_SUCCESS); +} + +static int +init_device(ib_device_t *device, char *path) +{ + bzero(device, sizeof (*device)); + device->fd = -1; + + device->path = strdup(path); + if (path == NULL) { + perror(gettext("Memory allocation failure")); + return (BC_ERROR); + } + + device->type = tgt_fs_type; + if (open_device(device) != BC_SUCCESS) + return (BC_ERROR); + + return (BC_SUCCESS); +} + +static void +cleanup_device(ib_device_t *device) +{ + free(device->path); + bzero(device, sizeof (*device)); + + if (device->fd != -1) + (void) close(device->fd); +} + +static void +cleanup_bootblock(ib_bootblock_t *bblock) +{ + free(bblock->buf); + bzero(bblock, sizeof (ib_bootblock_t)); +} + +/* + * Propagate the bootblock on the source disk to the destination disk and + * version it with 'updt_str' in the process. Since we cannot trust any data + * on the attaching disk, we do not perform any specific check on a potential + * target extended information structure and we just blindly update. + */ +static int +propagate_bootblock(ib_data_t *src, ib_data_t *dest, char *updt_str) +{ + ib_bootblock_t *src_bblock = &src->bootblock; + ib_bootblock_t *dest_bblock = &dest->bootblock; + uint32_t buf_size; + + assert(src != NULL); + assert(dest != NULL); + + cleanup_bootblock(dest_bblock); + + if (updt_str != NULL) { + do_version = B_TRUE; + } else { + do_version = B_FALSE; + } + + buf_size = src_bblock->file_size + SECTOR_SIZE; + + dest_bblock->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE); + dest_bblock->buf = malloc(dest_bblock->buf_size); + if (dest_bblock->buf == NULL) { + perror(gettext("Memory Allocation Failure")); + return (BC_ERROR); + } + dest_bblock->file = dest_bblock->buf; + dest_bblock->file_size = src_bblock->file_size; + (void) memcpy(dest_bblock->file, src_bblock->file, + dest_bblock->file_size); + + dest_bblock->mboot = (multiboot_header_t *)(dest_bblock->file + + P2ROUNDUP(dest_bblock->file_size, 8)); + dest_bblock->extra = (char *)dest_bblock->mboot + + sizeof (multiboot_header_t); + + (void) fprintf(stdout, gettext("Propagating %s bootblock to %s\n"), + src->device.path, dest->device.path); + + return (commit_to_disk(dest, updt_str)); +} + +static int +commit_to_disk(ib_data_t *data, char *update_str) +{ + assert(data != NULL); + + if (prepare_bootblock(data, update_str) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error updating the bootblock " + "image\n")); + return (BC_ERROR); + } + + if (write_bootblock(data) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error writing bootblock to " + "disk\n")); + return (BC_ERROR); + } + + return (BC_SUCCESS); +} + + +/* + * Install a new bootblock on the given device. handle_install() expects argv + * to contain 2 parameters (the target device path and the path to the + * bootblock. + * + * Returns: BC_SUCCESS - if the installation is successful + * BC_ERROR - if the installation failed + * BC_NOUPDT - if no installation was performed because the + * version currently installed is more recent than the + * supplied one. + * + */ +static int +handle_install(char *progname, char **argv) +{ + ib_data_t install_data; + char *bootblock = NULL; + char *device_path = NULL; + int ret = BC_ERROR; + + bootblock = strdup(argv[0]); + device_path = strdup(argv[1]); + + if (!device_path || !bootblock) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + + BOOT_DEBUG("device path: %s, bootblock file path: %s\n", device_path, + bootblock); + bzero(&install_data, sizeof (ib_data_t)); + + if (init_device(&install_data.device, device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to open device %s\n"), + device_path); + goto out; + } + + if (read_bootblock_from_file(bootblock, &install_data) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error reading %s\n"), + bootblock); + goto out_dev; + } + /* Versioning is only supported for the ZFS bootblock. */ + if (do_version && !is_zfs(install_data.device.type)) { + (void) fprintf(stderr, gettext("Versioning is only supported on" + " ZFS... skipping.\n")); + do_version = B_FALSE; + } + + /* + * is_update_necessary() will take care of checking if versioning and/or + * forcing the update have been specified. It will also emit a warning + * if a non-versioned update is attempted over a versioned bootblock. + */ + if (!is_update_necessary(&install_data, update_str)) { + (void) fprintf(stderr, gettext("bootblock version installed " + "on %s is more recent or identical\n" + "Use -F to override or install without the -u option\n"), + device_path); + ret = BC_NOUPDT; + goto out_dev; + } + + BOOT_DEBUG("Ready to commit to disk\n"); + ret = commit_to_disk(&install_data, update_str); + +out_dev: + cleanup_device(&install_data.device); +out: + free(bootblock); + free(device_path); + return (ret); +} + +/* + * Retrieves from a device the extended information (einfo) associated to the + * installed bootblock. + * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0. + * Returns: + * - BC_SUCCESS (and prints out einfo contents depending on 'flags') + * - BC_ERROR (on error) + * - BC_NOEINFO (no extended information available) + */ +static int +handle_getinfo(char *progname, char **argv) +{ + + ib_data_t data; + ib_bootblock_t *bblock = &data.bootblock; + ib_device_t *device = &data.device; + bblk_einfo_t *einfo; + uint8_t flags = 0; + uint32_t size; + char *device_path; + int retval = BC_ERROR; + int ret; + + device_path = strdup(argv[0]); + if (!device_path) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + + bzero(&data, sizeof (ib_data_t)); + BOOT_DEBUG("device path: %s\n", device_path); + + if (init_device(device, device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information from %s\n"), device_path); + goto out_dev; + } + + if (!is_zfs(device->type)) { + (void) fprintf(stderr, gettext("Versioning only supported on " + "ZFS\n")); + goto out_dev; + } + + ret = read_bootblock_from_disk(device->fd, bblock); + if (ret == BC_ERROR) { + (void) fprintf(stderr, gettext("Error reading bootblock from " + "%s\n"), device_path); + goto out_dev; + } + + if (ret == BC_NOEXTRA) { + BOOT_DEBUG("No multiboot header found on %s, unable " + "to locate extra information area (old/non versioned " + "bootblock?) \n", device_path); + (void) fprintf(stderr, gettext("No extended information " + "found\n")); + retval = BC_NOEINFO; + goto out_dev; + } + + einfo = find_einfo(bblock->extra); + if (einfo == NULL) { + retval = BC_NOEINFO; + (void) fprintf(stderr, gettext("No extended information " + "found\n")); + goto out_dev; + } + + /* Print the extended information. */ + if (strip) + flags |= EINFO_EASY_PARSE; + if (verbose_dump) + flags |= EINFO_PRINT_HEADER; + + size = bblock->buf_size - P2ROUNDUP(bblock->file_size, 8) - + sizeof (multiboot_header_t); + print_einfo(flags, einfo, size); + retval = BC_SUCCESS; + +out_dev: + cleanup_device(&data.device); +out: + free(device_path); + return (retval); + +} + +/* + * Attempt to mirror (propagate) the current bootblock over the attaching disk. + * + * Returns: + * - BC_SUCCESS (a successful propagation happened) + * - BC_ERROR (an error occurred) + * - BC_NOEXTRA (it is not possible to dump the current bootblock since + * there is no multiboot information) + */ +static int +handle_mirror(char *progname, char **argv) +{ + ib_data_t curr_data; + ib_data_t attach_data; + ib_device_t *curr_device = &curr_data.device; + ib_device_t *attach_device = &attach_data.device; + ib_bootblock_t *bblock_curr = &curr_data.bootblock; + ib_bootblock_t *bblock_attach = &attach_data.bootblock; + bblk_einfo_t *einfo_curr = NULL; + char *curr_device_path; + char *attach_device_path; + char *updt_str = NULL; + int retval = BC_ERROR; + int ret; + + curr_device_path = strdup(argv[0]); + attach_device_path = strdup(argv[1]); + + if (!curr_device_path || !attach_device_path) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + BOOT_DEBUG("Current device path is: %s, attaching device path is: " + " %s\n", curr_device_path, attach_device_path); + + bzero(&curr_data, sizeof (ib_data_t)); + bzero(&attach_data, sizeof (ib_data_t)); + + if (tgt_fs_type != TARGET_IS_ZFS) { + (void) fprintf(stderr, gettext("Mirroring is only supported on " + "ZFS\n")); + return (BC_ERROR); + } + + if (init_device(curr_device, curr_device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information from %s (current device)\n"), + curr_device_path); + goto out_currdev; + } + + if (init_device(attach_device, attach_device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information from %s (attaching device)\n"), + attach_device_path); + goto out_devs; + } + + ret = read_bootblock_from_disk(curr_device->fd, bblock_curr); + if (ret == BC_ERROR) { + BOOT_DEBUG("Error reading bootblock from %s\n", + curr_device->path); + retval = BC_ERROR; + goto out_devs; + } + + if (ret == BC_NOEXTRA) { + BOOT_DEBUG("No multiboot header found on %s, unable to retrieve" + " the bootblock\n", curr_device->path); + retval = BC_NOEXTRA; + goto out_devs; + } + + einfo_curr = find_einfo(bblock_curr->extra); + if (einfo_curr != NULL) + updt_str = einfo_get_string(einfo_curr); + + retval = propagate_bootblock(&curr_data, &attach_data, updt_str); + cleanup_bootblock(bblock_curr); + cleanup_bootblock(bblock_attach); +out_devs: + cleanup_device(attach_device); +out_currdev: + cleanup_device(curr_device); +out: + free(curr_device_path); + free(attach_device_path); + return (retval); +} + +#define USAGE_STRING "Usage: %s [-h|-f|-F fstype|-u verstr] bootblk " \ + "raw-device\n" \ + "\t%s [-e|-V] -i -F zfs raw-device\n" \ + "\t%s -M -F zfs raw-device attach-raw-device\n" \ + "\tfstype is one of: 'ufs', 'hsfs' or 'zfs'\n" + +#define CANON_USAGE_STR gettext(USAGE_STRING) + +static void +usage(char *progname) +{ + (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname); +} + +int +main(int argc, char **argv) +{ + int opt; + int params = 2; + int ret; + char *progname; + char **handle_args; + + (void) setlocale(LC_ALL, ""); + (void) textdomain(TEXT_DOMAIN); + + while ((opt = getopt(argc, argv, "F:efiVMndhu:")) != EOF) { + switch (opt) { + case 'F': + if (strcmp(optarg, "ufs") == 0) { + tgt_fs_type = TARGET_IS_UFS; + } else if (strcmp(optarg, "hsfs") == 0) { + tgt_fs_type = TARGET_IS_HSFS; + } else if (strcmp(optarg, "zfs") == 0) { + tgt_fs_type = TARGET_IS_ZFS; + } else { + (void) fprintf(stderr, gettext("Wrong " + "filesystem specified\n\n")); + usage(argv[0]); + exit(BC_ERROR); + } + break; + case 'e': + strip = B_TRUE; + break; + case 'f': + force_update = B_TRUE; + break; + case 'V': + verbose_dump = B_TRUE; + break; + case 'i': + do_getinfo = B_TRUE; + params = 1; + break; + case 'u': + do_version = B_TRUE; + + update_str = malloc(strlen(optarg) + 1); + if (update_str == NULL) { + perror(gettext("Memory allocation failure")); + exit(BC_ERROR); + } + (void) strlcpy(update_str, optarg, strlen(optarg) + 1); + break; + case 'M': + do_mirror_bblk = B_TRUE; + break; + case 'h': + usage(argv[0]); + exit(BC_SUCCESS); + break; + case 'd': + boot_debug = B_TRUE; + break; + case 'n': + nowrite = B_TRUE; + break; + default: + /* fall through to process non-optional args */ + break; + } + } + + /* check arguments */ + if (argc != optind + params) { + usage(argv[0]); + exit(BC_ERROR); + } + progname = argv[0]; + handle_args = argv + optind; + + /* check options. */ + if (do_getinfo && do_mirror_bblk) { + (void) fprintf(stderr, gettext("Only one of -M and -i can be " + "specified at the same time\n")); + usage(progname); + exit(BC_ERROR); + } + + if (nowrite) + (void) fprintf(stdout, gettext("Dry run requested. Nothing will" + " be written to disk.\n")); + + if (do_getinfo) { + ret = handle_getinfo(progname, handle_args); + } else if (do_mirror_bblk) { + ret = handle_mirror(progname, handle_args); + } else { + ret = handle_install(progname, handle_args); + } + return (ret); +} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installboot/installboot.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/installboot/installboot.h Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _INSTALLBOOT_H +#define _INSTALLBOOT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +enum ib_fs_types { + TARGET_IS_UFS = 0, + TARGET_IS_HSFS, + TARGET_IS_ZFS +}; + +typedef struct _ib_device { + char *path; + int fd; + uint8_t type; +} ib_device_t; + +typedef struct _ib_bootblock { + char *buf; + char *file; + char *extra; + multiboot_header_t *mboot; + uint32_t mboot_off; + uint32_t buf_size; + uint32_t file_size; +} ib_bootblock_t; + +typedef struct _ib_data { + ib_device_t device; + ib_bootblock_t bootblock; +} ib_data_t; + +#define is_zfs(type) (type == TARGET_IS_ZFS) + +#define BBLK_DATA_RSVD_SIZE (15 * SECTOR_SIZE) +#define BBLK_ZFS_EXTRA_OFF (SECTOR_SIZE * 1024) + +#ifdef __cplusplus +} +#endif + +#endif /* _INSTALLBOOT_H */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installgrub/Makefile --- a/usr/src/cmd/boot/installgrub/Makefile Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/cmd/boot/installgrub/Makefile Wed Jul 28 15:51:57 2010 -0700 @@ -18,17 +18,19 @@ # # CDDL HEADER END # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# +# Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. # PROG= installgrub PCFS_SRC= $(SRC)/common/fs/pcfs.c +EINFO_SRC= ./../common/bblk_einfo.c +UTILS_SRC =./../common/boot_utils.c +EXTRA_SRC =./../common/mboot_extra.c -OBJS= installgrub.o floppy.o pcfs.o -SRCS= installgrub.c floppy.c $(PCFS_SRC) +OBJS= installgrub.o pcfs_glue.o pcfs.o bblk_einfo.o boot_utils.o mboot_extra.o +SRCS= installgrub.c pcfs_glue.c $(PCFS_SRC) $(UTILS_SRC) $(EINFO_SRC) \ + $(EXTRA_SRC) SBINLINKS= $(PROG) include ../Makefile.com @@ -49,6 +51,9 @@ -erroff=E_FUNC_RET_MAYBE_IGNORED2 \ -xerroff=E_NAME_DEF_NOT_USED2 +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + .KEEP_STATE: all: $(PROG) @@ -62,6 +67,15 @@ pcfs.o: $(PCFS_SRC) $(COMPILE.c) -o $@ $(PCFS_SRC) +boot_utils.o: $(UTILS_SRC) + $(COMPILE.c) -o $@ $(UTILS_SRC) + +mboot_extra.o: $(EXTRA_SRC) + $(COMPILE.c) -o $@ $(EXTRA_SRC) + +bblk_einfo.o: $(EINFO_SRC) + $(COMPILE.c) -o $@ $(EINFO_SRC) + install: all $(ROOTSBINPROG) .WAIT $(ROOTUSRSBINLINKS) clean: diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installgrub/floppy.c --- a/usr/src/cmd/boot/installgrub/floppy.c Wed Jul 28 17:47:31 2010 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,214 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "message.h" - -/* - * This file is glue layer to pcfs module in usr/src/common/fs/pcfs.c. - * It's main functionality is to get the stage file blocklist. It's - * used for installing grub on floppy and Solaris boot partition. - */ -extern struct boot_fs_ops bpcfs_ops; -struct boot_fs_ops *bfs_ops; -struct boot_fs_ops *bfs_tab[] = {&bpcfs_ops, NULL}; -static int dev_fd; -int bootrd_debug = 0; - -#define DEV_BSIZE 512 -#define MAX_CHUNK 64 - -static unsigned int *blocklist; - -/* diskread_callback is set in filesytem module (pcfs.c) */ -int (*diskread_callback)(int, int); -int (*fileread_callback)(int, int); - -static int -add_stage2_block(int blocknum, int nblk) -{ - static int i = -2; - - if (i >= 0 && (blocklist[i] + blocklist[i + 1] == blocknum)) { - blocklist[i + 1] += nblk; - return (0); - } - - i += 2; - if (i >= DEV_BSIZE / 8) { - fprintf(stderr, PCFS_FRAGMENTED); - exit(-1); - } - blocklist[i] = blocknum; - blocklist[i + 1] = nblk; - return (0); -} - -/* - * This one reads the ramdisk. If fi_memp is set, we copy the - * ramdisk content to the designated buffer. Otherwise, we - * do a "cached" read (set fi_memp to the actual ramdisk buffer). - */ -int -diskread(fileid_t *filep) -{ - int ret; - uint_t blocknum, diskloc; - - blocknum = filep->fi_blocknum; - - if (diskread_callback) { - diskread_callback(blocknum, filep->fi_count / DEV_BSIZE); - return (0); - } - - diskloc = blocknum * DEV_BSIZE; - if (filep->fi_memp == NULL) { - filep->fi_memp = malloc(filep->fi_count); - } - if (filep->fi_memp == NULL) { - fprintf(stderr, OUT_OF_MEMORY); - return (-1); - } - - ret = pread(dev_fd, filep->fi_memp, filep->fi_count, diskloc); - if (ret < 0) - perror("diskread: pread"); - return (ret >= 0 ? 0 : -1); -} - -void * -bkmem_alloc(size_t s) -{ - return (malloc(s)); -} - -/*ARGSUSED*/ -void -bkmem_free(void *p, size_t s) -{ - free(p); -} - -static int -mountroot(char *name) -{ - int i; - - /* try ops in bfs_tab and return the first successful one */ - for (i = 0; bfs_tab[i] != NULL; i++) { - bfs_ops = bfs_tab[i]; - if (BRD_MOUNTROOT(bfs_ops, name) == 0) - return (0); - } - return (-1); -} - -static int -unmountroot() -{ - return (BRD_UNMOUNTROOT(bfs_ops)); -} - -static int -floppy_open(const char *filename, int flags) -{ - return (BRD_OPEN(bfs_ops, (char *)filename, flags)); -} - -static int -floppy_close(int fd) -{ - return (BRD_CLOSE(bfs_ops, fd)); -} - -static ssize_t -floppy_read(int fd, void *buf, size_t size) -{ - return (BRD_READ(bfs_ops, fd, buf, size)); -} - -static off_t -floppy_lseek(int fd, off_t addr, int whence) -{ - return (BRD_SEEK(bfs_ops, fd, addr, whence)); -} - -/* - * Get the blocklist for stage2 - */ -int -read_stage2_blocklist(int device_fd, unsigned int *blkbuf) -{ - int i, fd, stage2_block; - char buf[DEV_BSIZE]; - ssize_t size; - - dev_fd = device_fd; - if (mountroot("dummy") != 0) { - fprintf(stderr, MOUNT_FAIL_PCFS); - return (-1); - } - - if ((fd = floppy_open("/boot/grub/stage2", 0)) == -1) { - fprintf(stderr, OPEN_FAIL_PCFS); - return (-1); - } - - if (bootrd_debug) - (void) printf("start reading stage2:\n"); - stage2_block = 0; - blocklist = blkbuf; - fileread_callback = add_stage2_block; - for (;;) { - size = floppy_read(fd, buf, DEV_BSIZE); - if (size != DEV_BSIZE) - break; - stage2_block++; - } - fileread_callback = NULL; - (void) floppy_close(fd); - - if (bootrd_debug) { - (void) printf("last block size = %d\n", size); - for (i = 0; blocklist[i] != 0; i += 2) { - (void) printf("sectors: %d-%d\n", - blocklist[i], - blocklist[i] + blocklist[i + 1] - 1); - } - (void) printf("total blocks in stage 2: %d\n", stage2_block); - } - - (void) unmountroot(); - return (0); -} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installgrub/installgrub.c --- a/usr/src/cmd/boot/installgrub/installgrub.c Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/cmd/boot/installgrub/installgrub.c Wed Jul 28 15:51:57 2010 -0700 @@ -19,8 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. */ #include @@ -28,122 +27,154 @@ #include #include #include -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include + #include #include #include #include #include +#include +#include +#include +#include -#include -#include #include "message.h" -#include -#include -#include +#include "installgrub.h" +#include "./../common/bblk_einfo.h" +#include "./../common/boot_utils.h" +#include "./../common/mboot_extra.h" #ifndef TEXT_DOMAIN #define TEXT_DOMAIN "SUNW_OST_OSCMD" #endif -#define SECTOR_SIZE 0x200 -#define HASH_SIZE 0x10 -#define VERSION_SIZE 0x50 -#define STAGE2_MEMADDR 0x8000 /* loading addr of stage2 */ +/* + * Variables to track installgrub desired mode of operation. + * 'nowrite' and 'boot_debug' come from boot_common.h. + */ +static boolean_t write_mbr = B_FALSE; +static boolean_t force_mbr = B_FALSE; +static boolean_t force_update = B_FALSE; +static boolean_t do_getinfo = B_FALSE; +static boolean_t do_version = B_FALSE; +static boolean_t do_mirror_bblk = B_FALSE; +static boolean_t strip = B_FALSE; +static boolean_t verbose_dump = B_FALSE; -#define STAGE1_BPB_OFFSET 0x3 -#define STAGE1_BPB_SIZE 0x3B -#define STAGE1_BOOT_DRIVE 0x40 -#define STAGE1_FORCE_LBA 0x41 -#define STAGE1_STAGE2_ADDRESS 0x42 -#define STAGE1_STAGE2_SECTOR 0x44 -#define STAGE1_STAGE2_SEGMENT 0x48 +/* Installing the bootblock is the default operation. */ +static boolean_t do_install = B_TRUE; + +/* Versioning string, if present. */ +static char *update_str; -#define STAGE2_BLOCKLIST (SECTOR_SIZE - 0x8) -#define STAGE2_INSTALLPART (SECTOR_SIZE + 0x8) -#define STAGE2_FORCE_LBA (SECTOR_SIZE + 0x11) -#define STAGE2_VER_STRING (SECTOR_SIZE + 0x12) -#define STAGE2_SIGN_OFFSET (SECTOR_SIZE + 0x60) -#define STAGE2_PKG_VERSION (SECTOR_SIZE + 0x70) -#define STAGE2_BLKOFF 50 /* offset from start of fdisk part */ - -static char extended_sig[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB" -"\xBB\xBB\xBB\xBB"; +/* + * Temporary buffer to store the first 32K of data looking for a multiboot + * signature. + */ +char mboot_scan[MBOOT_SCAN_SIZE]; -static int nowrite = 0; -static int write_mboot = 0; -static int force_mboot = 0; -static int getinfo = 0; -static int do_version = 0; -static int is_floppy = 0; -static int is_bootpar = 0; -static int strip = 0; -static int stage2_fd; -static int partition, slice = 0xff; -static char *device_p0; -static uint32_t stage2_first_sector, stage2_second_sector; - - -static char bpb_sect[SECTOR_SIZE]; -static char boot_sect[SECTOR_SIZE]; -static char stage1_buffer[SECTOR_SIZE]; -static char stage2_buffer[2 * SECTOR_SIZE]; -static char signature[HASH_SIZE]; -static char verstring[VERSION_SIZE]; -static unsigned int blocklist[SECTOR_SIZE / sizeof (unsigned int)]; - -static int open_device(char *); -static void read_bpb_sect(int); -static void read_boot_sect(char *); -static void write_boot_sect(char *); -static void read_stage1_stage2(char *, char *); -static void modify_and_write_stage1(int); -static void modify_and_write_stage2(int); -static unsigned int get_start_sector(int); -static void copy_stage2(int, char *); -static char *get_raw_partition(char *); +/* Function prototypes. */ +static void check_options(char *); +static int handle_install(char *, char **); +static int handle_mirror(char *, char **); +static int handle_getinfo(char *, char **); +static int commit_to_disk(ig_data_t *, char *); +static int init_device(ig_device_t *, char *path); +static void cleanup_device(ig_device_t *); +static void cleanup_stage2(ig_stage2_t *); +static int get_start_sector(ig_device_t *); +static int get_disk_fd(ig_device_t *device); +static int get_raw_partition_fd(ig_device_t *); +static char *get_raw_partition_path(ig_device_t *); +static boolean_t gather_stage2_from_dev(ig_data_t *); +static int propagate_bootblock(ig_data_t *, ig_data_t *, char *); +static int find_x86_bootpar(struct mboot *, int *, uint32_t *); +static int copy_stage2_to_pcfs(ig_data_t *); +static int write_stage2(ig_data_t *); +static int write_stage1(ig_data_t *); static void usage(char *); -static void print_info(); -static int read_stage2_info(int); -static void check_extended_support(); +static int read_stage1_from_file(char *, ig_data_t *); +static int read_stage2_from_file(char *, ig_data_t *); +static int read_stage1_from_disk(int, char *); +static int read_stage2_from_disk(int, ig_stage2_t *); +static int prepare_stage1(ig_data_t *); +static int prepare_stage2(ig_data_t *, char *); +static void prepare_fake_multiboot(ig_stage2_t *); +static void add_stage2_einfo(ig_stage2_t *, char *updt_str); +static boolean_t is_update_necessary(ig_data_t *, char *); extern int read_stage2_blocklist(int, unsigned int *); int main(int argc, char *argv[]) { - int dev_fd, opt, params = 3; - char *stage1, *stage2, *device; + int opt; + int params = 3; + int ret; + char **handle_args; + char *progname; (void) setlocale(LC_ALL, ""); (void) textdomain(TEXT_DOMAIN); - while ((opt = getopt(argc, argv, "fmneis:")) != EOF) { + /* + * retro-compatibility: installing the bootblock is the default + * and there is no switch for it. + */ + do_install = B_TRUE; + + while ((opt = getopt(argc, argv, "dVMFfmneiu:")) != EOF) { switch (opt) { case 'm': - write_mboot = 1; + write_mbr = B_TRUE; break; case 'n': - nowrite = 1; + nowrite = B_TRUE; break; case 'f': - force_mboot = 1; + force_mbr = B_TRUE; break; case 'i': - getinfo = 1; + do_getinfo = B_TRUE; + do_install = B_FALSE; params = 1; break; + case 'V': + verbose_dump = B_TRUE; + break; + case 'd': + boot_debug = B_TRUE; + break; + case 'F': + force_update = B_TRUE; + break; case 'e': - strip = 1; + strip = B_TRUE; + break; + case 'M': + do_mirror_bblk = B_TRUE; + do_install = B_FALSE; + params = 2; break; - case 's': - do_version = 1; - (void) snprintf(verstring, sizeof (verstring), "%s", - optarg); + case 'u': + do_version = B_TRUE; + + update_str = malloc(strlen(optarg) + 1); + if (update_str == NULL) { + (void) fprintf(stderr, gettext("Unable to " + "allocate memory\n")); + exit(BC_ERROR); + } + (void) strlcpy(update_str, optarg, strlen(optarg) + 1); break; default: /* fall through to process non-optional args */ @@ -154,121 +185,543 @@ /* check arguments */ if (argc != optind + params) { usage(argv[0]); - } - - if (nowrite) { - (void) fprintf(stdout, DRY_RUN); + exit(BC_ERROR); } - if (params == 1) { - device = strdup(argv[optind]); - if (!device) { - usage(argv[0]); + /* + * clean up options (and bail out if an unrecoverable combination is + * requested. + */ + progname = argv[0]; + check_options(progname); + handle_args = argv + optind; + + if (nowrite) + (void) fprintf(stdout, DRY_RUN); + + if (do_getinfo) { + ret = handle_getinfo(progname, handle_args); + } else if (do_mirror_bblk) { + ret = handle_mirror(progname, handle_args); + } else { + ret = handle_install(progname, handle_args); + } + return (ret); +} + +#define MEANINGLESS_OPT gettext("%s specified but meaningless, ignoring\n") +static void +check_options(char *progname) +{ + if (do_getinfo && do_mirror_bblk) { + (void) fprintf(stderr, gettext("Only one of -M and -i can be " + "specified at the same time\n")); + usage(progname); + exit(BC_ERROR); + } + + if (do_mirror_bblk) { + /* + * -u and -F may actually reflect a user intent that is not + * correct with this command (mirror can be interpreted + * "similar" to install. Emit a message and continue. + * -e and -V have no meaning, be quiet here and only report the + * incongruence if a debug output is requested. + */ + if (do_version) { + (void) fprintf(stderr, MEANINGLESS_OPT, "-u"); + do_version = B_FALSE; } - } else if (params == 3) { - stage1 = strdup(argv[optind]); - stage2 = strdup(argv[optind + 1]); - device = strdup(argv[optind + 2]); - - if (!stage1 || !stage2 || !device) { - usage(argv[0]); + if (force_update) { + (void) fprintf(stderr, MEANINGLESS_OPT, "-F"); + force_update = B_FALSE; + } + if (strip || verbose_dump) { + BOOT_DEBUG(MEANINGLESS_OPT, "-e|-V"); + strip = B_FALSE; + verbose_dump = B_FALSE; } } - /* open and check device type */ - dev_fd = open_device(device); + if (do_getinfo) { + if (write_mbr || force_mbr || do_version || force_update) { + BOOT_DEBUG(MEANINGLESS_OPT, "-m|-f|-u|-F"); + write_mbr = force_mbr = do_version = B_FALSE; + force_update = B_FALSE; + } + } +} + +/* + * Install a new stage1/stage2 pair on the specified device. handle_install() + * expects argv to contain 3 parameters (the path to stage1, the path to stage2, + * the target device). + * + * Returns: BC_SUCCESS - if the installation is successful + * BC_ERROR - if the installation failed + * BC_NOUPDT - if no installation was performed because the GRUB + * version currently installed is more recent than the + * supplied one. + * + */ +static int +handle_install(char *progname, char **argv) +{ + ig_data_t install_data; + char *stage1_path = NULL; + char *stage2_path = NULL; + char *device_path = NULL; + int ret = BC_ERROR; - if (getinfo) { - if (read_stage2_info(dev_fd) != 0) { - fprintf(stderr, "Unable to read extended information" - " from %s\n", device); - exit(1); - } - print_info(); - (void) free(device); - (void) close(dev_fd); - return (0); + stage1_path = strdup(argv[0]); + stage2_path = strdup(argv[1]); + device_path = strdup(argv[2]); + + bzero(&install_data, sizeof (ig_data_t)); + + if (!stage1_path || !stage2_path || !device_path) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + + BOOT_DEBUG("stage1 path: %s, stage2 path: %s, device: %s\n", + stage1_path, stage2_path, device_path); + + if (init_device(&install_data.device, device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information for %s\n"), device_path); + goto out; + } + + /* read in stage1 and stage2. */ + if (read_stage1_from_file(stage1_path, &install_data) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error opening %s\n"), + stage1_path); + goto out_dev; + } + + if (read_stage2_from_file(stage2_path, &install_data) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error opening %s\n"), + stage2_path); + goto out_dev; } - /* read in stage1 and stage2 into buffer */ - read_stage1_stage2(stage1, stage2); + /* We do not support versioning on PCFS. */ + if (is_bootpar(install_data.device.type) && do_version) + do_version = B_FALSE; + + /* + * is_update_necessary() will take care of checking if versioning and/or + * forcing the update have been specified. It will also emit a warning + * if a non-versioned update is attempted over a versioned bootblock. + */ + if (!is_update_necessary(&install_data, update_str)) { + (void) fprintf(stderr, gettext("GRUB version installed " + "on %s is more recent or identical\n" + "Use -F to override or install without the -u option\n"), + device_path); + ret = BC_NOUPDT; + goto out_dev; + } + /* + * We get here if: + * - the installed GRUB version is older than the one about to be + * installed. + * - no versioning string has been passed through the command line. + * - a forced update is requested (-F). + */ + BOOT_DEBUG("Ready to commit to disk\n"); + ret = commit_to_disk(&install_data, update_str); + +out_dev: + cleanup_device(&install_data.device); +out: + free(stage1_path); + free(stage2_path); + free(device_path); + return (ret); +} + +/* + * Retrieves from a device the extended information (einfo) associated to the + * installed stage2. + * Expects one parameter, the device path, in the form: /dev/rdsk/c?[t?]d?s0. + * Returns: + * - BC_SUCCESS (and prints out einfo contents depending on 'flags') + * - BC_ERROR (on error) + * - BC_NOEINFO (no extended information available) + */ +static int +handle_getinfo(char *progname, char **argv) +{ + ig_data_t data; + ig_stage2_t *stage2 = &data.stage2; + ig_device_t *device = &data.device; + bblk_einfo_t *einfo; + uint8_t flags = 0; + uint32_t size; + char *device_path; + int retval = BC_ERROR; + int ret; + + device_path = strdup(argv[0]); + if (!device_path) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + + bzero(&data, sizeof (ig_data_t)); + BOOT_DEBUG("device path: %s\n", device_path); + + if (init_device(device, device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information for %s\n"), device_path); + goto out_dev; + } + + if (is_bootpar(device->type)) { + (void) fprintf(stderr, gettext("Versioning not supported on " + "PCFS\n")); + goto out_dev; + } - /* check if stage2 supports extended versioning */ - if (do_version) - check_extended_support(stage2); + ret = read_stage2_from_disk(device->part_fd, stage2); + if (ret == BC_ERROR) { + (void) fprintf(stderr, gettext("Error reading stage2 from " + "%s\n"), device_path); + goto out_dev; + } + + if (ret == BC_NOEXTRA) { + (void) fprintf(stdout, gettext("No multiboot header found on " + "%s, unable to locate extra information area\n"), + device_path); + retval = BC_NOEINFO; + goto out_dev; + } + + einfo = find_einfo(stage2->extra); + if (einfo == NULL) { + retval = BC_NOEINFO; + (void) fprintf(stderr, gettext("No extended information " + "found\n")); + goto out_dev; + } + + /* Print the extended information. */ + if (strip) + flags |= EINFO_EASY_PARSE; + if (verbose_dump) + flags |= EINFO_PRINT_HEADER; + + size = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8); + print_einfo(flags, einfo, size); + retval = BC_SUCCESS; + +out_dev: + cleanup_device(&data.device); +out: + free(device_path); + return (retval); +} - /* In the pcfs case, write a fresh stage2 */ - if (is_floppy || is_bootpar) { - copy_stage2(dev_fd, device); - read_bpb_sect(dev_fd); +/* + * Attempt to mirror (propagate) the current stage2 over the attaching disk. + * + * Returns: + * - BC_SUCCESS (a successful propagation happened) + * - BC_ERROR (an error occurred) + * - BC_NOEXTRA (it is not possible to dump the current bootblock since + * there is no multiboot information) + */ +static int +handle_mirror(char *progname, char **argv) +{ + ig_data_t curr_data; + ig_data_t attach_data; + ig_device_t *curr_device = &curr_data.device; + ig_device_t *attach_device = &attach_data.device; + ig_stage2_t *stage2_curr = &curr_data.stage2; + ig_stage2_t *stage2_attach = &attach_data.stage2; + bblk_einfo_t *einfo_curr = NULL; + char *curr_device_path; + char *attach_device_path; + char *updt_str = NULL; + int retval = BC_ERROR; + int ret; + + curr_device_path = strdup(argv[0]); + attach_device_path = strdup(argv[1]); + + if (!curr_device_path || !attach_device_path) { + (void) fprintf(stderr, gettext("Missing parameter")); + usage(progname); + goto out; + } + BOOT_DEBUG("Current device path is: %s, attaching device path is: " + " %s\n", curr_device_path, attach_device_path); + + bzero(&curr_data, sizeof (ig_data_t)); + bzero(&attach_data, sizeof (ig_data_t)); + + if (init_device(curr_device, curr_device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information for %s (current device)\n"), curr_device_path); + goto out_currdev; } - /* read in boot sector */ - if (!is_floppy) - read_boot_sect(device); + if (init_device(attach_device, attach_device_path) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Unable to gather device " + "information for %s (attaching device)\n"), + attach_device_path); + goto out_devs; + } + + if (is_bootpar(curr_device->type) || is_bootpar(attach_device->type)) { + (void) fprintf(stderr, gettext("boot block mirroring is not " + "supported on PCFS\n")); + goto out_devs; + } - /* modify stage1 based on grub needs */ - modify_and_write_stage1(dev_fd); + ret = read_stage2_from_disk(curr_device->part_fd, stage2_curr); + if (ret == BC_ERROR) { + BOOT_DEBUG("Error reading first stage2 blocks from %s\n", + curr_device->path); + retval = BC_ERROR; + goto out_devs; + } - /* modify stage2 and write to media */ - modify_and_write_stage2(dev_fd); + if (ret == BC_NOEXTRA) { + BOOT_DEBUG("No multiboot header found on %s, unable to grab " + "stage2\n", curr_device->path); + retval = BC_NOEXTRA; + goto out_devs; + } + + einfo_curr = find_einfo(stage2_curr->extra); + if (einfo_curr != NULL) + updt_str = einfo_get_string(einfo_curr); + + write_mbr = B_TRUE; + force_mbr = B_TRUE; + retval = propagate_bootblock(&curr_data, &attach_data, updt_str); + cleanup_stage2(stage2_curr); + cleanup_stage2(stage2_attach); - if (!is_floppy && write_mboot) - write_boot_sect(device); +out_devs: + cleanup_device(attach_device); +out_currdev: + cleanup_device(curr_device); +out: + free(curr_device_path); + free(attach_device_path); + return (retval); +} - (void) close(dev_fd); - free(device); - free(stage1); - free(stage2); +static int +commit_to_disk(ig_data_t *install, char *updt_str) +{ + assert(install != NULL); + /* + * vanilla stage1 and stage2 need to be updated at runtime. + * Update stage2 before stage1 because stage1 needs to know the first + * sector stage2 will be written to. + */ + if (prepare_stage2(install, updt_str) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error building stage2\n")); + return (BC_ERROR); + } + if (prepare_stage1(install) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error building stage1\n")); + return (BC_ERROR); + } - return (0); + /* Write stage2 out to disk. */ + if (write_stage2(install) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error writing stage2 to " + "disk\n")); + return (BC_ERROR); + } + + /* Write stage1 to disk and, if requested, to the MBR. */ + if (write_stage1(install) != BC_SUCCESS) { + (void) fprintf(stderr, gettext("Error writing stage1 to " + "disk\n")); + return (BC_ERROR); + } + + return (BC_SUCCESS); } -static unsigned int -get_start_sector(int fd) +/* + * Propagate the bootblock on the source disk to the destination disk and + * version it with 'updt_str' in the process. Since we cannot trust any data + * on the attaching disk, we do not perform any specific check on a potential + * target extended information structure and we just blindly update. + */ +static int +propagate_bootblock(ig_data_t *source, ig_data_t *target, char *updt_str) { - static unsigned int start_sect = 0; - uint32_t secnum = 0, numsec = 0; - int i, pno, rval, log_part = 0; - struct mboot *mboot; - struct ipart *part; - ext_part_t *epp; - struct part_info dkpi; - struct extpart_info edkpi; + ig_device_t *src_device = &source->device; + ig_device_t *dest_device = &target->device; + ig_stage2_t *src_stage2 = &source->stage2; + ig_stage2_t *dest_stage2 = &target->stage2; + uint32_t buf_size; + int retval; + + assert(source != NULL); + assert(target != NULL); + + /* read in stage1 from the source disk. */ + if (read_stage1_from_disk(src_device->part_fd, target->stage1_buf) + != BC_SUCCESS) + return (BC_ERROR); + + /* Prepare target stage2 for commit_to_disk. */ + cleanup_stage2(dest_stage2); + + if (updt_str != NULL) + do_version = B_TRUE; + else + do_version = B_FALSE; + + buf_size = src_stage2->file_size + SECTOR_SIZE; + + dest_stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE); + dest_stage2->buf = malloc(dest_stage2->buf_size); + if (dest_stage2->buf == NULL) { + perror(gettext("Memory allocation failed")); + return (BC_ERROR); + } + dest_stage2->file = dest_stage2->buf; + dest_stage2->file_size = src_stage2->file_size; + memcpy(dest_stage2->file, src_stage2->file, dest_stage2->file_size); + dest_stage2->extra = dest_stage2->buf + + P2ROUNDUP(dest_stage2->file_size, 8); + + /* If we get down here we do have a mboot structure. */ + assert(src_stage2->mboot); + + dest_stage2->mboot_off = src_stage2->mboot_off; + dest_stage2->mboot = (multiboot_header_t *)(dest_stage2->buf + + dest_stage2->mboot_off); + + (void) fprintf(stdout, gettext("Propagating %s stage1/stage2 to %s\n"), + src_device->path, dest_device->path); + retval = commit_to_disk(target, updt_str); + + return (retval); +} + +/* + * open the device and fill the various members of ig_device_t. + */ +static int +init_device(ig_device_t *device, char *path) +{ + bzero(device, sizeof (*device)); + device->part_fd = -1; + device->disk_fd = -1; + device->path_p0 = NULL; + + device->path = strdup(path); + if (device->path == NULL) { + perror(gettext("Memory allocation failed")); + return (BC_ERROR); + } - if (start_sect) - return (start_sect); + if (strstr(device->path, "diskette")) { + (void) fprintf(stderr, gettext("installing GRUB to a floppy " + "disk is no longer supported\n")); + return (BC_ERROR); + } + + /* Detect if the target device is a pcfs partition. */ + if (strstr(device->path, "p0:boot")) + device->type = IG_DEV_X86BOOTPAR; + + if (get_disk_fd(device) != BC_SUCCESS) + return (BC_ERROR); + + /* read in the device boot sector. */ + if (read(device->disk_fd, device->boot_sector, SECTOR_SIZE) + != SECTOR_SIZE) { + (void) fprintf(stderr, gettext("Error reading boot sector\n")); + perror("read"); + return (BC_ERROR); + } + + if (get_raw_partition_fd(device) != BC_SUCCESS) + return (BC_ERROR); + + if (get_start_sector(device) != BC_SUCCESS) + return (BC_ERROR); + + return (BC_SUCCESS); +} + +static void +cleanup_device(ig_device_t *device) +{ + if (device->path) + free(device->path); + if (device->path_p0) + free(device->path_p0); - mboot = (struct mboot *)boot_sect; - for (i = 0; i < FD_NUMPART; i++) { - part = (struct ipart *)mboot->parts + i; - if (is_bootpar) { - if (part->systid == 0xbe) { - start_sect = part->relsect; - partition = i; - goto found_part; - } + if (device->part_fd != -1) + (void) close(device->part_fd); + if (device->disk_fd != -1) + (void) close(device->disk_fd); + + bzero(device, sizeof (ig_device_t)); + device->part_fd = -1; + device->disk_fd = -1; +} + +static void +cleanup_stage2(ig_stage2_t *stage2) +{ + if (stage2->buf) + free(stage2->buf); + bzero(stage2, sizeof (ig_stage2_t)); +} + +static int +get_start_sector(ig_device_t *device) +{ + uint32_t secnum = 0, numsec = 0; + int i, pno, rval, log_part = 0; + struct mboot *mboot; + struct ipart *part; + ext_part_t *epp; + struct part_info dkpi; + struct extpart_info edkpi; + + mboot = (struct mboot *)device->boot_sector; + + if (is_bootpar(device->type)) { + if (find_x86_bootpar(mboot, &pno, &secnum) != BC_SUCCESS) { + (void) fprintf(stderr, NOBOOTPAR); + return (BC_ERROR); + } else { + device->start_sector = secnum; + device->partition = pno; + goto found_part; } } /* - * We will not support x86 boot partition on extended partitions - */ - if (is_bootpar) { - (void) fprintf(stderr, NOBOOTPAR); - exit(-1); - } - - /* - * Not an x86 boot partition. Search for Solaris fdisk partition + * Search for Solaris fdisk partition * Get the solaris partition information from the device * and compare the offset of S2 with offset of solaris partition * from fdisk partition table. */ - if (ioctl(fd, DKIOCEXTPARTINFO, &edkpi) < 0) { - if (ioctl(fd, DKIOCPARTINFO, &dkpi) < 0) { + if (ioctl(device->part_fd, DKIOCEXTPARTINFO, &edkpi) < 0) { + if (ioctl(device->part_fd, DKIOCPARTINFO, &dkpi) < 0) { (void) fprintf(stderr, PART_FAIL); - exit(-1); + return (BC_ERROR); } else { edkpi.p_start = dkpi.p_start; } @@ -279,7 +732,7 @@ if (part->relsect == 0) { (void) fprintf(stderr, BAD_PART, i); - exit(-1); + return (BC_ERROR); } if (edkpi.p_start >= part->relsect && @@ -292,7 +745,7 @@ if (i == FD_NUMPART) { /* No solaris fdisk partitions (primary or logical) */ (void) fprintf(stderr, NOSOLPAR); - exit(-1); + return (BC_ERROR); } /* @@ -300,8 +753,8 @@ * Handle the simple case first: Solaris in a primary partition */ if (!fdisk_is_dos_extended(part->systid)) { - start_sect = part->relsect; - partition = i; + device->start_sector = part->relsect; + device->partition = i; goto found_part; } @@ -309,7 +762,7 @@ * Solaris in a logical partition. Find that partition in the * extended part. */ - if ((rval = libfdisk_init(&epp, device_p0, NULL, FDISK_READ_DISK)) + if ((rval = libfdisk_init(&epp, device->path_p0, NULL, FDISK_READ_DISK)) != FDISK_SUCCESS) { switch (rval) { /* @@ -320,23 +773,19 @@ case FDISK_ENOLOGDRIVE: case FDISK_EBADMAGIC: (void) fprintf(stderr, NOSOLPAR); - exit(-1); - /*NOTREACHED*/ + return (BC_ERROR); case FDISK_ENOVGEOM: (void) fprintf(stderr, NO_VIRT_GEOM); - exit(1); - break; + return (BC_ERROR); case FDISK_ENOPGEOM: (void) fprintf(stderr, NO_PHYS_GEOM); - exit(1); - break; + return (BC_ERROR); case FDISK_ENOLGEOM: (void) fprintf(stderr, NO_LABEL_GEOM); - exit(1); - break; + return (BC_ERROR); default: (void) fprintf(stderr, LIBFDISK_INIT_FAIL); - exit(1); + return (BC_ERROR); break; } } @@ -346,21 +795,21 @@ if (rval != FDISK_SUCCESS) { /* No solaris logical partition */ (void) fprintf(stderr, NOSOLPAR); - exit(-1); + return (BC_ERROR); } - start_sect = secnum; - partition = pno - 1; + device->start_sector = secnum; + device->partition = pno - 1; log_part = 1; found_part: /* get confirmation for -m */ - if (write_mboot && !force_mboot) { + if (write_mbr && !force_mbr) { (void) fprintf(stdout, MBOOT_PROMPT); if (getchar() != 'y') { - write_mboot = 0; + write_mbr = 0; (void) fprintf(stdout, MBOOT_NOT_UPDATED); - exit(-1); + return (BC_ERROR); } } @@ -368,318 +817,463 @@ * Currently if Solaris is in an extended partition we need to * write GRUB to the MBR. Check for this. */ - if (log_part && !write_mboot) { - write_mboot = 1; + if (log_part && !write_mbr) { + (void) fprintf(stdout, gettext("Installing Solaris on an " + "extended partition... forcing MBR update\n")); + write_mbr = 1; } /* * warn, if Solaris in primary partition and GRUB not in MBR and * partition is not active */ - if (!log_part && part->bootid != 128 && !write_mboot) { - (void) fprintf(stdout, SOLPAR_INACTIVE, partition + 1); + if (!log_part && part->bootid != 128 && !write_mbr) { + (void) fprintf(stdout, SOLPAR_INACTIVE, device->partition + 1); + } + + return (BC_SUCCESS); +} + +static int +get_disk_fd(ig_device_t *device) +{ + int i; + char save[2]; + char *end = NULL; + + assert(device != NULL); + assert(device->path != NULL); + + if (is_bootpar(device->type)) { + end = strstr(device->path, "p0:boot"); + /* tested at the start of init_device() */ + assert(end != NULL); + /* chop off :boot */ + save[0] = end[2]; + end[2] = '\0'; + } else { + i = strlen(device->path); + save[0] = device->path[i - 2]; + save[1] = device->path[i - 1]; + device->path[i - 2] = 'p'; + device->path[i - 1] = '0'; + } + + if (nowrite) + device->disk_fd = open(device->path, O_RDONLY); + else + device->disk_fd = open(device->path, O_RDWR); + + device->path_p0 = strdup(device->path); + if (device->path_p0 == NULL) { + perror("strdup"); + return (BC_ERROR); + } + + if (is_bootpar(device->type)) { + end[2] = save[0]; + } else { + device->path[i - 2] = save[0]; + device->path[i - 1] = save[1]; + } + + if (device->disk_fd == -1) { + perror("open"); + return (BC_ERROR); } - return (start_sect); + return (BC_SUCCESS); +} + +static void +prepare_fake_multiboot(ig_stage2_t *stage2) +{ + multiboot_header_t *mboot; + + assert(stage2 != NULL); + assert(stage2->mboot != NULL); + assert(stage2->buf != NULL); + + mboot = stage2->mboot; + + /* + * Currently we expect find_multiboot() to have located a multiboot + * header with the AOUT kludge flag set. + */ + assert(mboot->flags & BB_MBOOT_AOUT_FLAG); + + /* Insert the information necessary to locate stage2. */ + mboot->header_addr = stage2->mboot_off; + mboot->load_addr = 0; + mboot->load_end_addr = stage2->file_size; +} + +static void +add_stage2_einfo(ig_stage2_t *stage2, char *updt_str) +{ + bblk_hs_t hs; + uint32_t avail_space; + + assert(stage2 != NULL); + + /* Fill bootblock hashing source information. */ + hs.src_buf = (unsigned char *)stage2->file; + hs.src_size = stage2->file_size; + /* How much space for the extended information structure? */ + avail_space = stage2->buf_size - P2ROUNDUP(stage2->file_size, 8); + add_einfo(stage2->extra, updt_str, &hs, avail_space); } + +static int +write_stage2(ig_data_t *install) +{ + ig_device_t *device = &install->device; + ig_stage2_t *stage2 = &install->stage2; + off_t offset; + + assert(install != NULL); + + if (is_bootpar(device->type)) { + /* + * stage2 is already on the filesystem, we only need to update + * the first two blocks (that we have modified during + * prepare_stage2()) + */ + if (write_out(device->part_fd, stage2->file, SECTOR_SIZE, + stage2->pcfs_first_sectors[0] * SECTOR_SIZE) + != BC_SUCCESS || + write_out(device->part_fd, stage2->file + SECTOR_SIZE, + SECTOR_SIZE, stage2->pcfs_first_sectors[1] * SECTOR_SIZE) + != BC_SUCCESS) { + (void) fprintf(stderr, WRITE_FAIL_STAGE2); + return (BC_ERROR); + } + (void) fprintf(stdout, WRITE_STAGE2_PCFS); + return (BC_SUCCESS); + } + + /* + * For disk, write stage2 starting at STAGE2_BLKOFF sector. + * Note that we use stage2->buf rather than stage2->file, because we + * may have extended information after the latter. + */ + offset = STAGE2_BLKOFF * SECTOR_SIZE; + if (write_out(device->part_fd, stage2->buf, stage2->buf_size, + offset) != BC_SUCCESS) { + perror("write"); + return (BC_ERROR); + } + + /* Simulate the "old" installgrub output. */ + (void) fprintf(stdout, WRITE_STAGE2_DISK, device->partition, + (stage2->buf_size / SECTOR_SIZE) + 1, STAGE2_BLKOFF, + stage2->first_sector); + + return (BC_SUCCESS); +} + +static int +write_stage1(ig_data_t *install) +{ + ig_device_t *device = &install->device; + + assert(install != NULL); + + if (write_out(device->part_fd, install->stage1_buf, + sizeof (install->stage1_buf), 0) != BC_SUCCESS) { + (void) fprintf(stdout, WRITE_FAIL_PBOOT); + perror("write"); + return (BC_ERROR); + } + + /* Simulate "old" installgrub output. */ + (void) fprintf(stdout, WRITE_PBOOT, device->partition, + device->start_sector); + + if (write_mbr) { + if (write_out(device->disk_fd, install->stage1_buf, + sizeof (install->stage1_buf), 0) != BC_SUCCESS) { + (void) fprintf(stdout, WRITE_FAIL_BOOTSEC); + perror("write"); + return (BC_ERROR); + } + /* Simulate "old" installgrub output. */ + (void) fprintf(stdout, WRITE_MBOOT); + } + + return (BC_SUCCESS); +} + +#define USAGE_STRING "%s [-m|-f|-n|-F|-u verstr] stage1 stage2 device\n" \ + "%s -M [-n] device1 device2\n" \ + "%s [-V|-e] -i device\n" \ + +#define CANON_USAGE_STR gettext(USAGE_STRING) + static void usage(char *progname) { - (void) fprintf(stderr, USAGE, basename(progname)); - exit(-1); -} - -static int -open_device(char *device) -{ - int dev_fd; - struct stat stat; - char *raw_part; - - is_floppy = strncmp(device, "/dev/rdsk", strlen("/dev/rdsk")) && - strncmp(device, "/dev/dsk", strlen("/dev/dsk")); - - /* handle boot partition specification */ - if (!is_floppy && strstr(device, "p0:boot")) { - is_bootpar = 1; - } - - raw_part = get_raw_partition(device); - - if (nowrite) - dev_fd = open(raw_part, O_RDONLY); - else - dev_fd = open(raw_part, O_RDWR); - - if (dev_fd == -1 || fstat(dev_fd, &stat) != 0) { - (void) fprintf(stderr, OPEN_FAIL, raw_part); - exit(-1); - } - if (S_ISCHR(stat.st_mode) == 0) { - (void) fprintf(stderr, NOT_RAW_DEVICE, raw_part); - exit(-1); - } - - return (dev_fd); -} - -static void -read_stage1_stage2(char *stage1, char *stage2) -{ - int fd; - - /* read the stage1 file from filesystem */ - fd = open(stage1, O_RDONLY); - if (fd == -1 || read(fd, stage1_buffer, SECTOR_SIZE) != SECTOR_SIZE) { - (void) fprintf(stderr, READ_FAIL_STAGE1, stage1); - exit(-1); - } - (void) close(fd); - - /* read first two blocks of stage 2 from filesystem */ - stage2_fd = open(stage2, O_RDONLY); - if (stage2_fd == -1 || - read(stage2_fd, stage2_buffer, 2 * SECTOR_SIZE) - != 2 * SECTOR_SIZE) { - (void) fprintf(stderr, READ_FAIL_STAGE2, stage2); - exit(-1); - } - /* leave the stage2 file open for later */ -} - -static void -read_bpb_sect(int dev_fd) -{ - if (pread(dev_fd, bpb_sect, SECTOR_SIZE, 0) != SECTOR_SIZE) { - (void) fprintf(stderr, READ_FAIL_BPB); - exit(-1); - } -} - -static void -read_boot_sect(char *device) -{ - static int read_mbr = 0; - int i, fd; - char save[2]; - - if (read_mbr) - return; - read_mbr = 1; - - /* get the whole disk (p0) */ - i = strlen(device); - save[0] = device[i - 2]; - save[1] = device[i - 1]; - device[i - 2] = 'p'; - device[i - 1] = '0'; - - device_p0 = strdup(device); - fd = open(device, O_RDONLY); - if (fd == -1 || read(fd, boot_sect, SECTOR_SIZE) != SECTOR_SIZE) { - (void) fprintf(stderr, READ_FAIL_MBR, device); - if (fd == -1) - perror("open"); - else - perror("read"); - exit(-1); - } - (void) close(fd); - device[i - 2] = save[0]; - device[i - 1] = save[1]; -} - -static void -write_boot_sect(char *device) -{ - int fd, len; - char *raw, *end; - struct stat stat; - - /* make a copy and chop off ":boot" */ - raw = strdup(device); - end = strstr(raw, "p0:boot"); - if (end) - end[2] = 0; - - /* open p0 (whole disk) */ - len = strlen(raw); - raw[len - 2] = 'p'; - raw[len - 1] = '0'; - fd = open(raw, O_WRONLY); - if (fd == -1 || fstat(fd, &stat) != 0) { - (void) fprintf(stderr, OPEN_FAIL, raw); - exit(-1); - } - if (!nowrite && - pwrite(fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { - (void) fprintf(stderr, WRITE_FAIL_BOOTSEC); - exit(-1); - } - (void) fprintf(stdout, WRITE_MBOOT); - (void) close(fd); -} - -static void -modify_and_write_stage1(int dev_fd) -{ - if (is_floppy) { - stage2_first_sector = blocklist[0]; - /* copy bios parameter block (for fat fs) */ - bcopy(bpb_sect + STAGE1_BPB_OFFSET, - stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); - } else if (is_bootpar) { - stage2_first_sector = get_start_sector(dev_fd) + blocklist[0]; - /* copy bios parameter block (for fat fs) and MBR */ - bcopy(bpb_sect + STAGE1_BPB_OFFSET, - stage1_buffer + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); - bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); - *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; - } else { - stage2_first_sector = get_start_sector(dev_fd) + STAGE2_BLKOFF; - /* copy MBR to stage1 in case of overwriting MBR sector */ - bcopy(boot_sect + BOOTSZ, stage1_buffer + BOOTSZ, 512 - BOOTSZ); - *((unsigned char *)(stage1_buffer + STAGE1_FORCE_LBA)) = 1; - } - - /* modify default stage1 file generated by GRUB */ - *((ulong_t *)(stage1_buffer + STAGE1_STAGE2_SECTOR)) - = stage2_first_sector; - *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_ADDRESS)) - = STAGE2_MEMADDR; - *((ushort_t *)(stage1_buffer + STAGE1_STAGE2_SEGMENT)) - = STAGE2_MEMADDR >> 4; - - /* - * XXX the default grub distribution also: - * - Copy the possible MBR/extended part table - * - Set the boot drive of stage1 - */ - - /* write stage1/pboot to 1st sector */ - if (!nowrite && - pwrite(dev_fd, stage1_buffer, SECTOR_SIZE, 0) != SECTOR_SIZE) { - (void) fprintf(stderr, WRITE_FAIL_PBOOT); - exit(-1); - } - - if (is_floppy) { - (void) fprintf(stdout, WRITE_BOOTSEC_FLOPPY); - } else { - (void) fprintf(stdout, WRITE_PBOOT, - partition, get_start_sector(dev_fd)); - } -} - -static void check_extended_support(char *stage2) -{ - char *cmp = stage2_buffer + STAGE2_SIGN_OFFSET - 1; - - if ((*cmp++ != '\xEE') && memcmp(cmp, extended_sig, HASH_SIZE) != 0) { - fprintf(stderr, "%s does not support extended versioning\n", - stage2); - do_version = 0; - } -} - - -static void print_info() -{ - int i; - - if (strip) { - fprintf(stdout, "%s\n", verstring); - } else { - fprintf(stdout, "Grub extended version information : %s\n", - verstring); - fprintf(stdout, "Grub stage2 (MD5) signature : "); - } - - for (i = 0; i < HASH_SIZE; i++) - fprintf(stdout, "%02x", (unsigned char)signature[i]); - - fprintf(stdout, "\n"); -} - -static int -read_stage2_info(int dev_fd) -{ - int ret; - int first_offset, second_offset; - char *sign; - - if (is_floppy || is_bootpar) { - - ret = pread(dev_fd, stage1_buffer, SECTOR_SIZE, 0); - if (ret != SECTOR_SIZE) { - perror("Error reading stage1 sector"); - return (1); - } - - first_offset = *((ulong_t *)(stage1_buffer + - STAGE1_STAGE2_SECTOR)); - - /* Start reading in the first sector of stage 2 */ - - ret = pread(dev_fd, stage2_buffer, SECTOR_SIZE, first_offset * - SECTOR_SIZE); - if (ret != SECTOR_SIZE) { - perror("Error reading stage2 first sector"); - return (1); - } - - /* From the block list section grab stage2 second sector */ - - second_offset = *((ulong_t *)(stage2_buffer + - STAGE2_BLOCKLIST)); - - ret = pread(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE, - second_offset * SECTOR_SIZE); - if (ret != SECTOR_SIZE) { - perror("Error reading stage2 second sector"); - return (1); - } - } else { - ret = pread(dev_fd, stage2_buffer, 2 * SECTOR_SIZE, - STAGE2_BLKOFF * SECTOR_SIZE); - if (ret != 2 * SECTOR_SIZE) { - perror("Error reading stage2 sectors"); - return (1); - } - } - - sign = stage2_buffer + STAGE2_SIGN_OFFSET - 1; - if (*sign++ != '\xEE') - return (1); - (void) memcpy(signature, sign, HASH_SIZE); - sign = stage2_buffer + STAGE2_PKG_VERSION; - (void) strncpy(verstring, sign, VERSION_SIZE); - return (0); + (void) fprintf(stdout, CANON_USAGE_STR, progname, progname, progname); } static int -compute_and_write_md5hash(char *dest) +read_stage1_from_file(char *path, ig_data_t *dest) { + int fd; + + assert(dest); + + /* read the stage1 file from filesystem */ + fd = open(path, O_RDONLY); + if (fd == -1 || + read(fd, dest->stage1_buf, SECTOR_SIZE) != SECTOR_SIZE) { + (void) fprintf(stderr, READ_FAIL_STAGE1, path); + return (BC_ERROR); + } + (void) close(fd); + return (BC_SUCCESS); +} + +static int +read_stage2_from_file(char *path, ig_data_t *dest) +{ + int fd; struct stat sb; - char *buffer; + ig_stage2_t *stage2 = &dest->stage2; + ig_device_t *device = &dest->device; + uint32_t buf_size; + + assert(dest); + assert(stage2->buf == NULL); + + fd = open(path, O_RDONLY); + if (fstat(fd, &sb) == -1) { + perror("fstat"); + goto out; + } + + stage2->file_size = sb.st_size; + + if (!is_bootpar(device->type)) { + /* + * buffer size needs to account for stage2 plus the extra + * versioning information at the end of it. We reserve one + * extra sector (plus we round up to the next sector boundary). + */ + buf_size = stage2->file_size + SECTOR_SIZE; + } else { + /* In the PCFS case we only need to read in stage2. */ + buf_size = stage2->file_size; + } + + stage2->buf_size = P2ROUNDUP(buf_size, SECTOR_SIZE); + + BOOT_DEBUG("stage2 buffer size = %d (%d sectors)\n", stage2->buf_size, + stage2->buf_size / SECTOR_SIZE); + + stage2->buf = malloc(stage2->buf_size); + if (stage2->buf == NULL) { + perror(gettext("Memory allocation failed")); + goto out_fd; + } + + stage2->file = stage2->buf; + + /* + * Extra information (e.g. the versioning structure) is placed at the + * end of stage2, aligned on a 8-byte boundary. + */ + if (!(is_bootpar(device->type))) + stage2->extra = stage2->file + P2ROUNDUP(stage2->file_size, 8); + + if (lseek(fd, 0, SEEK_SET) == -1) { + perror("lseek"); + goto out_alloc; + } - if (fstat(stage2_fd, &sb) == -1) - return (-1); + if (read(fd, stage2->file, stage2->file_size) < 0) { + perror(gettext("unable to read stage2")); + goto out_alloc; + } + + (void) close(fd); + return (BC_SUCCESS); + +out_alloc: + free(stage2->buf); + stage2->buf = NULL; +out_fd: + (void) close(fd); +out: + return (BC_ERROR); +} + +static int +prepare_stage1(ig_data_t *install) +{ + ig_device_t *device = &install->device; + + assert(install != NULL); + + /* If PCFS add the BIOS Parameter Block. */ + if (is_bootpar(device->type)) { + char bpb_sect[SECTOR_SIZE]; + + if (pread(device->part_fd, bpb_sect, SECTOR_SIZE, 0) + != SECTOR_SIZE) { + (void) fprintf(stderr, READ_FAIL_BPB); + return (BC_ERROR); + } + bcopy(bpb_sect + STAGE1_BPB_OFFSET, + install->stage1_buf + STAGE1_BPB_OFFSET, STAGE1_BPB_SIZE); + } + + /* copy MBR to stage1 in case of overwriting MBR sector. */ + bcopy(device->boot_sector + BOOTSZ, install->stage1_buf + BOOTSZ, + SECTOR_SIZE - BOOTSZ); + /* modify default stage1 file generated by GRUB. */ + *((unsigned char *)(install->stage1_buf + STAGE1_FORCE_LBA)) = 1; + *((ulong_t *)(install->stage1_buf + STAGE1_STAGE2_SECTOR)) + = install->stage2.first_sector; + *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_ADDRESS)) + = STAGE2_MEMADDR; + *((ushort_t *)(install->stage1_buf + STAGE1_STAGE2_SEGMENT)) + = STAGE2_MEMADDR >> 4; + + return (BC_SUCCESS); +} - buffer = malloc(sb.st_size); - if (buffer == NULL) - return (-1); +/* + * Grab stage1 from the specified device file descriptor. + */ +static int +read_stage1_from_disk(int dev_fd, char *stage1_buf) +{ + assert(stage1_buf != NULL); + + if (read_in(dev_fd, stage1_buf, SECTOR_SIZE, 0) != BC_SUCCESS) { + perror(gettext("Unable to read stage1 from disk")); + return (BC_ERROR); + } + return (BC_SUCCESS); +} + +static int +read_stage2_from_disk(int dev_fd, ig_stage2_t *stage2) +{ + uint32_t size; + uint32_t buf_size; + uint32_t mboot_off; + multiboot_header_t *mboot; + + assert(stage2 != NULL); + assert(dev_fd != -1); + + if (read_in(dev_fd, mboot_scan, sizeof (mboot_scan), + STAGE2_BLKOFF * SECTOR_SIZE) != BC_SUCCESS) { + perror(gettext("Error reading stage2 sectors")); + return (BC_ERROR); + } + + /* No multiboot means no chance of knowing stage2 size */ + if (find_multiboot(mboot_scan, sizeof (mboot_scan), &mboot_off) + != BC_SUCCESS) { + BOOT_DEBUG("Unable to find multiboot header\n"); + return (BC_NOEXTRA); + } + mboot = (multiboot_header_t *)(mboot_scan + mboot_off); + + /* + * Unfilled mboot values mean an older version of installgrub installed + * the stage2. Again we have no chance of knowing stage2 size. + */ + if (mboot->load_end_addr == 0 || + mboot->load_end_addr < mboot->load_addr) + return (BC_NOEXTRA); + + /* + * Currently, the amount of space reserved for extra information + * is "fixed". We may have to scan for the terminating extra payload + * in the future. + */ + size = mboot->load_end_addr - mboot->load_addr; + buf_size = P2ROUNDUP(size + SECTOR_SIZE, SECTOR_SIZE); + + stage2->buf = malloc(buf_size); + if (stage2->buf == NULL) { + perror(gettext("Memory allocation failed")); + return (BC_ERROR); + } + stage2->buf_size = buf_size; - if (lseek(stage2_fd, 0, SEEK_SET) == -1) - return (-1); - if (read(stage2_fd, buffer, sb.st_size) < 0) - return (-1); + if (read_in(dev_fd, stage2->buf, buf_size, STAGE2_BLKOFF * + SECTOR_SIZE) != BC_SUCCESS) { + perror("read"); + free(stage2->buf); + return (BC_ERROR); + } + + /* Update pointers. */ + stage2->file = stage2->buf; + stage2->file_size = size; + stage2->mboot_off = mboot_off; + stage2->mboot = (multiboot_header_t *)(stage2->buf + stage2->mboot_off); + stage2->extra = stage2->buf + P2ROUNDUP(stage2->file_size, 8); + + return (BC_SUCCESS); +} + +static boolean_t +is_update_necessary(ig_data_t *data, char *updt_str) +{ + bblk_einfo_t *einfo; + bblk_hs_t stage2_hs; + ig_stage2_t stage2_disk; + ig_stage2_t *stage2_file = &data->stage2; + ig_device_t *device = &data->device; + int dev_fd = device->part_fd; + + assert(data != NULL); + assert(device->part_fd != -1); + + bzero(&stage2_disk, sizeof (ig_stage2_t)); - md5_calc(dest, buffer, sb.st_size); - free(buffer); - return (0); + /* Gather stage2 (if present) from the target device. */ + if (read_stage2_from_disk(dev_fd, &stage2_disk) != BC_SUCCESS) { + BOOT_DEBUG("Unable to read stage2 from %s\n", device->path); + BOOT_DEBUG("No multiboot wrapped stage2 on %s\n", device->path); + return (B_TRUE); + } + + /* + * Look for the extended information structure in the extra payload + * area. + */ + einfo = find_einfo(stage2_disk.extra); + if (einfo == NULL) { + BOOT_DEBUG("No extended information available\n"); + return (B_TRUE); + } + + if (!do_version || updt_str == NULL) { + (void) fprintf(stdout, "WARNING: target device %s has a " + "versioned stage2 that is going to be overwritten by a non " + "versioned one\n", device->path); + return (B_TRUE); + } + + if (force_update) { + BOOT_DEBUG("Forcing update of %s bootblock\n", device->path); + return (B_TRUE); + } + + /* Compare the two extended information structures. */ + stage2_hs.src_buf = (unsigned char *)stage2_file->file; + stage2_hs.src_size = stage2_file->file_size; + + return (einfo_should_update(einfo, &stage2_hs, updt_str)); } @@ -687,198 +1281,230 @@ #define NUM_BLOCK(pos) (*(ushort_t *)((pos) + 4)) #define START_SEG(pos) (*(ushort_t *)((pos) + 6)) -static void -modify_and_write_stage2(int dev_fd) +static int +prepare_stage2(ig_data_t *install, char *updt_str) { - int nrecord; - off_t offset; - char *dest; + ig_device_t *device = &install->device; + ig_stage2_t *stage2 = &install->stage2; + uint32_t mboot_off = 0; + + assert(install != NULL); + assert(stage2->file != NULL); - if (do_version) { - dest = stage2_buffer + STAGE2_SIGN_OFFSET; - if (compute_and_write_md5hash(dest) < 0) - perror("MD5 operation"); - dest = stage2_buffer + STAGE2_PKG_VERSION; - (void) strncpy(dest, verstring, VERSION_SIZE); + /* New stage2 files come with an embedded stage2. */ + if (find_multiboot(stage2->file, stage2->file_size, &mboot_off) + != BC_SUCCESS) { + BOOT_DEBUG("WARNING: no multiboot structure found in stage2, " + "are you using an old GRUB stage2?\n"); + if (do_version == B_TRUE) { + (void) fprintf(stderr, gettext("Versioning requested " + "but stage2 does not support it.. skipping.\n")); + do_version = B_FALSE; + } + } else { + /* Keep track of where the multiboot header is. */ + stage2->mboot_off = mboot_off; + stage2->mboot = (multiboot_header_t *)(stage2->file + + mboot_off); + if (do_version) { + /* + * Adding stage2 information needs to happen before + * we modify the copy of stage2 we have in memory, so + * that the hashing reflects the one of the file. + * An error here is not fatal. + */ + add_stage2_einfo(stage2, updt_str); + } + /* + * Fill multiboot information. We add them even without + * versioning to support as much as possible mirroring. + */ + prepare_fake_multiboot(stage2); } - if (is_floppy || is_bootpar) { - int i = 0; - uint32_t partition_offset; - uint32_t install_addr = 0x8200; - uchar_t *pos = (uchar_t *)stage2_buffer + STAGE2_BLOCKLIST; + if (is_bootpar(device->type)) { + uint32_t blocklist[SECTOR_SIZE / sizeof (uint32_t)]; + uint32_t install_addr = STAGE2_MEMADDR + SECTOR_SIZE; + int i = 0; + uchar_t *pos; - stage2_first_sector = blocklist[0]; + bzero(blocklist, sizeof (blocklist)); + if (read_stage2_blocklist(device->part_fd, blocklist) != 0) { + (void) fprintf(stderr, gettext("Error reading pcfs " + "stage2 blocklist\n")); + return (BC_ERROR); + } - /* figure out the second sector */ + pos = (uchar_t *)stage2->file + STAGE2_BLOCKLIST; + stage2->first_sector = device->start_sector + blocklist[0]; + stage2->pcfs_first_sectors[0] = blocklist[0]; + BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector); + + if (blocklist[1] > 1) { blocklist[0]++; blocklist[1]--; } else { i += 2; } - stage2_second_sector = blocklist[i]; - if (is_floppy) - partition_offset = 0; - else /* solaris boot partition */ - partition_offset = get_start_sector(dev_fd); + stage2->pcfs_first_sectors[1] = blocklist[i]; - /* install the blocklist at the end of stage2_buffer */ while (blocklist[i]) { if (START_BLOCK(pos - 8) != 0 && START_BLOCK(pos - 8) != blocklist[i + 2]) { (void) fprintf(stderr, PCFS_FRAGMENTED); - exit(-1); + return (BC_ERROR); } - START_BLOCK(pos) = blocklist[i] + partition_offset; + START_BLOCK(pos) = blocklist[i] + device->start_sector; START_SEG(pos) = (ushort_t)(install_addr >> 4); NUM_BLOCK(pos) = blocklist[i + 1]; install_addr += blocklist[i + 1] * SECTOR_SIZE; pos -= 8; i += 2; } - } else { + /* Solaris VTOC */ + stage2->first_sector = device->start_sector + STAGE2_BLKOFF; + BOOT_DEBUG("stage2 first sector: %d\n", stage2->first_sector); /* * In a solaris partition, stage2 is written to contiguous * blocks. So we update the starting block only. */ - *((ulong_t *)(stage2_buffer + STAGE2_BLOCKLIST)) = - stage2_first_sector + 1; - } - - if (is_floppy) { - /* modify the config file to add (fd0) */ - char *config_file = stage2_buffer + STAGE2_VER_STRING; - while (*config_file++) - ; - strcpy(config_file, "(fd0)/boot/grub/menu.lst"); - } else { - /* force lba and set disk partition */ - *((unsigned char *) (stage2_buffer + STAGE2_FORCE_LBA)) = 1; - *((long *)(stage2_buffer + STAGE2_INSTALLPART)) - = (partition << 16) | (slice << 8) | 0xff; - } - - /* modification done, now do the writing */ - if (is_floppy || is_bootpar) { - /* we rewrite block 0 and 1 and that's it */ - if (!nowrite && - (pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, - stage2_first_sector * SECTOR_SIZE) != SECTOR_SIZE || - pwrite(dev_fd, stage2_buffer + SECTOR_SIZE, SECTOR_SIZE, - stage2_second_sector * SECTOR_SIZE) != SECTOR_SIZE)) { - (void) fprintf(stderr, WRITE_FAIL_STAGE2); - exit(-1); - } - (void) fprintf(stdout, WRITE_STAGE2_PCFS); - return; + *((ulong_t *)(stage2->file + STAGE2_BLOCKLIST)) = + stage2->first_sector + 1; } - /* for disk, write stage2 starting at STAGE2_BLKOFF sector */ - offset = STAGE2_BLKOFF; + /* force lba and set disk partition */ + *((unsigned char *) (stage2->file + STAGE2_FORCE_LBA)) = 1; + *((long *)(stage2->file + STAGE2_INSTALLPART)) + = (device->partition << 16) | (device->slice << 8) | 0xff; - /* write the modified first two sectors */ - if (!nowrite && pwrite(dev_fd, stage2_buffer, 2 * SECTOR_SIZE, - offset * SECTOR_SIZE) != 2 * SECTOR_SIZE) { - (void) fprintf(stderr, WRITE_FAIL_STAGE2); - exit(-1); - } + return (BC_SUCCESS); +} + +static int +find_x86_bootpar(struct mboot *mboot, int *part_num, uint32_t *start_sect) +{ + int i; - /* write the remaining sectors */ - nrecord = 2; - offset += 2; - for (;;) { - int nread, nwrite; - nread = pread(stage2_fd, stage2_buffer, SECTOR_SIZE, - nrecord * SECTOR_SIZE); - if (nread > 0 && !nowrite) - nwrite = pwrite(dev_fd, stage2_buffer, SECTOR_SIZE, - offset * SECTOR_SIZE); - else - nwrite = SECTOR_SIZE; - if (nread < 0 || nwrite != SECTOR_SIZE) { - (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, - nread, nwrite); - break; + for (i = 0; i < FD_NUMPART; i++) { + struct ipart *part; + + part = (struct ipart *)mboot->parts + i; + if (part->systid == 0xbe) { + if (start_sect) + *start_sect = part->relsect; + if (part_num) + *part_num = i; + /* solaris boot part */ + return (BC_SUCCESS); } - if (nread > 0) { - nrecord ++; - offset ++; - } - if (nread < SECTOR_SIZE) - break; /* end of file */ } - (void) fprintf(stdout, WRITE_STAGE2_DISK, - partition, nrecord, STAGE2_BLKOFF, stage2_first_sector); + return (BC_ERROR); } static char * -get_raw_partition(char *device) +get_raw_partition_path(ig_device_t *device) { - int len; - struct mboot *mboot; - static char *raw = NULL; - - if (raw) - return (raw); - raw = strdup(device); + char *raw; + int len; - if (is_floppy) - return (raw); - - if (is_bootpar) { - int i; - char *end = strstr(raw, "p0:boot"); + if (is_bootpar(device->type)) { + int part; + struct mboot *mboot; - end[2] = 0; /* chop off :boot */ - read_boot_sect(raw); - mboot = (struct mboot *)boot_sect; - for (i = 0; i < FD_NUMPART; i++) { - struct ipart *part = (struct ipart *)mboot->parts + i; - if (part->systid == 0xbe) /* solaris boot part */ - break; + mboot = (struct mboot *)device->boot_sector; + if (find_x86_bootpar(mboot, &part, NULL) != BC_SUCCESS) { + (void) fprintf(stderr, BOOTPAR_NOTFOUND, + device->path_p0); + return (NULL); } - if (i == FD_NUMPART) { - (void) fprintf(stderr, BOOTPAR_NOTFOUND, device); - exit(-1); + raw = strdup(device->path_p0); + if (raw == NULL) { + perror(gettext("Memory allocation failed")); + return (NULL); } - end[1] = '1' + i; /* set partition name */ + + raw[strlen(raw) - 2] = '1' + part; return (raw); } /* For disk, remember slice and return whole fdisk partition */ + raw = strdup(device->path); + if (raw == NULL) { + perror(gettext("Memory allocation failed")); + return (NULL); + } + len = strlen(raw); if (raw[len - 2] != 's' || raw[len - 1] == '2') { (void) fprintf(stderr, NOT_ROOT_SLICE); - exit(-1); + free(raw); + return (NULL); } - slice = atoi(&raw[len - 1]); + device->slice = atoi(&raw[len - 1]); raw[len - 2] = 's'; raw[len - 1] = '2'; + return (raw); } +static int +get_raw_partition_fd(ig_device_t *device) +{ + struct stat stat = {0}; + char *raw; + + raw = get_raw_partition_path(device); + if (raw == NULL) + return (BC_ERROR); + + if (nowrite) + device->part_fd = open(raw, O_RDONLY); + else + device->part_fd = open(raw, O_RDWR); + + if (device->part_fd < 0 || fstat(device->part_fd, &stat) != 0) { + (void) fprintf(stderr, OPEN_FAIL, raw); + free(raw); + return (BC_ERROR); + } + + if (S_ISCHR(stat.st_mode) == 0) { + (void) fprintf(stderr, NOT_RAW_DEVICE, raw); + (void) close(device->part_fd); + device->part_fd = -1; + free(raw); + return (BC_ERROR); + } + + free(raw); + return (BC_SUCCESS); +} + #define TMP_MNTPT "/tmp/installgrub_pcfs" -static void -copy_stage2(int dev_fd, char *device) +static int +copy_stage2_to_pcfs(ig_data_t *install) { - FILE *mntfp; - int i, pcfs_fp; - char buf[SECTOR_SIZE]; - char *cp; - struct mnttab mp = {0}, mpref = {0}; + FILE *mntfp; + int pcfs_fp; + int status = BC_ERROR; + char buf[SECTOR_SIZE]; + char *cp; + struct mnttab mp = {0}, mpref = {0}; + ig_device_t *device = &install->device; + ig_stage2_t *stage2 = &install->stage2; /* convert raw to block device name by removing the first 'r' */ - (void) strncpy(buf, device, sizeof (buf)); + (void) strncpy(buf, device->path, sizeof (buf)); buf[sizeof (buf) - 1] = 0; cp = strchr(buf, 'r'); if (cp == NULL) { - (void) fprintf(stderr, CONVERT_FAIL, device); - exit(-1); + (void) fprintf(stderr, CONVERT_FAIL, device->path); + return (BC_ERROR); } do { *cp = *(cp + 1); @@ -888,7 +1514,7 @@ mntfp = fopen("/etc/mnttab", "r"); if (mntfp == NULL) { (void) fprintf(stderr, OPEN_FAIL_FILE, "/etc/mnttab"); - exit(-1); + return (BC_ERROR); } mpref.mnt_special = buf; @@ -904,7 +1530,7 @@ bzero(&mp, sizeof (mp)); if (getmntany(mntfp, &mp, &mpref) != 0) { (void) fprintf(stderr, MOUNT_FAIL, buf); - exit(-1); + return (BC_ERROR); } } @@ -919,33 +1545,21 @@ if (pcfs_fp == -1) { (void) fprintf(stderr, OPEN_FAIL_FILE, buf); perror("open:"); - (void) umount(TMP_MNTPT); - exit(-1); + goto out; } - /* write stage2 to pcfs */ - for (i = 0; ; i++) { - int nread, nwrite; - nread = pread(stage2_fd, buf, SECTOR_SIZE, i * SECTOR_SIZE); - if (nowrite) - nwrite = nread; - else - nwrite = pwrite(pcfs_fp, buf, nread, i * SECTOR_SIZE); - if (nread < 0 || nwrite != nread) { - (void) fprintf(stderr, WRITE_FAIL_STAGE2_BLOCKS, - nread, nwrite); - break; - } - if (nread < SECTOR_SIZE) - break; /* end of file */ + /* write stage2 to the pcfs mounted filesystem. */ + if (write(pcfs_fp, stage2->file, stage2->file_size) + != stage2->file_size) { + perror(gettext("Error writing stage2")); + goto out; } + + status = BC_SUCCESS; +out_fd: (void) close(pcfs_fp); +out: (void) umount(TMP_MNTPT); - - /* - * Now, get the blocklist from the device. - */ - bzero(blocklist, sizeof (blocklist)); - if (read_stage2_blocklist(dev_fd, blocklist) != 0) - exit(-1); + (void) rmdir(TMP_MNTPT); + return (status); } diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installgrub/installgrub.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/installgrub/installgrub.h Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,93 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#ifndef _INSTALLGRUB_H +#define _INSTALLGRUB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "./../common/bblk_einfo.h" + +#define SECTOR_SIZE (512) + +typedef struct _device_data { + char *path; + char *path_p0; + uint8_t type; + int part_fd; + int disk_fd; + int slice; + int partition; + uint32_t start_sector; + char boot_sector[SECTOR_SIZE]; +} ig_device_t; + +typedef struct _stage2_data { + char *buf; + char *file; + char *extra; + multiboot_header_t *mboot; + uint32_t mboot_off; + uint32_t file_size; + uint32_t buf_size; + uint32_t first_sector; + uint32_t pcfs_first_sectors[2]; +} ig_stage2_t; + +typedef struct _ig_data { + char stage1_buf[SECTOR_SIZE]; + ig_stage2_t stage2; + ig_device_t device; +} ig_data_t; + +enum ig_devtype_t { + IG_DEV_X86BOOTPAR = 1, + IG_DEV_SOLVTOC +}; + +#define is_bootpar(type) (type == IG_DEV_X86BOOTPAR) + +#define STAGE2_MEMADDR (0x8000) /* loading addr of stage2 */ + +#define STAGE1_BPB_OFFSET (0x3) +#define STAGE1_BPB_SIZE (0x3B) +#define STAGE1_BOOT_DRIVE (0x40) +#define STAGE1_FORCE_LBA (0x41) +#define STAGE1_STAGE2_ADDRESS (0x42) +#define STAGE1_STAGE2_SECTOR (0x44) +#define STAGE1_STAGE2_SEGMENT (0x48) + +#define STAGE2_BLOCKLIST (SECTOR_SIZE - 0x8) +#define STAGE2_INSTALLPART (SECTOR_SIZE + 0x8) +#define STAGE2_FORCE_LBA (SECTOR_SIZE + 0x11) +#define STAGE2_BLKOFF (50) /* offset from start of fdisk part */ + +#ifdef __cplusplus +} +#endif + +#endif /* _INSTALLGRUB_H */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/cmd/boot/installgrub/pcfs_glue.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/cmd/boot/installgrub/pcfs_glue.c Wed Jul 28 15:51:57 2010 -0700 @@ -0,0 +1,213 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "message.h" + +/* + * This file is glue layer to pcfs module in usr/src/common/fs/pcfs.c. + * It's main functionality is to get the stage file blocklist. It's + * used for installing grub on a Solaris boot partition. + */ +extern struct boot_fs_ops bpcfs_ops; +struct boot_fs_ops *bfs_ops; +struct boot_fs_ops *bfs_tab[] = {&bpcfs_ops, NULL}; +static int dev_fd; +int bootrd_debug = 0; + +#define DEV_BSIZE 512 +#define MAX_CHUNK 64 + +static unsigned int *blocklist; + +/* diskread_callback is set in filesytem module (pcfs.c) */ +int (*diskread_callback)(int, int); +int (*fileread_callback)(int, int); + +static int +add_stage2_block(int blocknum, int nblk) +{ + static int i = -2; + + if (i >= 0 && (blocklist[i] + blocklist[i + 1] == blocknum)) { + blocklist[i + 1] += nblk; + return (0); + } + + i += 2; + if (i >= DEV_BSIZE / 8) { + fprintf(stderr, PCFS_FRAGMENTED); + exit(-1); + } + blocklist[i] = blocknum; + blocklist[i + 1] = nblk; + return (0); +} + +/* + * This one reads the ramdisk. If fi_memp is set, we copy the + * ramdisk content to the designated buffer. Otherwise, we + * do a "cached" read (set fi_memp to the actual ramdisk buffer). + */ +int +diskread(fileid_t *filep) +{ + int ret; + uint_t blocknum, diskloc; + + blocknum = filep->fi_blocknum; + + if (diskread_callback) { + diskread_callback(blocknum, filep->fi_count / DEV_BSIZE); + return (0); + } + + diskloc = blocknum * DEV_BSIZE; + if (filep->fi_memp == NULL) { + filep->fi_memp = malloc(filep->fi_count); + } + if (filep->fi_memp == NULL) { + fprintf(stderr, OUT_OF_MEMORY); + return (-1); + } + + ret = pread(dev_fd, filep->fi_memp, filep->fi_count, diskloc); + if (ret < 0) + perror("diskread: pread"); + return (ret >= 0 ? 0 : -1); +} + +void * +bkmem_alloc(size_t s) +{ + return (malloc(s)); +} + +/*ARGSUSED*/ +void +bkmem_free(void *p, size_t s) +{ + free(p); +} + +static int +mountroot(char *name) +{ + int i; + + /* try ops in bfs_tab and return the first successful one */ + for (i = 0; bfs_tab[i] != NULL; i++) { + bfs_ops = bfs_tab[i]; + if (BRD_MOUNTROOT(bfs_ops, name) == 0) + return (0); + } + return (-1); +} + +static int +unmountroot() +{ + return (BRD_UNMOUNTROOT(bfs_ops)); +} + +static int +pcfs_glue_open(const char *filename, int flags) +{ + return (BRD_OPEN(bfs_ops, (char *)filename, flags)); +} + +static int +pcfs_glue_close(int fd) +{ + return (BRD_CLOSE(bfs_ops, fd)); +} + +static ssize_t +pcfs_glue_read(int fd, void *buf, size_t size) +{ + return (BRD_READ(bfs_ops, fd, buf, size)); +} + +static off_t +pcfs_glue_lseek(int fd, off_t addr, int whence) +{ + return (BRD_SEEK(bfs_ops, fd, addr, whence)); +} + +/* + * Get the blocklist for stage2 + */ +int +read_stage2_blocklist(int device_fd, unsigned int *blkbuf) +{ + int i, fd, stage2_block; + char buf[DEV_BSIZE]; + ssize_t size; + + dev_fd = device_fd; + if (mountroot("dummy") != 0) { + fprintf(stderr, MOUNT_FAIL_PCFS); + return (-1); + } + + if ((fd = pcfs_glue_open("/boot/grub/stage2", 0)) == -1) { + fprintf(stderr, OPEN_FAIL_PCFS); + return (-1); + } + + if (bootrd_debug) + (void) printf("start reading stage2:\n"); + stage2_block = 0; + blocklist = blkbuf; + fileread_callback = add_stage2_block; + for (;;) { + size = pcfs_glue_read(fd, buf, DEV_BSIZE); + if (size != DEV_BSIZE) + break; + stage2_block++; + } + fileread_callback = NULL; + (void) pcfs_glue_close(fd); + + if (bootrd_debug) { + (void) printf("last block size = %d\n", size); + for (i = 0; blocklist[i] != 0; i += 2) { + (void) printf("sectors: %d-%d\n", + blocklist[i], + blocklist[i] + blocklist[i + 1] - 1); + } + (void) printf("total blocks in stage 2: %d\n", stage2_block); + } + + (void) unmountroot(); + return (0); +} diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/grub/grub-0.97/grub/asmstub.c --- a/usr/src/grub/grub-0.97/grub/asmstub.c Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/grub/grub-0.97/grub/asmstub.c Wed Jul 28 15:51:57 2010 -0700 @@ -67,9 +67,6 @@ #define EXTENDED_MEMSIZE (64 * 1024 * 1024) /* 64MB */ #define CONVENTIONAL_MEMSIZE (640 * 1024) /* 640kB */ -unsigned char md5hash[] = "\xCC\xCC\xCC\xCC\xAA\xAA\xAA\xAA\xBB\xBB\xBB\xBB" - "\xBB\xBB\xBB\xBB"; -char pkg_version[] = "empty"; unsigned long install_partition = 0x20000; unsigned long boot_drive = 0; int saved_entryno = 0; diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/grub/grub-0.97/stage2/asm.S --- a/usr/src/grub/grub-0.97/stage2/asm.S Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/grub/grub-0.97/stage2/asm.S Wed Jul 28 15:51:57 2010 -0700 @@ -108,17 +108,16 @@ * Leave some breathing room for the config file name. */ - . = EXT_C(main) + 0x5f -VARIABLE(ext_info) - .byte 0xEE -VARIABLE(md5hash) - .quad 0xAAAAAAAACCCCCCCC, 0xBBBBBBBBBBBBBBBB -VARIABLE(pkg_version) - .byte 0 + . = EXT_C(main) + 0x60 +VARIABLE(fake_mboot) + .long 0x1BADB002 + .long 0x00010003 + .long -0x1BAEB005 /* - * installgrub will place the pkg_version here + * installgrub will place the rest of the fake + * multiboot header here. */ - .= EXT_C(main) + 0x110 + .= EXT_C(main) + 0x140 /* the real mode code continues... */ codestart: cli /* we're not safe here! */ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/grub/grub-0.97/stage2/builtins.c --- a/usr/src/grub/grub-0.97/stage2/builtins.c Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/grub/grub-0.97/stage2/builtins.c Wed Jul 28 15:51:57 2010 -0700 @@ -18,11 +18,6 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - /* Include stdio.h before shared.h, because we can't define WITHOUT_LIBC_STUBS here. */ #ifdef GRUB_UTIL @@ -2175,32 +2170,6 @@ " a tight loop." }; -/* extended info */ -static int -info_func (char *arg, int flags) -{ - int i; - - grub_printf("Extended version information : %s\n", pkg_version); - grub_printf("stage2 (MD5) signature : "); - - for (i = 0; i < 0x10; i++) - grub_printf("%x", md5hash[i]); - - grub_printf("\n"); -} - -static struct builtin builtin_info = -{ - "info", - info_func, - BUILTIN_CMDLINE | BUILTIN_HELP_LIST | BUILTIN_SCRIPT, - "info", - "Read Grub extended version and stage2 MD5 hash" -}; - - - /* initrd */ static int initrd_func (char *arg, int flags) @@ -5896,7 +5865,6 @@ &builtin_ifconfig, #endif /* SUPPORT_NETBOOT */ &builtin_impsprobe, - &builtin_info, &builtin_initrd, &builtin_install, &builtin_ioprobe, diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/grub/grub-0.97/stage2/shared.h --- a/usr/src/grub/grub-0.97/stage2/shared.h Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/grub/grub-0.97/stage2/shared.h Wed Jul 28 15:51:57 2010 -0700 @@ -17,10 +17,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ /* * Generic defines to use anywhere @@ -534,7 +530,6 @@ unsigned char reserved3[189]; } __attribute__ ((packed)); - #undef NULL #define NULL ((void *) 0) @@ -607,8 +602,6 @@ extern char config_file[]; extern char *bootfile; extern configfile_origin_t configfile_origin; -extern unsigned char md5hash[]; -extern char pkg_version[]; extern unsigned long linux_text_len; extern char *linux_data_tmp_addr; extern char *linux_data_real_addr; diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/psm/stand/bootblks/ufs/sparc/Makefile --- a/usr/src/psm/stand/bootblks/ufs/sparc/Makefile Wed Jul 28 17:47:31 2010 -0500 +++ b/usr/src/psm/stand/bootblks/ufs/sparc/Makefile Wed Jul 28 15:51:57 2010 -0700 @@ -18,31 +18,13 @@ # # CDDL HEADER END # -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. +# Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved. # # psm/stand/bootblks/ufs/sparc/Makefile # BASEDIR = ../.. -include $(BASEDIR)/Makefile.com - -# -# This program is used to install the boot block -# -INSTALLBOOT = installboot - -USR = $(ROOT)/usr -USR_SBIN = $(USR)/sbin -USR_SBIN_INSTALLBOOT = $(USR_SBIN)/$(INSTALLBOOT) - -# -# Overrides for installing installboot. -# -INS.file.555 = $(RM) $@; $(INS) -s -m 555 -f $(@D) $< - - SUBDIRS = sun4u sun4v all := TARGET= all @@ -53,30 +35,9 @@ .KEEP_STATE: -all: $(INSTALLBOOT) $(SUBDIRS) - -install: $(USR_SBIN_INSTALLBOOT) $(SUBDIRS) - -lint clean: $(SUBDIRS) - -clobber: $(SUBDIRS) - -$(RM) $(INSTALLBOOT) +all install lint clean clobber: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: - -# -# install rules -# -$(USR_SBIN)/%: % $(USR_SBIN) - $(INS.file.555) - -# -# Pattern matching rules for source in this directory -# -%: %.sh - $(RM) $@ - cat $< > $@ - chmod +x $@ diff -r ebf6da4d3fca -r f5f96e09bf49 usr/src/psm/stand/bootblks/ufs/sparc/installboot.sh --- a/usr/src/psm/stand/bootblks/ufs/sparc/installboot.sh Wed Jul 28 17:47:31 2010 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,73 +0,0 @@ -#!/bin/sh -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# - -PATH=/usr/bin - -away() { - echo $2 1>&2 - exit $1 -} - -COUNT=15 -FSTYPE=ufs - -while getopts F: a; do - - case $a in - F) case $OPTARG in - ufs|hsfs|zfs) FSTYPE=$OPTARG;; - *) away 1 "$OPTARG: Unknown fstype";; - esac;; - ?) away 1 "unknown fstype: $fs" - esac -done -shift `expr $OPTIND - 1` - -Usage="Usage: `basename $0` [-F fstype] bootblk raw-device" - -test $# -ne 2 && away 1 "$Usage" - -BOOTBLK=$1 -DEVICE=$2 -test ! -f $BOOTBLK && away 1 "$BOOTBLK: File not found" -test ! -c $DEVICE && away 1 "$DEVICE: Not a character device" -test ! -w $DEVICE && away 1 "$DEVICE: Not writeable" - -# label at block 0, bootblk from block 1 through 15 -stderr=`dd if=$BOOTBLK of=$DEVICE bs=1b oseek=1 count=$COUNT conv=sync 2>&1` -err=$? ; test $err -ne 0 && away $err "$stderr" - -# -# The ZFS boot block is larger than what will fit into the first 7.5K so -# we break it up and write the remaining portion into the ZFS provided boot -# block region at offset 512K -# -if [ $FSTYPE = "zfs" ]; then - stderr=`dd if=$BOOTBLK of=$DEVICE bs=1b iseek=$COUNT oseek=1024 \ - count=16 conv=sync 2>&1` - err=$? ; test $err -ne 0 && away $err "$stderr" -fi -exit 0