1 /* $NetBSD: efidisk.c,v 1.11 2024/01/06 21:26:43 mlelstv Exp $ */
2
3 /*-
4 * Copyright (c) 2016 Kimihiro Nonaka <nonaka@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 #include "bootinfo.h"
42
43 static struct efidiskinfo_lh efi_disklist;
44 static int nefidisks;
45 static struct btinfo_biosgeom *bibg;
46 static size_t bibg_len;
47
48 #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */
49
50 #include <dev/raidframe/raidframevar.h>
51 #define RF_COMPONENT_INFO_OFFSET 16384 /* from sys/dev/raidframe/rf_netbsdkintf.c */
52 #define RF_COMPONENT_LABEL_VERSION 2 /* from <dev/raidframe/rf_raid.h> */
53
54 #define RAIDFRAME_NDEV 16 /* abitrary limit to 15 raidframe devices */
55 struct efi_raidframe {
56 int last_unit;
57 int serial;
58 const struct efidiskinfo *edi;
59 int parent_part;
60 char parent_name[MAXDEVNAME + 1];
61 daddr_t offset;
62 daddr_t size;
63 };
64
65 static void
dealloc_biosdisk_part(struct biosdisk_partition * part,int nparts)66 dealloc_biosdisk_part(struct biosdisk_partition *part, int nparts)
67 {
68 int i;
69
70 for (i = 0; i < nparts; i++) {
71 if (part[i].part_name != NULL) {
72 dealloc(part[i].part_name, BIOSDISK_PART_NAME_LEN);
73 part[i].part_name = NULL;
74 }
75 }
76
77 dealloc(part, sizeof(*part) * nparts);
78
79 return;
80 }
81
82 void
efi_disk_probe(void)83 efi_disk_probe(void)
84 {
85 EFI_STATUS status;
86 UINTN i, nhandles;
87 EFI_HANDLE *handles;
88 EFI_BLOCK_IO *bio;
89 EFI_BLOCK_IO_MEDIA *media;
90 EFI_DEVICE_PATH *dp;
91 struct efidiskinfo *edi;
92 int dev, depth = -1;
93
94 TAILQ_INIT(&efi_disklist);
95
96 status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL,
97 &nhandles, &handles);
98 if (EFI_ERROR(status))
99 return;
100
101 if (efi_bootdp != NULL)
102 depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
103
104 /*
105 * U-Boot incorrectly represents devices with a single
106 * MEDIA_DEVICE_PATH component. In that case include that
107 * component into the matching, otherwise we'll blindly select
108 * the first device.
109 */
110 if (depth == 0)
111 depth = 1;
112
113 for (i = 0; i < nhandles; i++) {
114 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
115 &BlockIoProtocol, (void **)&bio);
116 if (EFI_ERROR(status))
117 continue;
118
119 media = bio->Media;
120 if (media->LogicalPartition || !media->MediaPresent)
121 continue;
122
123 edi = alloc(sizeof(struct efidiskinfo));
124 memset(edi, 0, sizeof(*edi));
125 edi->type = BIOSDISK_TYPE_HD;
126 edi->bio = bio;
127 edi->media_id = media->MediaId;
128
129 if (efi_bootdp != NULL && depth > 0) {
130 status = uefi_call_wrapper(BS->HandleProtocol, 3,
131 handles[i], &DevicePathProtocol, (void **)&dp);
132 if (EFI_ERROR(status))
133 goto next;
134 if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
135 edi->bootdev = true;
136 TAILQ_INSERT_HEAD(&efi_disklist, edi,
137 list);
138 continue;
139 }
140 }
141 next:
142 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
143 }
144
145 FreePool(handles);
146
147 if (efi_bootdp_type == BOOT_DEVICE_TYPE_CD) {
148 edi = TAILQ_FIRST(&efi_disklist);
149 if (edi != NULL && edi->bootdev) {
150 edi->type = BIOSDISK_TYPE_CD;
151 TAILQ_REMOVE(&efi_disklist, edi, list);
152 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
153 }
154 }
155
156 dev = 0x80;
157 TAILQ_FOREACH(edi, &efi_disklist, list) {
158 edi->dev = dev++;
159 if (edi->type == BIOSDISK_TYPE_HD)
160 nefidisks++;
161 if (edi->bootdev)
162 boot_biosdev = edi->dev;
163 }
164
165 bibg_len = sizeof(*bibg) + nefidisks * sizeof(struct bi_biosgeom_entry);
166 bibg = alloc(bibg_len);
167 if (bibg == NULL)
168 return;
169
170 bibg->num = nefidisks;
171
172 i = 0;
173 TAILQ_FOREACH(edi, &efi_disklist, list) {
174 if (edi->type == BIOSDISK_TYPE_HD) {
175 memset(&bibg->disk[i], 0, sizeof(bibg->disk[i]));
176 bibg->disk[i].dev = edi->dev;
177 bibg->disk[i].flags = BI_GEOM_INVALID;
178 }
179 ++i;
180 }
181 }
182
183 static void
efi_raidframe_probe(struct efi_raidframe * raidframe,int * raidframe_count,const struct efidiskinfo * edi,struct biosdisk_partition * part,int parent_part)184 efi_raidframe_probe(struct efi_raidframe *raidframe, int *raidframe_count,
185 const struct efidiskinfo *edi,
186 struct biosdisk_partition *part, int parent_part)
187
188 {
189 int i = *raidframe_count;
190 struct RF_ComponentLabel_s label;
191
192 if (i + 1 > RAIDFRAME_NDEV)
193 return;
194
195 if (biosdisk_read_raidframe(edi->dev, part->offset, &label) != 0)
196 return;
197
198 if (label.version != RF_COMPONENT_LABEL_VERSION)
199 return;
200
201 raidframe[i].last_unit = label.last_unit;
202 raidframe[i].serial = label.serial_number;
203 raidframe[i].edi = edi;
204 raidframe[i].parent_part = parent_part;
205 if (part->part_name)
206 strlcpy(raidframe[i].parent_name, part->part_name, MAXDEVNAME);
207 else
208 raidframe[i].parent_name[0] = '\0';
209 raidframe[i].offset = part->offset;
210 raidframe[i].size = label.__numBlocks;
211
212 (*raidframe_count)++;
213
214 return;
215 }
216
217 void
efi_disk_show(void)218 efi_disk_show(void)
219 {
220 const struct efidiskinfo *edi;
221 struct efi_raidframe raidframe[RAIDFRAME_NDEV];
222 int raidframe_count = 0;
223 EFI_BLOCK_IO_MEDIA *media;
224 struct biosdisk_partition *part;
225 uint64_t size;
226 int i, j, nparts;
227 bool first;
228
229 TAILQ_FOREACH(edi, &efi_disklist, list) {
230 media = edi->bio->Media;
231 first = true;
232 printf("disk ");
233 switch (edi->type) {
234 case BIOSDISK_TYPE_CD:
235 printf("cd0");
236 printf(" mediaId %u", media->MediaId);
237 if (edi->media_id != media->MediaId)
238 printf("(%u)", edi->media_id);
239 printf("\n");
240 printf(" cd0a\n");
241 break;
242 case BIOSDISK_TYPE_HD:
243 printf("hd%d", edi->dev & 0x7f);
244 printf(" mediaId %u", media->MediaId);
245 if (edi->media_id != media->MediaId)
246 printf("(%u)", edi->media_id);
247 printf(" size ");
248 size = (media->LastBlock + 1) * media->BlockSize;
249 if (size >= (10ULL * 1024 * 1024 * 1024))
250 printf("%"PRIu64" GB", size / (1024 * 1024 * 1024));
251 else
252 printf("%"PRIu64" MB", size / (1024 * 1024));
253 printf("\n");
254 break;
255 }
256 if (edi->type != BIOSDISK_TYPE_HD)
257 continue;
258
259 if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts))
260 continue;
261
262 for (i = 0; i < nparts; i++) {
263 if (part[i].size == 0)
264 continue;
265 if (part[i].fstype == FS_UNUSED)
266 continue;
267 if (part[i].fstype == FS_RAID) {
268 efi_raidframe_probe(raidframe, &raidframe_count,
269 edi, &part[i], i);
270 }
271 if (first) {
272 printf(" ");
273 first = false;
274 }
275 if (part[i].part_name && part[i].part_name[0])
276 printf(" NAME=%s(", part[i].part_name);
277 else
278 printf(" hd%d%c(", edi->dev & 0x7f, i + 'a');
279 if (part[i].guid != NULL)
280 printf("%s", part[i].guid->name);
281 else if (part[i].fstype < FSMAXTYPES)
282 printf("%s", fstypenames[part[i].fstype]);
283 else
284 printf("%d", part[i].fstype);
285 printf(")");
286 }
287 if (!first)
288 printf("\n");
289 dealloc_biosdisk_part(part, nparts);
290 }
291
292 for (i = 0; i < raidframe_count; i++) {
293 size_t secsize = raidframe[i].edi->bio->Media->BlockSize;
294 printf("raidframe raid%d serial %d in ",
295 raidframe[i].last_unit, raidframe[i].serial);
296 if (raidframe[i].parent_name[0])
297 printf("NAME=%s size ", raidframe[i].parent_name);
298 else
299 printf("hd%d%c size ",
300 raidframe[i].edi->dev & 0x7f,
301 raidframe[i].parent_part + 'a');
302 if (raidframe[i].size >= (10ULL * 1024 * 1024 * 1024 / secsize))
303 printf("%"PRIu64" GB",
304 raidframe[i].size / (1024 * 1024 * 1024 / secsize));
305 else
306 printf("%"PRIu64" MB",
307 raidframe[i].size / (1024 * 1024 / secsize));
308 printf("\n");
309
310 if (biosdisk_readpartition(raidframe[i].edi->dev,
311 raidframe[i].offset + RF_PROTECTED_SECTORS,
312 raidframe[i].size,
313 &part, &nparts))
314 continue;
315
316 first = 1;
317 for (j = 0; j < nparts; j++) {
318 bool bootme = part[j].attr & GPT_ENT_ATTR_BOOTME;
319
320 if (part[j].size == 0)
321 continue;
322 if (part[j].fstype == FS_UNUSED)
323 continue;
324 if (part[j].fstype == FS_RAID) /* raid in raid? */
325 continue;
326 if (first) {
327 printf(" ");
328 first = 0;
329 }
330 if (part[j].part_name && part[j].part_name[0])
331 printf(" NAME=%s(", part[j].part_name);
332 else
333 printf(" raid%d%c(",
334 raidframe[i].last_unit, j + 'a');
335 if (part[j].guid != NULL)
336 printf("%s", part[j].guid->name);
337 else if (part[j].fstype < FSMAXTYPES)
338 printf("%s",
339 fstypenames[part[j].fstype]);
340 else
341 printf("%d", part[j].fstype);
342 printf("%s)", bootme ? ", bootme" : "");
343 }
344
345 if (first == 0)
346 printf("\n");
347
348 dealloc_biosdisk_part(part, nparts);
349 }
350 }
351
352 const struct efidiskinfo *
efidisk_getinfo(int dev)353 efidisk_getinfo(int dev)
354 {
355 const struct efidiskinfo *edi;
356
357 TAILQ_FOREACH(edi, &efi_disklist, list) {
358 if (dev == edi->dev)
359 return edi;
360 }
361 return NULL;
362 }
363
364 /*
365 * Return the number of hard disk drives.
366 */
367 int
get_harddrives(void)368 get_harddrives(void)
369 {
370 return nefidisks;
371 }
372
373 int
efidisk_get_efi_system_partition(int dev,int * partition)374 efidisk_get_efi_system_partition(int dev, int *partition)
375 {
376 extern const struct uuid GET_efi;
377 const struct efidiskinfo *edi;
378 struct biosdisk_partition *part;
379 int i, nparts;
380
381 edi = efidisk_getinfo(dev);
382 if (edi == NULL)
383 return ENXIO;
384
385 if (edi->type != BIOSDISK_TYPE_HD)
386 return ENOTSUP;
387
388 if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts))
389 return EIO;
390
391 for (i = 0; i < nparts; i++) {
392 if (part[i].size == 0)
393 continue;
394 if (part[i].fstype == FS_UNUSED)
395 continue;
396 if (guid_is_equal(part[i].guid->guid, &GET_efi))
397 break;
398 }
399 dealloc_biosdisk_part(part, nparts);
400 if (i == nparts)
401 return ENOENT;
402
403 *partition = i;
404 return 0;
405 }
406
407 void
efidisk_getbiosgeom()408 efidisk_getbiosgeom()
409 {
410 BI_ADD(bibg, BTINFO_BIOSGEOM, bibg_len);
411 }
412
413