efidisk.c revision 1.10 1 /* $NetBSD: efidisk.c,v 1.10 2023/05/14 09:07:54 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #define FSTYPENAMES /* for sys/disklabel.h */
30
31 #include "efiboot.h"
32
33 #include <sys/param.h> /* for howmany, required by <dev/raidframe/raidframevar.h> */
34 #include <sys/disklabel.h>
35 #include <sys/disklabel_gpt.h>
36
37 #include "biosdisk.h"
38 #include "biosdisk_ll.h"
39 #include "devopen.h"
40 #include "efidisk.h"
41
42 static struct efidiskinfo_lh efi_disklist;
43 static int nefidisks;
44
45 #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */
46
47 #include <dev/raidframe/raidframevar.h>
48 #define RF_COMPONENT_INFO_OFFSET 16384 /* from sys/dev/raidframe/rf_netbsdkintf.c */
49 #define RF_COMPONENT_LABEL_VERSION 2 /* from <dev/raidframe/rf_raid.h> */
50
51 #define RAIDFRAME_NDEV 16 /* abitrary limit to 15 raidframe devices */
52 struct efi_raidframe {
53 int last_unit;
54 int serial;
55 const struct efidiskinfo *edi;
56 int parent_part;
57 char parent_name[MAXDEVNAME + 1];
58 daddr_t offset;
59 daddr_t size;
60 };
61
62 static void
63 dealloc_biosdisk_part(struct biosdisk_partition *part, int nparts)
64 {
65 int i;
66
67 for (i = 0; i < nparts; i++) {
68 if (part[i].part_name != NULL) {
69 dealloc(part[i].part_name, BIOSDISK_PART_NAME_LEN);
70 part[i].part_name = NULL;
71 }
72 }
73
74 dealloc(part, sizeof(*part) * nparts);
75
76 return;
77 }
78
79 void
80 efi_disk_probe(void)
81 {
82 EFI_STATUS status;
83 UINTN i, nhandles;
84 EFI_HANDLE *handles;
85 EFI_BLOCK_IO *bio;
86 EFI_BLOCK_IO_MEDIA *media;
87 EFI_DEVICE_PATH *dp;
88 struct efidiskinfo *edi;
89 int dev, depth = -1;
90
91 TAILQ_INIT(&efi_disklist);
92
93 status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL,
94 &nhandles, &handles);
95 if (EFI_ERROR(status))
96 return;
97
98 if (efi_bootdp != NULL)
99 depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
100
101 /*
102 * U-Boot incorrectly represents devices with a single
103 * MEDIA_DEVICE_PATH component. In that case include that
104 * component into the matching, otherwise we'll blindly select
105 * the first device.
106 */
107 if (depth == 0)
108 depth = 1;
109
110 for (i = 0; i < nhandles; i++) {
111 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
112 &BlockIoProtocol, (void **)&bio);
113 if (EFI_ERROR(status))
114 continue;
115
116 media = bio->Media;
117 if (media->LogicalPartition || !media->MediaPresent)
118 continue;
119
120 edi = alloc(sizeof(struct efidiskinfo));
121 memset(edi, 0, sizeof(*edi));
122 edi->type = BIOSDISK_TYPE_HD;
123 edi->bio = bio;
124 edi->media_id = media->MediaId;
125
126 if (efi_bootdp != NULL && depth > 0) {
127 status = uefi_call_wrapper(BS->HandleProtocol, 3,
128 handles[i], &DevicePathProtocol, (void **)&dp);
129 if (EFI_ERROR(status))
130 goto next;
131 if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
132 edi->bootdev = true;
133 TAILQ_INSERT_HEAD(&efi_disklist, edi,
134 list);
135 continue;
136 }
137 }
138 next:
139 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
140 }
141
142 FreePool(handles);
143
144 if (efi_bootdp_type == BOOT_DEVICE_TYPE_CD) {
145 edi = TAILQ_FIRST(&efi_disklist);
146 if (edi != NULL && edi->bootdev) {
147 edi->type = BIOSDISK_TYPE_CD;
148 TAILQ_REMOVE(&efi_disklist, edi, list);
149 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
150 }
151 }
152
153 dev = 0x80;
154 TAILQ_FOREACH(edi, &efi_disklist, list) {
155 edi->dev = dev++;
156 if (edi->type == BIOSDISK_TYPE_HD)
157 nefidisks++;
158 if (edi->bootdev)
159 boot_biosdev = edi->dev;
160 }
161 }
162
163 static void
164 efi_raidframe_probe(struct efi_raidframe *raidframe, int *raidframe_count,
165 const struct efidiskinfo *edi,
166 struct biosdisk_partition *part, int parent_part)
167
168 {
169 int i = *raidframe_count;
170 struct RF_ComponentLabel_s label;
171
172 if (i + 1 > RAIDFRAME_NDEV)
173 return;
174
175 if (biosdisk_read_raidframe(edi->dev, part->offset, &label) != 0)
176 return;
177
178 if (label.version != RF_COMPONENT_LABEL_VERSION)
179 return;
180
181 raidframe[i].last_unit = label.last_unit;
182 raidframe[i].serial = label.serial_number;
183 raidframe[i].edi = edi;
184 raidframe[i].parent_part = parent_part;
185 if (part->part_name)
186 strlcpy(raidframe[i].parent_name, part->part_name, MAXDEVNAME);
187 else
188 raidframe[i].parent_name[0] = '\0';
189 raidframe[i].offset = part->offset;
190 raidframe[i].size = label.__numBlocks;
191
192 (*raidframe_count)++;
193
194 return;
195 }
196
197 void
198 efi_disk_show(void)
199 {
200 const struct efidiskinfo *edi;
201 struct efi_raidframe raidframe[RAIDFRAME_NDEV];
202 int raidframe_count = 0;
203 EFI_BLOCK_IO_MEDIA *media;
204 struct biosdisk_partition *part;
205 uint64_t size;
206 int i, j, nparts;
207 bool first;
208
209 TAILQ_FOREACH(edi, &efi_disklist, list) {
210 media = edi->bio->Media;
211 first = true;
212 printf("disk ");
213 switch (edi->type) {
214 case BIOSDISK_TYPE_CD:
215 printf("cd0");
216 printf(" mediaId %u", media->MediaId);
217 if (edi->media_id != media->MediaId)
218 printf("(%u)", edi->media_id);
219 printf("\n");
220 printf(" cd0a\n");
221 break;
222 case BIOSDISK_TYPE_HD:
223 printf("hd%d", edi->dev & 0x7f);
224 printf(" mediaId %u", media->MediaId);
225 if (edi->media_id != media->MediaId)
226 printf("(%u)", edi->media_id);
227 printf(" size ");
228 size = (media->LastBlock + 1) * media->BlockSize;
229 if (size >= (10ULL * 1024 * 1024 * 1024))
230 printf("%"PRIu64" GB", size / (1024 * 1024 * 1024));
231 else
232 printf("%"PRIu64" MB", size / (1024 * 1024));
233 printf("\n");
234 break;
235 }
236 if (edi->type != BIOSDISK_TYPE_HD)
237 continue;
238
239 if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts))
240 continue;
241
242 for (i = 0; i < nparts; i++) {
243 if (part[i].size == 0)
244 continue;
245 if (part[i].fstype == FS_UNUSED)
246 continue;
247 if (part[i].fstype == FS_RAID) {
248 efi_raidframe_probe(raidframe, &raidframe_count,
249 edi, &part[i], i);
250 }
251 if (first) {
252 printf(" ");
253 first = false;
254 }
255 if (part[i].part_name && part[i].part_name[0])
256 printf(" NAME=%s(", part[i].part_name);
257 else
258 printf(" hd%d%c(", edi->dev & 0x7f, i + 'a');
259 if (part[i].guid != NULL)
260 printf("%s", part[i].guid->name);
261 else if (part[i].fstype < FSMAXTYPES)
262 printf("%s", fstypenames[part[i].fstype]);
263 else
264 printf("%d", part[i].fstype);
265 printf(")");
266 }
267 if (!first)
268 printf("\n");
269 dealloc_biosdisk_part(part, nparts);
270 }
271
272 for (i = 0; i < raidframe_count; i++) {
273 size_t secsize = raidframe[i].edi->bio->Media->BlockSize;
274 printf("raidframe raid%d serial %d in ",
275 raidframe[i].last_unit, raidframe[i].serial);
276 if (raidframe[i].parent_name[0])
277 printf("NAME=%s size ", raidframe[i].parent_name);
278 else
279 printf("hd%d%c size ",
280 raidframe[i].edi->dev & 0x7f,
281 raidframe[i].parent_part + 'a');
282 if (raidframe[i].size >= (10ULL * 1024 * 1024 * 1024 / secsize))
283 printf("%"PRIu64" GB",
284 raidframe[i].size / (1024 * 1024 * 1024 / secsize));
285 else
286 printf("%"PRIu64" MB",
287 raidframe[i].size / (1024 * 1024 / secsize));
288 printf("\n");
289
290 if (biosdisk_readpartition(raidframe[i].edi->dev,
291 raidframe[i].offset + RF_PROTECTED_SECTORS,
292 raidframe[i].size,
293 &part, &nparts))
294 continue;
295
296 first = 1;
297 for (j = 0; j < nparts; j++) {
298 bool bootme = part[j].attr & GPT_ENT_ATTR_BOOTME;
299
300 if (part[j].size == 0)
301 continue;
302 if (part[j].fstype == FS_UNUSED)
303 continue;
304 if (part[j].fstype == FS_RAID) /* raid in raid? */
305 continue;
306 if (first) {
307 printf(" ");
308 first = 0;
309 }
310 if (part[j].part_name && part[j].part_name[0])
311 printf(" NAME=%s(", part[j].part_name);
312 else
313 printf(" raid%d%c(",
314 raidframe[i].last_unit, j + 'a');
315 if (part[j].guid != NULL)
316 printf("%s", part[j].guid->name);
317 else if (part[j].fstype < FSMAXTYPES)
318 printf("%s",
319 fstypenames[part[j].fstype]);
320 else
321 printf("%d", part[j].fstype);
322 printf("%s)", bootme ? ", bootme" : "");
323 }
324
325 if (first == 0)
326 printf("\n");
327
328 dealloc_biosdisk_part(part, nparts);
329 }
330 }
331
332 const struct efidiskinfo *
333 efidisk_getinfo(int dev)
334 {
335 const struct efidiskinfo *edi;
336
337 TAILQ_FOREACH(edi, &efi_disklist, list) {
338 if (dev == edi->dev)
339 return edi;
340 }
341 return NULL;
342 }
343
344 /*
345 * Return the number of hard disk drives.
346 */
347 int
348 get_harddrives(void)
349 {
350 return nefidisks;
351 }
352
353 int
354 efidisk_get_efi_system_partition(int dev, int *partition)
355 {
356 extern const struct uuid GET_efi;
357 const struct efidiskinfo *edi;
358 struct biosdisk_partition *part;
359 int i, nparts;
360
361 edi = efidisk_getinfo(dev);
362 if (edi == NULL)
363 return ENXIO;
364
365 if (edi->type != BIOSDISK_TYPE_HD)
366 return ENOTSUP;
367
368 if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts))
369 return EIO;
370
371 for (i = 0; i < nparts; i++) {
372 if (part[i].size == 0)
373 continue;
374 if (part[i].fstype == FS_UNUSED)
375 continue;
376 if (guid_is_equal(part[i].guid->guid, &GET_efi))
377 break;
378 }
379 dealloc_biosdisk_part(part, nparts);
380 if (i == nparts)
381 return ENOENT;
382
383 *partition = i;
384 return 0;
385 }
386