boot1.c revision 1.22 1 1.22 manu /* $NetBSD: boot1.c,v 1.22 2023/06/29 14:18:58 manu Exp $ */
2 1.1 dsl
3 1.1 dsl /*-
4 1.1 dsl * Copyright (c) 2003 The NetBSD Foundation, Inc.
5 1.1 dsl * All rights reserved.
6 1.1 dsl *
7 1.1 dsl * This code is derived from software contributed to The NetBSD Foundation
8 1.1 dsl * by David Laight.
9 1.1 dsl *
10 1.1 dsl * Redistribution and use in source and binary forms, with or without
11 1.1 dsl * modification, are permitted provided that the following conditions
12 1.1 dsl * are met:
13 1.1 dsl * 1. Redistributions of source code must retain the above copyright
14 1.1 dsl * notice, this list of conditions and the following disclaimer.
15 1.1 dsl * 2. Redistributions in binary form must reproduce the above copyright
16 1.1 dsl * notice, this list of conditions and the following disclaimer in the
17 1.1 dsl * documentation and/or other materials provided with the distribution.
18 1.1 dsl *
19 1.1 dsl * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1 dsl * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1 dsl * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1 dsl * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1 dsl * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1 dsl * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1 dsl * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1 dsl * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1 dsl * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1 dsl * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1 dsl * POSSIBILITY OF SUCH DAMAGE.
30 1.1 dsl */
31 1.1 dsl
32 1.1 dsl #include <sys/cdefs.h>
33 1.22 manu __RCSID("$NetBSD: boot1.c,v 1.22 2023/06/29 14:18:58 manu Exp $");
34 1.1 dsl
35 1.1 dsl #include <lib/libsa/stand.h>
36 1.1 dsl #include <lib/libkern/libkern.h>
37 1.1 dsl #include <biosdisk_ll.h>
38 1.1 dsl
39 1.1 dsl #include <sys/param.h>
40 1.22 manu #include <sys/uuid.h>
41 1.1 dsl #include <sys/bootblock.h>
42 1.5 dsl #include <sys/disklabel.h>
43 1.22 manu #include <sys/disklabel_gpt.h>
44 1.3 dsl #include <dev/raidframe/raidframevar.h> /* For RF_PROTECTED_SECTORS */
45 1.1 dsl
46 1.2 dsl #define XSTR(x) #x
47 1.2 dsl #define STR(x) XSTR(x)
48 1.2 dsl
49 1.20 jakllsch static daddr_t bios_sector;
50 1.1 dsl
51 1.6 dsl static struct biosdisk_ll d;
52 1.1 dsl
53 1.20 jakllsch const char *boot1(uint32_t, uint64_t *);
54 1.22 manu #ifndef NO_GPT
55 1.22 manu static daddr_t gpt_lookup(daddr_t);
56 1.22 manu #endif
57 1.1 dsl extern void putstr(const char *);
58 1.1 dsl
59 1.5 dsl extern struct disklabel ptn_disklabel;
60 1.5 dsl
61 1.10 christos static int
62 1.10 christos ob(void)
63 1.10 christos {
64 1.10 christos return open("boot", 0);
65 1.10 christos }
66 1.10 christos
67 1.1 dsl const char *
68 1.20 jakllsch boot1(uint32_t biosdev, uint64_t *sector)
69 1.1 dsl {
70 1.18 dsl struct stat sb;
71 1.1 dsl int fd;
72 1.1 dsl
73 1.6 dsl bios_sector = *sector;
74 1.1 dsl d.dev = biosdev;
75 1.1 dsl
76 1.18 dsl putstr("\r\nNetBSD/x86 " STR(FS) " Primary Bootstrap\r\n");
77 1.1 dsl
78 1.1 dsl if (set_geometry(&d, NULL))
79 1.1 dsl return "set_geometry\r\n";
80 1.1 dsl
81 1.10 christos /*
82 1.10 christos * We default to the filesystem at the start of the
83 1.10 christos * MBR partition
84 1.10 christos */
85 1.10 christos fd = ob();
86 1.10 christos if (fd != -1)
87 1.12 oster goto done;
88 1.10 christos /*
89 1.10 christos * Maybe the filesystem is enclosed in a raid set.
90 1.10 christos * add in size of raidframe header and try again.
91 1.10 christos * (Maybe this should only be done if the filesystem
92 1.10 christos * magic number is absent.)
93 1.10 christos */
94 1.10 christos bios_sector += RF_PROTECTED_SECTORS;
95 1.10 christos fd = ob();
96 1.10 christos if (fd != -1)
97 1.12 oster goto done;
98 1.22 manu
99 1.22 manu #ifndef NO_GPT
100 1.22 manu /*
101 1.22 manu * Test for a GPT inside the RAID
102 1.22 manu */
103 1.22 manu bios_sector += gpt_lookup(bios_sector);
104 1.22 manu fd = ob();
105 1.22 manu if (fd != -1)
106 1.22 manu goto done;
107 1.22 manu #endif
108 1.22 manu
109 1.10 christos /*
110 1.10 christos * Nothing at the start of the MBR partition, fallback on
111 1.10 christos * partition 'a' from the disklabel in this MBR partition.
112 1.10 christos */
113 1.11 christos if (ptn_disklabel.d_magic != DISKMAGIC ||
114 1.11 christos ptn_disklabel.d_magic2 != DISKMAGIC ||
115 1.11 christos ptn_disklabel.d_partitions[0].p_fstype == FS_UNUSED)
116 1.12 oster goto done;
117 1.10 christos bios_sector = ptn_disklabel.d_partitions[0].p_offset;
118 1.10 christos *sector = bios_sector;
119 1.10 christos if (ptn_disklabel.d_partitions[0].p_fstype == FS_RAID)
120 1.5 dsl bios_sector += RF_PROTECTED_SECTORS;
121 1.5 dsl
122 1.10 christos fd = ob();
123 1.10 christos
124 1.12 oster done:
125 1.19 christos if (fd == -1 || fstat(fd, &sb) == -1)
126 1.11 christos return "Can't open /boot\r\n";
127 1.1 dsl
128 1.11 christos biosdev = (uint32_t)sb.st_size;
129 1.1 dsl #if 0
130 1.11 christos if (biosdev > SECONDARY_MAX_LOAD)
131 1.11 christos return "/boot too large\r\n";
132 1.1 dsl #endif
133 1.1 dsl
134 1.11 christos if (read(fd, (void *)SECONDARY_LOAD_ADDRESS, biosdev) != biosdev)
135 1.11 christos return "/boot load failed\r\n";
136 1.1 dsl
137 1.1 dsl if (*(uint32_t *)(SECONDARY_LOAD_ADDRESS + 4) != X86_BOOT_MAGIC_2)
138 1.11 christos return "Invalid /boot file format\r\n";
139 1.1 dsl
140 1.1 dsl /* We need to jump to the secondary bootstrap in realmode */
141 1.1 dsl return 0;
142 1.1 dsl }
143 1.1 dsl
144 1.1 dsl int
145 1.1 dsl blkdevstrategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize)
146 1.1 dsl {
147 1.1 dsl if (flag != F_READ)
148 1.1 dsl return EROFS;
149 1.1 dsl
150 1.8 junyoung if (size & (BIOSDISK_DEFAULT_SECSIZE - 1))
151 1.1 dsl return EINVAL;
152 1.1 dsl
153 1.1 dsl if (rsize)
154 1.1 dsl *rsize = size;
155 1.1 dsl
156 1.1 dsl if (size != 0 && readsects(&d, bios_sector + dblk,
157 1.8 junyoung size / BIOSDISK_DEFAULT_SECSIZE,
158 1.8 junyoung buf, 1) != 0)
159 1.1 dsl return EIO;
160 1.1 dsl
161 1.1 dsl return 0;
162 1.1 dsl }
163 1.22 manu
164 1.22 manu #ifndef NO_GPT
165 1.22 manu static int
166 1.22 manu is_unused(struct gpt_ent *ent)
167 1.22 manu {
168 1.22 manu const struct uuid unused = GPT_ENT_TYPE_UNUSED;
169 1.22 manu
170 1.22 manu return (memcmp(ent->ent_type, &unused, sizeof(unused)) == 0);
171 1.22 manu }
172 1.22 manu
173 1.22 manu static int
174 1.22 manu is_bootable(struct gpt_ent *ent)
175 1.22 manu {
176 1.22 manu /* GPT_ENT_TYPE_NETBSD_RAID omitted as we are already in a RAID */
177 1.22 manu const struct uuid bootable[] = {
178 1.22 manu GPT_ENT_TYPE_NETBSD_FFS,
179 1.22 manu GPT_ENT_TYPE_NETBSD_LFS,
180 1.22 manu GPT_ENT_TYPE_NETBSD_CCD,
181 1.22 manu GPT_ENT_TYPE_NETBSD_CGD,
182 1.22 manu };
183 1.22 manu int i;
184 1.22 manu
185 1.22 manu for (i = 0; i < sizeof(bootable) / sizeof(*bootable); i++) {
186 1.22 manu if (memcmp(ent->ent_type, &bootable[i],
187 1.22 manu sizeof(struct uuid)) == 0)
188 1.22 manu return 1;
189 1.22 manu }
190 1.22 manu
191 1.22 manu return 0;
192 1.22 manu }
193 1.22 manu
194 1.22 manu static daddr_t
195 1.22 manu gpt_lookup(daddr_t sector)
196 1.22 manu {
197 1.22 manu char buf[BIOSDISK_DEFAULT_SECSIZE];
198 1.22 manu struct mbr_sector *pmbr;
199 1.22 manu const char gpt_hdr_sig[] = GPT_HDR_SIG;
200 1.22 manu struct gpt_hdr *hdr;
201 1.22 manu struct gpt_ent *ent;
202 1.22 manu uint32_t nents;
203 1.22 manu uint32_t entsz;
204 1.22 manu uint32_t entries_per_sector;
205 1.22 manu uint32_t sectors_per_entry;
206 1.22 manu uint64_t firstpart_lba = 0;
207 1.22 manu uint64_t bootable_lba = 0;
208 1.22 manu uint64_t bootme_lba = 0;
209 1.22 manu int i, j;
210 1.22 manu
211 1.22 manu /*
212 1.22 manu * Look for a PMBR
213 1.22 manu */
214 1.22 manu if (readsects(&d, sector, 1, buf, 1) != 0)
215 1.22 manu return 0;
216 1.22 manu
217 1.22 manu pmbr = (struct mbr_sector *)buf;
218 1.22 manu
219 1.22 manu if (pmbr->mbr_magic != htole16(MBR_MAGIC))
220 1.22 manu return 0;
221 1.22 manu
222 1.22 manu if (pmbr->mbr_parts[0].mbrp_type != MBR_PTYPE_PMBR)
223 1.22 manu return 0;
224 1.22 manu
225 1.22 manu sector++; /* skip PMBR */
226 1.22 manu
227 1.22 manu /*
228 1.22 manu * Look for a GPT header
229 1.22 manu * Space is scarce, we do not check CRC.
230 1.22 manu */
231 1.22 manu if (readsects(&d, sector, 1, buf, 1) != 0)
232 1.22 manu return 0;
233 1.22 manu
234 1.22 manu hdr = (struct gpt_hdr *)buf;
235 1.22 manu
236 1.22 manu if (memcmp(gpt_hdr_sig, hdr->hdr_sig, sizeof(hdr->hdr_sig)) != 0)
237 1.22 manu return 0;
238 1.22 manu
239 1.22 manu if (hdr->hdr_revision != htole32(GPT_HDR_REVISION))
240 1.22 manu return 0;
241 1.22 manu
242 1.22 manu if (le32toh(hdr->hdr_size) > BIOSDISK_DEFAULT_SECSIZE)
243 1.22 manu return 0;
244 1.22 manu
245 1.22 manu nents = le32toh(hdr->hdr_entries);
246 1.22 manu entsz = le32toh(hdr->hdr_entsz);
247 1.22 manu
248 1.22 manu sector++; /* skip GPT header */
249 1.22 manu
250 1.22 manu /*
251 1.22 manu * Read partition table
252 1.22 manu *
253 1.22 manu * According to UEFI specification section 5.3.2, entries
254 1.22 manu * are 128 * (2^n) bytes long. The most common scenario is
255 1.22 manu * 128 bytes (n = 0) where there are 4 entries per sector.
256 1.22 manu * If n > 2, then entries spans multiple sectors, but they
257 1.22 manu * remain sector-aligned.
258 1.22 manu */
259 1.22 manu entries_per_sector = BIOSDISK_DEFAULT_SECSIZE / entsz;
260 1.22 manu if (entries_per_sector == 0)
261 1.22 manu entries_per_sector = 1;
262 1.22 manu
263 1.22 manu sectors_per_entry = entsz / BIOSDISK_DEFAULT_SECSIZE;
264 1.22 manu if (sectors_per_entry == 0)
265 1.22 manu sectors_per_entry = 1;
266 1.22 manu
267 1.22 manu for (i = 0; i < nents; i += entries_per_sector) {
268 1.22 manu if (readsects(&d, sector, 1, buf, 1) != 0)
269 1.22 manu return 0;
270 1.22 manu
271 1.22 manu sector += sectors_per_entry;
272 1.22 manu
273 1.22 manu for (j = 0; j < entries_per_sector; j++) {
274 1.22 manu ent = (struct gpt_ent *)&buf[j * entsz];
275 1.22 manu
276 1.22 manu if (is_unused(ent))
277 1.22 manu continue;
278 1.22 manu
279 1.22 manu /* First bootme wins, we can stop there */
280 1.22 manu if (ent->ent_attr & GPT_ENT_ATTR_BOOTME) {
281 1.22 manu bootme_lba = le64toh(ent->ent_lba_start);
282 1.22 manu goto out;
283 1.22 manu }
284 1.22 manu
285 1.22 manu if (firstpart_lba == 0)
286 1.22 manu firstpart_lba = le64toh(ent->ent_lba_start);
287 1.22 manu
288 1.22 manu if (is_bootable(ent) && bootable_lba == 0)
289 1.22 manu bootable_lba = le64toh(ent->ent_lba_start);
290 1.22 manu }
291 1.22 manu }
292 1.22 manu
293 1.22 manu out:
294 1.22 manu if (bootme_lba)
295 1.22 manu return bootme_lba;
296 1.22 manu
297 1.22 manu if (bootable_lba)
298 1.22 manu return bootable_lba;
299 1.22 manu
300 1.22 manu if (firstpart_lba)
301 1.22 manu return firstpart_lba;
302 1.22 manu
303 1.22 manu return 0;
304 1.22 manu }
305 1.22 manu #endif /* ! NO_GPT */
306