efidisk.c revision 1.5 1 /* $NetBSD: efidisk.c,v 1.5 2018/04/02 09:44:18 nonaka 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/disklabel.h>
34
35 #include "biosdisk.h"
36 #include "biosdisk_ll.h"
37 #include "devopen.h"
38 #include "efidisk.h"
39
40 static struct efidiskinfo_lh efi_disklist;
41 static int nefidisks;
42
43 void
44 efi_disk_probe(void)
45 {
46 EFI_STATUS status;
47 UINTN i, nhandles;
48 EFI_HANDLE *handles;
49 EFI_BLOCK_IO *bio;
50 EFI_BLOCK_IO_MEDIA *media;
51 EFI_DEVICE_PATH *dp;
52 struct efidiskinfo *edi;
53 int dev, depth = -1;
54
55 TAILQ_INIT(&efi_disklist);
56
57 status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL,
58 &nhandles, &handles);
59 if (EFI_ERROR(status))
60 panic("LocateHandle(BlockIoProtocol): %" PRIxMAX,
61 (uintmax_t)status);
62
63 if (efi_bootdp != NULL)
64 depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
65
66 /*
67 * U-Boot incorrectly represents devices with a single
68 * MEDIA_DEVICE_PATH component. In that case include that
69 * component into the matching, otherwise we'll blindly select
70 * the first device.
71 */
72 if (depth == 0)
73 depth = 1;
74
75 for (i = 0; i < nhandles; i++) {
76 status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i],
77 &BlockIoProtocol, (void **)&bio);
78 if (EFI_ERROR(status))
79 panic("HandleProtocol(BlockIoProtocol): %" PRIxMAX,
80 (uintmax_t)status);
81
82 media = bio->Media;
83 if (media->LogicalPartition || !media->MediaPresent)
84 continue;
85
86 edi = alloc(sizeof(struct efidiskinfo));
87 memset(edi, 0, sizeof(*edi));
88 edi->type = BIOSDISK_TYPE_HD;
89 edi->bio = bio;
90 edi->media_id = media->MediaId;
91
92 if (efi_bootdp != NULL && depth > 0) {
93 status = uefi_call_wrapper(BS->HandleProtocol, 3,
94 handles[i], &DevicePathProtocol, (void **)&dp);
95 if (EFI_ERROR(status))
96 goto next;
97 if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) {
98 edi->bootdev = true;
99 TAILQ_INSERT_HEAD(&efi_disklist, edi,
100 list);
101 continue;
102 }
103 }
104 next:
105 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
106 }
107
108 FreePool(handles);
109
110 if (efi_bootdp_type == BIOSDISK_TYPE_CD) {
111 edi = TAILQ_FIRST(&efi_disklist);
112 if (edi != NULL && edi->bootdev) {
113 edi->type = BIOSDISK_TYPE_CD;
114 TAILQ_REMOVE(&efi_disklist, edi, list);
115 TAILQ_INSERT_TAIL(&efi_disklist, edi, list);
116 }
117 }
118
119 dev = 0x80;
120 TAILQ_FOREACH(edi, &efi_disklist, list) {
121 edi->dev = dev++;
122 if (edi->type == BIOSDISK_TYPE_HD)
123 nefidisks++;
124 if (edi->bootdev)
125 boot_biosdev = edi->dev;
126 }
127 }
128
129 void
130 efi_disk_show(void)
131 {
132 const struct efidiskinfo *edi;
133 EFI_BLOCK_IO_MEDIA *media;
134 struct biosdisk_partition *part;
135 uint64_t size;
136 int i, nparts;
137 bool first;
138
139 TAILQ_FOREACH(edi, &efi_disklist, list) {
140 media = edi->bio->Media;
141 first = true;
142 printf("disk ");
143 switch (edi->type) {
144 case BIOSDISK_TYPE_CD:
145 printf("cd0");
146 printf(" mediaId %u", media->MediaId);
147 if (edi->media_id != media->MediaId)
148 printf("(%u)", edi->media_id);
149 printf("\n");
150 printf(" cd0a\n");
151 break;
152 case BIOSDISK_TYPE_HD:
153 printf("hd%d", edi->dev & 0x7f);
154 printf(" mediaId %u", media->MediaId);
155 if (edi->media_id != media->MediaId)
156 printf("(%u)", edi->media_id);
157 printf(" size ");
158 size = (media->LastBlock + 1) * media->BlockSize;
159 if (size >= (10ULL * 1024 * 1024 * 1024))
160 printf("%"PRIu64" GB", size / (1024 * 1024 * 1024));
161 else
162 printf("%"PRIu64" MB", size / (1024 * 1024));
163 printf("\n");
164 break;
165 }
166 if (edi->type != BIOSDISK_TYPE_HD)
167 continue;
168
169 if (biosdisk_readpartition(edi->dev, &part, &nparts))
170 continue;
171
172 for (i = 0; i < nparts; i++) {
173 if (part[i].size == 0)
174 continue;
175 if (part[i].fstype == FS_UNUSED)
176 continue;
177 if (first) {
178 printf(" ");
179 first = false;
180 }
181 printf(" hd%d%c(", edi->dev & 0x7f, i + 'a');
182 if (part[i].guid != NULL)
183 printf("%s", part[i].guid->name);
184 else if (part[i].fstype < FSMAXTYPES)
185 printf("%s", fstypenames[part[i].fstype]);
186 else
187 printf("%d", part[i].fstype);
188 printf(")");
189 }
190 if (!first)
191 printf("\n");
192 dealloc(part, sizeof(*part) * nparts);
193 }
194 }
195
196 const struct efidiskinfo *
197 efidisk_getinfo(int dev)
198 {
199 const struct efidiskinfo *edi;
200
201 TAILQ_FOREACH(edi, &efi_disklist, list) {
202 if (dev == edi->dev)
203 return edi;
204 }
205 return NULL;
206 }
207
208 /*
209 * Return the number of hard disk drives.
210 */
211 int
212 get_harddrives(void)
213 {
214 return nefidisks;
215 }
216
217 int
218 efidisk_get_efi_system_partition(int dev, int *partition)
219 {
220 extern const struct uuid GET_efi;
221 const struct efidiskinfo *edi;
222 struct biosdisk_partition *part;
223 int i, nparts;
224
225 edi = efidisk_getinfo(dev);
226 if (edi == NULL)
227 return ENXIO;
228
229 if (edi->type != BIOSDISK_TYPE_HD)
230 return ENOTSUP;
231
232 if (biosdisk_readpartition(edi->dev, &part, &nparts))
233 return EIO;
234
235 for (i = 0; i < nparts; i++) {
236 if (part[i].size == 0)
237 continue;
238 if (part[i].fstype == FS_UNUSED)
239 continue;
240 if (guid_is_equal(part[i].guid->guid, &GET_efi))
241 break;
242 }
243 dealloc(part, sizeof(*part) * nparts);
244 if (i == nparts)
245 return ENOENT;
246
247 *partition = i;
248 return 0;
249 }
250