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