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