1 /*-
2 * Copyright (c) 2019 Netflix, Inc
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 #include <sys/param.h>
27 #include <machine/elf.h>
28 #include <machine/stdarg.h>
29 #include <stand.h>
30
31 #include <efi.h>
32 #include <eficonsctl.h>
33 #include <efichar.h>
34
35 #include "boot_module.h"
36 #include "paths.h"
37 #include "proto.h"
38
39 #include "gpt.h"
40 #include <sys/gpt.h>
41 static const uuid_t freebsd_ufs_uuid = GPT_ENT_TYPE_FREEBSD_UFS;
42 static char secbuf[4096] __aligned(4096);
43 static struct dsk dsk;
44 static dev_info_t *devices = NULL;
45 static dev_info_t *raw_device = NULL;
46
47 static EFI_GUID BlockIoProtocolGUID = BLOCK_IO_PROTOCOL;
48 static EFI_GUID DevicePathGUID = DEVICE_PATH_PROTOCOL;
49
50 /*
51 * Shim routine for the gpt code to read in the gpt table. The
52 * devinfo is always going to be for the raw device.
53 */
54 int
drvread(struct dsk * dskp,void * buf,daddr_t lba,unsigned nblk)55 drvread(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
56 {
57 int size;
58 EFI_STATUS status;
59 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
60 EFI_BLOCK_IO *dev = devinfo->dev;
61
62 lba = lba / (dev->Media->BlockSize / DEV_BSIZE);
63 size = nblk * DEV_BSIZE;
64
65 status = dev->ReadBlocks(dev, dev->Media->MediaId, lba, size, buf);
66 if (status != EFI_SUCCESS) {
67 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
68 "status: %lu\n", devinfo->dev,
69 dev->Media->MediaId, (uintmax_t)lba, size,
70 EFI_ERROR_CODE(status));
71 return (-1);
72 }
73
74 return (0);
75 }
76
77 /*
78 * Shim routine for the gpt code to write in the gpt table. The
79 * devinfo is always going to be for the raw device.
80 */
81 int
drvwrite(struct dsk * dskp,void * buf,daddr_t lba,unsigned nblk)82 drvwrite(struct dsk *dskp, void *buf, daddr_t lba, unsigned nblk)
83 {
84 int size;
85 EFI_STATUS status;
86 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
87 EFI_BLOCK_IO *dev = devinfo->dev;
88
89 if (dev->Media->ReadOnly)
90 return -1;
91
92 lba = lba / (dev->Media->BlockSize / DEV_BSIZE);
93 size = nblk * DEV_BSIZE;
94
95 status = dev->WriteBlocks(dev, dev->Media->MediaId, lba, size, buf);
96 if (status != EFI_SUCCESS) {
97 DPRINTF("dskread: failed dev: %p, id: %u, lba: %ju, size: %d, "
98 "status: %lu\n", devinfo->dev,
99 dev->Media->MediaId, (uintmax_t)lba, size,
100 EFI_ERROR_CODE(status));
101 return (-1);
102 }
103
104 return (0);
105 }
106
107 /*
108 * Return the number of LBAs the drive has.
109 */
110 uint64_t
drvsize(struct dsk * dskp)111 drvsize(struct dsk *dskp)
112 {
113 dev_info_t *devinfo = (dev_info_t *)dskp->devinfo;
114 EFI_BLOCK_IO *dev = devinfo->dev;
115
116 return (dev->Media->LastBlock + 1);
117 }
118
119 static int
partition_number(EFI_DEVICE_PATH * devpath)120 partition_number(EFI_DEVICE_PATH *devpath)
121 {
122 EFI_DEVICE_PATH *md;
123 HARDDRIVE_DEVICE_PATH *hd;
124
125 md = efi_devpath_last_node(devpath);
126 if (md == NULL)
127 return (-1);
128 if (DevicePathSubType(md) != MEDIA_HARDDRIVE_DP)
129 return (-1);
130 hd = (HARDDRIVE_DEVICE_PATH *)md;
131 return (hd->PartitionNumber);
132 }
133
134 /*
135 * Find the raw partition for the imgpath and save it
136 */
137 static void
probe_handle(EFI_HANDLE h,EFI_DEVICE_PATH * imgpath)138 probe_handle(EFI_HANDLE h, EFI_DEVICE_PATH *imgpath)
139 {
140 dev_info_t *devinfo;
141 EFI_BLOCK_IO *blkio;
142 EFI_DEVICE_PATH *devpath, *trimmed = NULL;
143 EFI_STATUS status;
144
145 /* Figure out if we're dealing with an actual partition. */
146 status = OpenProtocolByHandle(h, &DevicePathGUID, (void **)&devpath);
147 if (status != EFI_SUCCESS)
148 return;
149 #ifdef EFI_DEBUG
150 {
151 CHAR16 *text = efi_devpath_name(devpath);
152 DPRINTF("probing: %S ", text);
153 efi_free_devpath_name(text);
154 }
155 #endif
156 /*
157 * The RAW device is the same as the imgpath with the last
158 * MEDIA_DEVICE bit trimmed off. imgpath will end with the
159 * MEDIA_DEVICE for the ESP we booted off of.
160 */
161 if (!efi_devpath_same_disk(imgpath, devpath)) {
162 trimmed = efi_devpath_trim(imgpath);
163 if (!efi_devpath_match(trimmed, devpath)) {
164 free(trimmed);
165 DPRINTF("Not the same disk\n");
166 return;
167 }
168 }
169 status = OpenProtocolByHandle(h, &BlockIoProtocolGUID, (void **)&blkio);
170 if (status != EFI_SUCCESS) {
171 DPRINTF("Can't get the block I/O protocol block\n");
172 return;
173 }
174 devinfo = malloc(sizeof(*devinfo));
175 if (devinfo == NULL) {
176 DPRINTF("Failed to allocate devinfo\n");
177 return;
178 }
179 devinfo->dev = blkio;
180 devinfo->devpath = devpath;
181 devinfo->devhandle = h;
182 devinfo->preferred = 1;
183 devinfo->next = NULL;
184 devinfo->devdata = NULL;
185 if (trimmed == NULL) {
186 DPRINTF("Found partition %d\n", partition_number(devpath));
187 add_device(&devices, devinfo);
188 } else {
189 free(trimmed);
190 DPRINTF("Found raw device\n");
191 if (raw_device) {
192 printf(BOOTPROG": Found two raw devices, inconceivable?\n");
193 return;
194 }
195 raw_device = devinfo;
196 }
197 }
198
199 static void
probe_handles(EFI_HANDLE * handles,UINTN nhandles,EFI_DEVICE_PATH * imgpath)200 probe_handles(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
201 {
202 UINTN i;
203
204 for (i = 0; i < nhandles; i++)
205 probe_handle(handles[i], imgpath);
206 }
207
208 static dev_info_t *
find_partition(int part)209 find_partition(int part)
210 {
211 dev_info_t *dev;
212
213 if (part == 0)
214 return (NULL);
215 for (dev = devices; dev != NULL; dev = dev->next)
216 if (partition_number(dev->devpath) == part)
217 break;
218 return (dev);
219 }
220
221 void
choice_protocol(EFI_HANDLE * handles,UINTN nhandles,EFI_DEVICE_PATH * imgpath)222 choice_protocol(EFI_HANDLE *handles, UINTN nhandles, EFI_DEVICE_PATH *imgpath)
223 {
224 const boot_module_t *mod = &ufs_module;
225 dev_info_t *bootdev;
226 void *loaderbuf;
227 size_t loadersize;
228 int parts;
229 const char *fn = PATH_LOADER_EFI;
230
231 /*
232 * Probe the provided handles to find the partitions that
233 * are on the same drive.
234 */
235 probe_handles(handles, nhandles, imgpath);
236 dsk.devinfo = raw_device;
237 if (dsk.devinfo == NULL) {
238 printf(BOOTPROG": unable to find raw disk to read gpt\n");
239 return;
240 }
241
242 /*
243 * Read in the GPT table, and then find the right partition.
244 * gptread, gptfind and gptfaileboot are shared with the
245 * BIOS version of the gptboot program.
246 */
247 if (gptread(&dsk, secbuf) == -1) {
248 printf(BOOTPROG ": unable to load GPT\n");
249 return;
250 }
251 // XXX:
252 // real gptboot can parse a command line before trying this loop.
253 // But since we don't parse anything at all, hard wire the partition
254 // to be -1 (meaning look for the next one).
255 parts = 0;
256 while (gptfind(&freebsd_ufs_uuid, &dsk, -1) != -1) {
257 parts++;
258 bootdev = find_partition(dsk.part);
259 if (bootdev == NULL) {
260 printf(BOOTPROG": Can't find partition %d\n",
261 dsk.part);
262 goto next;
263 }
264 if (mod->load(fn, bootdev, &loaderbuf, &loadersize) !=
265 EFI_SUCCESS) {
266 printf(BOOTPROG": Can't load %s from partition %d\n",
267 fn, dsk.part);
268 goto next;
269 }
270 try_boot(mod, bootdev, loaderbuf, loadersize);
271 next:
272 gptbootfailed(&dsk);
273 }
274 if (parts == 0)
275 printf("%s: no UFS partition was found\n", BOOTPROG);
276 }
277