efiblock.c revision 1.5.4.3 1 1.5.4.3 martin /* $NetBSD: efiblock.c,v 1.5.4.3 2020/04/13 08:05:19 martin Exp $ */
2 1.5.4.2 christos
3 1.5.4.2 christos /*-
4 1.5.4.2 christos * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org>
5 1.5.4.2 christos * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
6 1.5.4.2 christos * All rights reserved.
7 1.5.4.2 christos *
8 1.5.4.2 christos * Redistribution and use in source and binary forms, with or without
9 1.5.4.2 christos * modification, are permitted provided that the following conditions
10 1.5.4.2 christos * are met:
11 1.5.4.2 christos * 1. Redistributions of source code must retain the above copyright
12 1.5.4.2 christos * notice, this list of conditions and the following disclaimer.
13 1.5.4.2 christos * 2. Redistributions in binary form must reproduce the above copyright
14 1.5.4.2 christos * notice, this list of conditions and the following disclaimer in the
15 1.5.4.2 christos * documentation and/or other materials provided with the distribution.
16 1.5.4.2 christos *
17 1.5.4.2 christos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 1.5.4.2 christos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 1.5.4.2 christos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 1.5.4.2 christos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 1.5.4.2 christos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.5.4.2 christos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 1.5.4.2 christos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 1.5.4.2 christos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.5.4.2 christos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.5.4.2 christos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.5.4.2 christos * SUCH DAMAGE.
28 1.5.4.2 christos */
29 1.5.4.2 christos
30 1.5.4.2 christos #define FSTYPENAMES
31 1.5.4.2 christos
32 1.5.4.2 christos #include <sys/param.h>
33 1.5.4.2 christos #include <sys/md5.h>
34 1.5.4.2 christos #include <sys/uuid.h>
35 1.5.4.2 christos
36 1.5.4.2 christos #include "efiboot.h"
37 1.5.4.2 christos #include "efiblock.h"
38 1.5.4.2 christos
39 1.5.4.2 christos static EFI_HANDLE *efi_block;
40 1.5.4.2 christos static UINTN efi_nblock;
41 1.5.4.2 christos static struct efi_block_part *efi_block_booted = NULL;
42 1.5.4.2 christos
43 1.5.4.2 christos static TAILQ_HEAD(, efi_block_dev) efi_block_devs = TAILQ_HEAD_INITIALIZER(efi_block_devs);
44 1.5.4.2 christos
45 1.5.4.2 christos static int
46 1.5.4.2 christos efi_block_parse(const char *fname, struct efi_block_part **pbpart, char **pfile)
47 1.5.4.2 christos {
48 1.5.4.2 christos struct efi_block_dev *bdev;
49 1.5.4.2 christos struct efi_block_part *bpart;
50 1.5.4.2 christos char pathbuf[PATH_MAX], *default_device, *ep = NULL;
51 1.5.4.2 christos const char *full_path;
52 1.5.4.2 christos intmax_t dev;
53 1.5.4.2 christos int part;
54 1.5.4.2 christos
55 1.5.4.2 christos default_device = get_default_device();
56 1.5.4.2 christos if (strchr(fname, ':') == NULL) {
57 1.5.4.2 christos if (strlen(default_device) > 0) {
58 1.5.4.2 christos snprintf(pathbuf, sizeof(pathbuf), "%s:%s", default_device, fname);
59 1.5.4.2 christos full_path = pathbuf;
60 1.5.4.2 christos *pfile = __UNCONST(fname);
61 1.5.4.2 christos } else {
62 1.5.4.2 christos return EINVAL;
63 1.5.4.2 christos }
64 1.5.4.2 christos } else {
65 1.5.4.2 christos full_path = fname;
66 1.5.4.2 christos *pfile = strchr(fname, ':') + 1;
67 1.5.4.2 christos }
68 1.5.4.2 christos
69 1.5.4.2 christos if (strncasecmp(full_path, "hd", 2) != 0)
70 1.5.4.2 christos return EINVAL;
71 1.5.4.2 christos dev = strtoimax(full_path + 2, &ep, 10);
72 1.5.4.2 christos if (dev < 0 || dev >= efi_nblock)
73 1.5.4.2 christos return ENXIO;
74 1.5.4.2 christos if (ep[0] < 'a' || ep[0] >= 'a' + MAXPARTITIONS || ep[1] != ':')
75 1.5.4.2 christos return EINVAL;
76 1.5.4.2 christos part = ep[0] - 'a';
77 1.5.4.2 christos TAILQ_FOREACH(bdev, &efi_block_devs, entries) {
78 1.5.4.2 christos if (bdev->index == dev) {
79 1.5.4.2 christos TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
80 1.5.4.2 christos if (bpart->index == part) {
81 1.5.4.2 christos *pbpart = bpart;
82 1.5.4.2 christos return 0;
83 1.5.4.2 christos }
84 1.5.4.2 christos }
85 1.5.4.2 christos }
86 1.5.4.2 christos }
87 1.5.4.2 christos
88 1.5.4.2 christos return ENOENT;
89 1.5.4.2 christos }
90 1.5.4.2 christos
91 1.5.4.2 christos static void
92 1.5.4.2 christos efi_block_generate_hash_mbr(struct efi_block_part *bpart, struct mbr_sector *mbr)
93 1.5.4.2 christos {
94 1.5.4.2 christos MD5_CTX md5ctx;
95 1.5.4.2 christos
96 1.5.4.2 christos MD5Init(&md5ctx);
97 1.5.4.2 christos MD5Update(&md5ctx, (void *)mbr, sizeof(*mbr));
98 1.5.4.2 christos MD5Final(bpart->hash, &md5ctx);
99 1.5.4.2 christos }
100 1.5.4.2 christos
101 1.5.4.3 martin static void *
102 1.5.4.3 martin efi_block_allocate_device_buffer(struct efi_block_dev *bdev, UINTN size,
103 1.5.4.3 martin void **buf_start)
104 1.5.4.3 martin {
105 1.5.4.3 martin void *buf;
106 1.5.4.3 martin
107 1.5.4.3 martin if (bdev->bio->Media->IoAlign <= 1)
108 1.5.4.3 martin *buf_start = buf = AllocatePool(size);
109 1.5.4.3 martin else {
110 1.5.4.3 martin buf = AllocatePool(size + bdev->bio->Media->IoAlign - 1);
111 1.5.4.3 martin *buf_start = (buf == NULL) ? NULL :
112 1.5.4.3 martin (void *)roundup2((intptr_t)buf, bdev->bio->Media->IoAlign);
113 1.5.4.3 martin }
114 1.5.4.3 martin
115 1.5.4.3 martin return buf;
116 1.5.4.3 martin }
117 1.5.4.3 martin
118 1.5.4.2 christos static int
119 1.5.4.2 christos efi_block_find_partitions_disklabel(struct efi_block_dev *bdev, struct mbr_sector *mbr, uint32_t start, uint32_t size)
120 1.5.4.2 christos {
121 1.5.4.2 christos struct efi_block_part *bpart;
122 1.5.4.2 christos struct disklabel d;
123 1.5.4.2 christos struct partition *p;
124 1.5.4.2 christos EFI_STATUS status;
125 1.5.4.2 christos EFI_LBA lba;
126 1.5.4.3 martin void *buf, *buf_start;
127 1.5.4.2 christos UINT32 sz;
128 1.5.4.2 christos int n;
129 1.5.4.2 christos
130 1.5.4.2 christos sz = __MAX(sizeof(d), bdev->bio->Media->BlockSize);
131 1.5.4.2 christos sz = roundup(sz, bdev->bio->Media->BlockSize);
132 1.5.4.3 martin if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
133 1.5.4.2 christos return ENOMEM;
134 1.5.4.2 christos
135 1.5.4.2 christos lba = (((EFI_LBA)start + LABELSECTOR) * DEV_BSIZE) / bdev->bio->Media->BlockSize;
136 1.5.4.3 martin status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
137 1.5.4.3 martin lba, sz, buf_start);
138 1.5.4.3 martin if (EFI_ERROR(status) || getdisklabel(buf_start, &d) != NULL) {
139 1.5.4.2 christos FreePool(buf);
140 1.5.4.2 christos return EIO;
141 1.5.4.2 christos }
142 1.5.4.2 christos FreePool(buf);
143 1.5.4.2 christos
144 1.5.4.2 christos if (le32toh(d.d_magic) != DISKMAGIC || le32toh(d.d_magic2) != DISKMAGIC)
145 1.5.4.2 christos return EINVAL;
146 1.5.4.2 christos if (le16toh(d.d_npartitions) > MAXPARTITIONS)
147 1.5.4.2 christos return EINVAL;
148 1.5.4.2 christos
149 1.5.4.2 christos for (n = 0; n < le16toh(d.d_npartitions); n++) {
150 1.5.4.2 christos p = &d.d_partitions[n];
151 1.5.4.2 christos switch (p->p_fstype) {
152 1.5.4.2 christos case FS_BSDFFS:
153 1.5.4.2 christos case FS_MSDOS:
154 1.5.4.2 christos case FS_BSDLFS:
155 1.5.4.2 christos break;
156 1.5.4.2 christos default:
157 1.5.4.2 christos continue;
158 1.5.4.2 christos }
159 1.5.4.2 christos
160 1.5.4.2 christos bpart = alloc(sizeof(*bpart));
161 1.5.4.2 christos bpart->index = n;
162 1.5.4.2 christos bpart->bdev = bdev;
163 1.5.4.2 christos bpart->type = EFI_BLOCK_PART_DISKLABEL;
164 1.5.4.2 christos bpart->disklabel.secsize = le32toh(d.d_secsize);
165 1.5.4.2 christos bpart->disklabel.part = *p;
166 1.5.4.2 christos efi_block_generate_hash_mbr(bpart, mbr);
167 1.5.4.2 christos TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
168 1.5.4.2 christos }
169 1.5.4.2 christos
170 1.5.4.2 christos return 0;
171 1.5.4.2 christos }
172 1.5.4.2 christos
173 1.5.4.2 christos static int
174 1.5.4.2 christos efi_block_find_partitions_mbr(struct efi_block_dev *bdev)
175 1.5.4.2 christos {
176 1.5.4.2 christos struct mbr_sector mbr;
177 1.5.4.2 christos struct mbr_partition *mbr_part;
178 1.5.4.2 christos EFI_STATUS status;
179 1.5.4.3 martin void *buf, *buf_start;
180 1.5.4.2 christos UINT32 sz;
181 1.5.4.2 christos int n;
182 1.5.4.2 christos
183 1.5.4.2 christos sz = __MAX(sizeof(mbr), bdev->bio->Media->BlockSize);
184 1.5.4.2 christos sz = roundup(sz, bdev->bio->Media->BlockSize);
185 1.5.4.3 martin if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
186 1.5.4.2 christos return ENOMEM;
187 1.5.4.2 christos
188 1.5.4.3 martin status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
189 1.5.4.3 martin 0, sz, buf_start);
190 1.5.4.2 christos if (EFI_ERROR(status)) {
191 1.5.4.2 christos FreePool(buf);
192 1.5.4.2 christos return EIO;
193 1.5.4.2 christos }
194 1.5.4.3 martin memcpy(&mbr, buf_start, sizeof(mbr));
195 1.5.4.2 christos FreePool(buf);
196 1.5.4.2 christos
197 1.5.4.2 christos if (le32toh(mbr.mbr_magic) != MBR_MAGIC)
198 1.5.4.2 christos return ENOENT;
199 1.5.4.2 christos
200 1.5.4.2 christos for (n = 0; n < MBR_PART_COUNT; n++) {
201 1.5.4.2 christos mbr_part = &mbr.mbr_parts[n];
202 1.5.4.2 christos if (le32toh(mbr_part->mbrp_size) == 0)
203 1.5.4.2 christos continue;
204 1.5.4.2 christos if (mbr_part->mbrp_type == MBR_PTYPE_NETBSD) {
205 1.5.4.2 christos efi_block_find_partitions_disklabel(bdev, &mbr, le32toh(mbr_part->mbrp_start), le32toh(mbr_part->mbrp_size));
206 1.5.4.2 christos break;
207 1.5.4.2 christos }
208 1.5.4.2 christos }
209 1.5.4.2 christos
210 1.5.4.2 christos return 0;
211 1.5.4.2 christos }
212 1.5.4.2 christos
213 1.5.4.2 christos static const struct {
214 1.5.4.2 christos struct uuid guid;
215 1.5.4.2 christos uint8_t fstype;
216 1.5.4.2 christos } gpt_guid_to_str[] = {
217 1.5.4.2 christos { GPT_ENT_TYPE_NETBSD_FFS, FS_BSDFFS },
218 1.5.4.2 christos { GPT_ENT_TYPE_NETBSD_LFS, FS_BSDLFS },
219 1.5.4.2 christos { GPT_ENT_TYPE_NETBSD_RAIDFRAME, FS_RAID },
220 1.5.4.2 christos { GPT_ENT_TYPE_NETBSD_CCD, FS_CCD },
221 1.5.4.2 christos { GPT_ENT_TYPE_NETBSD_CGD, FS_CGD },
222 1.5.4.2 christos { GPT_ENT_TYPE_MS_BASIC_DATA, FS_MSDOS }, /* or NTFS? ambiguous */
223 1.5.4.2 christos };
224 1.5.4.2 christos
225 1.5.4.2 christos static int
226 1.5.4.2 christos efi_block_find_partitions_gpt_entry(struct efi_block_dev *bdev, struct gpt_hdr *hdr, struct gpt_ent *ent, UINT32 index)
227 1.5.4.2 christos {
228 1.5.4.2 christos struct efi_block_part *bpart;
229 1.5.4.2 christos uint8_t fstype = FS_UNUSED;
230 1.5.4.2 christos struct uuid uuid;
231 1.5.4.2 christos int n;
232 1.5.4.2 christos
233 1.5.4.2 christos memcpy(&uuid, ent->ent_type, sizeof(uuid));
234 1.5.4.2 christos for (n = 0; n < __arraycount(gpt_guid_to_str); n++)
235 1.5.4.2 christos if (memcmp(ent->ent_type, &gpt_guid_to_str[n].guid, sizeof(ent->ent_type)) == 0) {
236 1.5.4.2 christos fstype = gpt_guid_to_str[n].fstype;
237 1.5.4.2 christos break;
238 1.5.4.2 christos }
239 1.5.4.2 christos if (fstype == FS_UNUSED)
240 1.5.4.2 christos return 0;
241 1.5.4.2 christos
242 1.5.4.2 christos bpart = alloc(sizeof(*bpart));
243 1.5.4.2 christos bpart->index = index;
244 1.5.4.2 christos bpart->bdev = bdev;
245 1.5.4.2 christos bpart->type = EFI_BLOCK_PART_GPT;
246 1.5.4.2 christos bpart->gpt.fstype = fstype;
247 1.5.4.2 christos bpart->gpt.ent = *ent;
248 1.5.4.2 christos memcpy(bpart->hash, ent->ent_guid, sizeof(bpart->hash));
249 1.5.4.2 christos TAILQ_INSERT_TAIL(&bdev->partitions, bpart, entries);
250 1.5.4.2 christos
251 1.5.4.2 christos return 0;
252 1.5.4.2 christos }
253 1.5.4.2 christos
254 1.5.4.2 christos static int
255 1.5.4.2 christos efi_block_find_partitions_gpt(struct efi_block_dev *bdev)
256 1.5.4.2 christos {
257 1.5.4.2 christos struct gpt_hdr hdr;
258 1.5.4.2 christos struct gpt_ent ent;
259 1.5.4.2 christos EFI_STATUS status;
260 1.5.4.3 martin void *buf, *buf_start;
261 1.5.4.2 christos UINT32 sz, entry;
262 1.5.4.2 christos
263 1.5.4.2 christos sz = __MAX(sizeof(hdr), bdev->bio->Media->BlockSize);
264 1.5.4.2 christos sz = roundup(sz, bdev->bio->Media->BlockSize);
265 1.5.4.3 martin if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
266 1.5.4.2 christos return ENOMEM;
267 1.5.4.2 christos
268 1.5.4.3 martin status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
269 1.5.4.3 martin GPT_HDR_BLKNO, sz, buf_start);
270 1.5.4.2 christos if (EFI_ERROR(status)) {
271 1.5.4.2 christos FreePool(buf);
272 1.5.4.2 christos return EIO;
273 1.5.4.2 christos }
274 1.5.4.3 martin memcpy(&hdr, buf_start, sizeof(hdr));
275 1.5.4.2 christos FreePool(buf);
276 1.5.4.2 christos
277 1.5.4.2 christos if (memcmp(hdr.hdr_sig, GPT_HDR_SIG, sizeof(hdr.hdr_sig)) != 0)
278 1.5.4.2 christos return ENOENT;
279 1.5.4.2 christos if (le32toh(hdr.hdr_entsz) < sizeof(ent))
280 1.5.4.2 christos return EINVAL;
281 1.5.4.2 christos
282 1.5.4.2 christos sz = __MAX(le32toh(hdr.hdr_entsz) * le32toh(hdr.hdr_entries), bdev->bio->Media->BlockSize);
283 1.5.4.2 christos sz = roundup(sz, bdev->bio->Media->BlockSize);
284 1.5.4.3 martin if ((buf = efi_block_allocate_device_buffer(bdev, sz, &buf_start)) == NULL)
285 1.5.4.2 christos return ENOMEM;
286 1.5.4.2 christos
287 1.5.4.3 martin status = uefi_call_wrapper(bdev->bio->ReadBlocks, 5, bdev->bio, bdev->media_id,
288 1.5.4.3 martin le64toh(hdr.hdr_lba_table), sz, buf_start);
289 1.5.4.2 christos if (EFI_ERROR(status)) {
290 1.5.4.2 christos FreePool(buf);
291 1.5.4.2 christos return EIO;
292 1.5.4.2 christos }
293 1.5.4.2 christos
294 1.5.4.2 christos for (entry = 0; entry < le32toh(hdr.hdr_entries); entry++) {
295 1.5.4.3 martin memcpy(&ent, buf_start + (entry * le32toh(hdr.hdr_entsz)),
296 1.5.4.3 martin sizeof(ent));
297 1.5.4.2 christos efi_block_find_partitions_gpt_entry(bdev, &hdr, &ent, entry);
298 1.5.4.2 christos }
299 1.5.4.2 christos
300 1.5.4.2 christos FreePool(buf);
301 1.5.4.2 christos
302 1.5.4.2 christos return 0;
303 1.5.4.2 christos }
304 1.5.4.2 christos
305 1.5.4.2 christos static int
306 1.5.4.2 christos efi_block_find_partitions(struct efi_block_dev *bdev)
307 1.5.4.2 christos {
308 1.5.4.2 christos int error;
309 1.5.4.2 christos
310 1.5.4.2 christos error = efi_block_find_partitions_gpt(bdev);
311 1.5.4.2 christos if (error)
312 1.5.4.2 christos error = efi_block_find_partitions_mbr(bdev);
313 1.5.4.2 christos
314 1.5.4.2 christos return error;
315 1.5.4.2 christos }
316 1.5.4.2 christos
317 1.5.4.2 christos void
318 1.5.4.2 christos efi_block_probe(void)
319 1.5.4.2 christos {
320 1.5.4.2 christos struct efi_block_dev *bdev;
321 1.5.4.2 christos struct efi_block_part *bpart;
322 1.5.4.2 christos EFI_BLOCK_IO *bio;
323 1.5.4.2 christos EFI_STATUS status;
324 1.5.4.2 christos uint16_t devindex = 0;
325 1.5.4.2 christos int depth = -1;
326 1.5.4.2 christos int n;
327 1.5.4.2 christos
328 1.5.4.2 christos status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, &efi_nblock, &efi_block);
329 1.5.4.2 christos if (EFI_ERROR(status))
330 1.5.4.2 christos return;
331 1.5.4.2 christos
332 1.5.4.2 christos if (efi_bootdp) {
333 1.5.4.2 christos depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH);
334 1.5.4.2 christos if (depth == 0)
335 1.5.4.2 christos depth = 1;
336 1.5.4.2 christos else if (depth == -1)
337 1.5.4.2 christos depth = 2;
338 1.5.4.2 christos }
339 1.5.4.2 christos
340 1.5.4.2 christos for (n = 0; n < efi_nblock; n++) {
341 1.5.4.2 christos status = uefi_call_wrapper(BS->HandleProtocol, 3, efi_block[n], &BlockIoProtocol, (void **)&bio);
342 1.5.4.2 christos if (EFI_ERROR(status) || !bio->Media->MediaPresent)
343 1.5.4.2 christos continue;
344 1.5.4.2 christos
345 1.5.4.2 christos if (bio->Media->LogicalPartition)
346 1.5.4.2 christos continue;
347 1.5.4.2 christos
348 1.5.4.2 christos bdev = alloc(sizeof(*bdev));
349 1.5.4.2 christos bdev->index = devindex++;
350 1.5.4.2 christos bdev->bio = bio;
351 1.5.4.2 christos bdev->media_id = bio->Media->MediaId;
352 1.5.4.2 christos bdev->path = DevicePathFromHandle(efi_block[n]);
353 1.5.4.2 christos TAILQ_INIT(&bdev->partitions);
354 1.5.4.2 christos TAILQ_INSERT_TAIL(&efi_block_devs, bdev, entries);
355 1.5.4.2 christos
356 1.5.4.2 christos efi_block_find_partitions(bdev);
357 1.5.4.2 christos
358 1.5.4.2 christos if (depth > 0 && efi_device_path_ncmp(efi_bootdp, DevicePathFromHandle(efi_block[n]), depth) == 0) {
359 1.5.4.2 christos TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
360 1.5.4.2 christos uint8_t fstype = FS_UNUSED;
361 1.5.4.2 christos switch (bpart->type) {
362 1.5.4.2 christos case EFI_BLOCK_PART_DISKLABEL:
363 1.5.4.2 christos fstype = bpart->disklabel.part.p_fstype;
364 1.5.4.2 christos break;
365 1.5.4.2 christos case EFI_BLOCK_PART_GPT:
366 1.5.4.2 christos fstype = bpart->gpt.fstype;
367 1.5.4.2 christos break;
368 1.5.4.2 christos }
369 1.5.4.2 christos if (fstype == FS_BSDFFS) {
370 1.5.4.2 christos char devname[9];
371 1.5.4.2 christos snprintf(devname, sizeof(devname), "hd%u%c", bdev->index, bpart->index + 'a');
372 1.5.4.2 christos set_default_device(devname);
373 1.5.4.2 christos break;
374 1.5.4.2 christos }
375 1.5.4.2 christos }
376 1.5.4.2 christos }
377 1.5.4.2 christos }
378 1.5.4.2 christos }
379 1.5.4.2 christos
380 1.5.4.2 christos static void
381 1.5.4.2 christos print_guid(const uint8_t *guid)
382 1.5.4.2 christos {
383 1.5.4.2 christos const int index[] = { 3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15 };
384 1.5.4.2 christos int i;
385 1.5.4.2 christos
386 1.5.4.2 christos for (i = 0; i < 16; i++) {
387 1.5.4.2 christos printf("%02x", guid[index[i]]);
388 1.5.4.2 christos if (i == 3 || i == 5 || i == 7 || i == 9)
389 1.5.4.2 christos printf("-");
390 1.5.4.2 christos }
391 1.5.4.2 christos }
392 1.5.4.2 christos
393 1.5.4.2 christos void
394 1.5.4.2 christos efi_block_show(void)
395 1.5.4.2 christos {
396 1.5.4.2 christos struct efi_block_dev *bdev;
397 1.5.4.2 christos struct efi_block_part *bpart;
398 1.5.4.2 christos uint64_t size;
399 1.5.4.2 christos CHAR16 *path;
400 1.5.4.2 christos
401 1.5.4.2 christos TAILQ_FOREACH(bdev, &efi_block_devs, entries) {
402 1.5.4.2 christos printf("hd%u (", bdev->index);
403 1.5.4.2 christos
404 1.5.4.2 christos /* Size in MB */
405 1.5.4.2 christos size = ((bdev->bio->Media->LastBlock + 1) * bdev->bio->Media->BlockSize) / (1024 * 1024);
406 1.5.4.2 christos if (size >= 10000)
407 1.5.4.2 christos printf("%"PRIu64" GB", size / 1024);
408 1.5.4.2 christos else
409 1.5.4.2 christos printf("%"PRIu64" MB", size);
410 1.5.4.2 christos printf("): ");
411 1.5.4.2 christos
412 1.5.4.2 christos path = DevicePathToStr(bdev->path);
413 1.5.4.2 christos Print(L"%s", path);
414 1.5.4.2 christos FreePool(path);
415 1.5.4.2 christos
416 1.5.4.2 christos printf("\n");
417 1.5.4.2 christos
418 1.5.4.2 christos TAILQ_FOREACH(bpart, &bdev->partitions, entries) {
419 1.5.4.2 christos switch (bpart->type) {
420 1.5.4.2 christos case EFI_BLOCK_PART_DISKLABEL:
421 1.5.4.2 christos printf(" hd%u%c (", bdev->index, bpart->index + 'a');
422 1.5.4.2 christos
423 1.5.4.2 christos /* Size in MB */
424 1.5.4.2 christos size = ((uint64_t)bpart->disklabel.secsize * bpart->disklabel.part.p_size) / (1024 * 1024);
425 1.5.4.2 christos if (size >= 10000)
426 1.5.4.2 christos printf("%"PRIu64" GB", size / 1024);
427 1.5.4.2 christos else
428 1.5.4.2 christos printf("%"PRIu64" MB", size);
429 1.5.4.2 christos printf("): ");
430 1.5.4.2 christos
431 1.5.4.2 christos printf("%s\n", fstypenames[bpart->disklabel.part.p_fstype]);
432 1.5.4.2 christos break;
433 1.5.4.2 christos case EFI_BLOCK_PART_GPT:
434 1.5.4.2 christos printf(" hd%u%c ", bdev->index, bpart->index + 'a');
435 1.5.4.2 christos
436 1.5.4.2 christos if (bpart->gpt.ent.ent_name[0] == 0x0000) {
437 1.5.4.2 christos printf("\"");
438 1.5.4.2 christos print_guid(bpart->gpt.ent.ent_guid);
439 1.5.4.2 christos printf("\"");
440 1.5.4.2 christos } else {
441 1.5.4.2 christos Print(L"\"%s\"", bpart->gpt.ent.ent_name);
442 1.5.4.2 christos }
443 1.5.4.2 christos
444 1.5.4.2 christos /* Size in MB */
445 1.5.4.2 christos size = (le64toh(bpart->gpt.ent.ent_lba_end) - le64toh(bpart->gpt.ent.ent_lba_start)) * bdev->bio->Media->BlockSize;
446 1.5.4.2 christos size /= (1024 * 1024);
447 1.5.4.2 christos if (size >= 10000)
448 1.5.4.2 christos printf(" (%"PRIu64" GB): ", size / 1024);
449 1.5.4.2 christos else
450 1.5.4.2 christos printf(" (%"PRIu64" MB): ", size);
451 1.5.4.2 christos
452 1.5.4.2 christos printf("%s\n", fstypenames[bpart->gpt.fstype]);
453 1.5.4.2 christos break;
454 1.5.4.2 christos default:
455 1.5.4.2 christos break;
456 1.5.4.2 christos }
457 1.5.4.2 christos }
458 1.5.4.2 christos }
459 1.5.4.2 christos }
460 1.5.4.2 christos
461 1.5.4.2 christos struct efi_block_part *
462 1.5.4.2 christos efi_block_boot_part(void)
463 1.5.4.2 christos {
464 1.5.4.2 christos return efi_block_booted;
465 1.5.4.2 christos }
466 1.5.4.2 christos
467 1.5.4.2 christos int
468 1.5.4.2 christos efi_block_open(struct open_file *f, ...)
469 1.5.4.2 christos {
470 1.5.4.2 christos struct efi_block_part *bpart;
471 1.5.4.2 christos const char *fname;
472 1.5.4.2 christos char **file;
473 1.5.4.2 christos char *path;
474 1.5.4.2 christos va_list ap;
475 1.5.4.2 christos int rv, n;
476 1.5.4.2 christos
477 1.5.4.2 christos va_start(ap, f);
478 1.5.4.2 christos fname = va_arg(ap, const char *);
479 1.5.4.2 christos file = va_arg(ap, char **);
480 1.5.4.2 christos va_end(ap);
481 1.5.4.2 christos
482 1.5.4.2 christos rv = efi_block_parse(fname, &bpart, &path);
483 1.5.4.2 christos if (rv != 0)
484 1.5.4.2 christos return rv;
485 1.5.4.2 christos
486 1.5.4.2 christos for (n = 0; n < ndevs; n++)
487 1.5.4.2 christos if (strcmp(DEV_NAME(&devsw[n]), "efiblock") == 0) {
488 1.5.4.2 christos f->f_dev = &devsw[n];
489 1.5.4.2 christos break;
490 1.5.4.2 christos }
491 1.5.4.2 christos if (n == ndevs)
492 1.5.4.2 christos return ENXIO;
493 1.5.4.2 christos
494 1.5.4.2 christos f->f_devdata = bpart;
495 1.5.4.2 christos
496 1.5.4.2 christos *file = path;
497 1.5.4.2 christos
498 1.5.4.2 christos efi_block_booted = bpart;
499 1.5.4.2 christos
500 1.5.4.2 christos return 0;
501 1.5.4.2 christos }
502 1.5.4.2 christos
503 1.5.4.2 christos int
504 1.5.4.2 christos efi_block_close(struct open_file *f)
505 1.5.4.2 christos {
506 1.5.4.2 christos return 0;
507 1.5.4.2 christos }
508 1.5.4.2 christos
509 1.5.4.2 christos int
510 1.5.4.2 christos efi_block_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
511 1.5.4.2 christos {
512 1.5.4.2 christos struct efi_block_part *bpart = devdata;
513 1.5.4.2 christos EFI_STATUS status;
514 1.5.4.3 martin void *allocated_buf, *aligned_buf;
515 1.5.4.2 christos
516 1.5.4.2 christos if (rw != F_READ)
517 1.5.4.2 christos return EROFS;
518 1.5.4.2 christos
519 1.5.4.2 christos switch (bpart->type) {
520 1.5.4.2 christos case EFI_BLOCK_PART_DISKLABEL:
521 1.5.4.2 christos if (bpart->bdev->bio->Media->BlockSize != bpart->disklabel.secsize) {
522 1.5.4.2 christos printf("%s: unsupported block size %d (expected %d)\n", __func__,
523 1.5.4.2 christos bpart->bdev->bio->Media->BlockSize, bpart->disklabel.secsize);
524 1.5.4.2 christos return EIO;
525 1.5.4.2 christos }
526 1.5.4.2 christos dblk += bpart->disklabel.part.p_offset;
527 1.5.4.2 christos break;
528 1.5.4.2 christos case EFI_BLOCK_PART_GPT:
529 1.5.4.2 christos if (bpart->bdev->bio->Media->BlockSize != DEV_BSIZE) {
530 1.5.4.2 christos printf("%s: unsupported block size %d (expected %d)\n", __func__,
531 1.5.4.2 christos bpart->bdev->bio->Media->BlockSize, DEV_BSIZE);
532 1.5.4.2 christos return EIO;
533 1.5.4.2 christos }
534 1.5.4.2 christos dblk += le64toh(bpart->gpt.ent.ent_lba_start);
535 1.5.4.2 christos break;
536 1.5.4.2 christos default:
537 1.5.4.2 christos return EINVAL;
538 1.5.4.2 christos }
539 1.5.4.2 christos
540 1.5.4.3 martin if ((bpart->bdev->bio->Media->IoAlign <= 1) ||
541 1.5.4.3 martin ((intptr_t)buf & (bpart->bdev->bio->Media->IoAlign - 1)) == 0) {
542 1.5.4.3 martin allocated_buf = NULL;
543 1.5.4.3 martin aligned_buf = buf;
544 1.5.4.3 martin } else if ((allocated_buf = efi_block_allocate_device_buffer(bpart->bdev,
545 1.5.4.3 martin size, &aligned_buf)) == NULL)
546 1.5.4.3 martin return ENOMEM;
547 1.5.4.3 martin
548 1.5.4.3 martin status = uefi_call_wrapper(bpart->bdev->bio->ReadBlocks, 5,
549 1.5.4.3 martin bpart->bdev->bio, bpart->bdev->media_id, dblk, size, aligned_buf);
550 1.5.4.3 martin if (EFI_ERROR(status)) {
551 1.5.4.3 martin if (allocated_buf != NULL)
552 1.5.4.3 martin FreePool(allocated_buf);
553 1.5.4.2 christos return EIO;
554 1.5.4.3 martin }
555 1.5.4.3 martin if (allocated_buf != NULL) {
556 1.5.4.3 martin memcpy(buf, aligned_buf, size);
557 1.5.4.3 martin FreePool(allocated_buf);
558 1.5.4.3 martin }
559 1.5.4.2 christos
560 1.5.4.2 christos *rsize = size;
561 1.5.4.2 christos
562 1.5.4.2 christos return 0;
563 1.5.4.2 christos }
564