8010 loader: want mechanism to avoid RA with bcache
[unleashed.git] / usr / src / boot / sys / boot / efi / libefi / efipart.c
blobfb65fe82cc105a836396e23f52cc35e36263f5e3
1 /*-
2 * Copyright (c) 2010 Marcel Moolenaar
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
27 #include <sys/cdefs.h>
29 #include <sys/disk.h>
30 #include <sys/param.h>
31 #include <sys/time.h>
32 #include <stddef.h>
33 #include <stdarg.h>
35 #include <bootstrap.h>
37 #include <efi.h>
38 #include <efilib.h>
39 #include <efiprot.h>
41 static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
43 static int efipart_init(void);
44 static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
45 static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
46 static int efipart_open(struct open_file *, ...);
47 static int efipart_close(struct open_file *);
48 static int efipart_ioctl(struct open_file *, u_long, void *);
49 static int efipart_print(int);
51 struct devsw efipart_dev = {
52 .dv_name = "part",
53 .dv_type = DEVT_DISK,
54 .dv_init = efipart_init,
55 .dv_strategy = efipart_strategy,
56 .dv_open = efipart_open,
57 .dv_close = efipart_close,
58 .dv_ioctl = efipart_ioctl,
59 .dv_print = efipart_print,
60 .dv_cleanup = NULL
64 * info structure to support bcache
66 struct pdinfo
68 int pd_unit; /* unit number */
69 int pd_open; /* reference counter */
70 void *pd_bcache; /* buffer cache data */
72 static struct pdinfo *pdinfo;
73 static int npdinfo = 0;
75 #define PD(dev) (pdinfo[(dev)->d_unit])
77 static int
78 efipart_init(void)
80 EFI_BLOCK_IO *blkio;
81 EFI_DEVICE_PATH *devpath, *devpathcpy, *tmpdevpath, *node;
82 EFI_HANDLE *hin, *hout, *aliases, handle;
83 EFI_STATUS status;
84 UINTN sz;
85 u_int n, nin, nout;
86 int err;
88 sz = 0;
89 hin = NULL;
90 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0);
91 if (status == EFI_BUFFER_TOO_SMALL) {
92 hin = (EFI_HANDLE *)malloc(sz * 3);
93 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
94 hin);
95 if (EFI_ERROR(status))
96 free(hin);
98 if (EFI_ERROR(status))
99 return (efi_status_to_errno(status));
101 /* Filter handles to only include illumos partitions. */
102 nin = sz / sizeof(EFI_HANDLE);
103 hout = hin + nin;
104 aliases = hout + nin;
105 nout = 0;
107 bzero(aliases, nin * sizeof(EFI_HANDLE));
108 pdinfo = malloc(nin * sizeof(*pdinfo));
109 if (pdinfo == NULL)
110 return (ENOMEM);
112 for (n = 0; n < nin; n++) {
113 devpath = efi_lookup_devpath(hin[n]);
114 if (devpath == NULL) {
115 continue;
118 status = BS->HandleProtocol(hin[n], &blkio_guid,
119 (void**)&blkio);
120 if (EFI_ERROR(status))
121 continue;
122 if (!blkio->Media->LogicalPartition)
123 continue;
126 * If we come across a logical partition of subtype CDROM
127 * it doesn't refer to the CD filesystem itself, but rather
128 * to any usable El Torito boot image on it. In this case
129 * we try to find the parent device and add that instead as
130 * that will be the CD filesystem.
132 if ((node = efi_devpath_last_node(devpath)) == NULL)
133 continue;
134 if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
135 DevicePathSubType(node) == MEDIA_CDROM_DP) {
136 devpathcpy = efi_devpath_trim(devpath);
137 if (devpathcpy == NULL)
138 continue;
139 tmpdevpath = devpathcpy;
140 status = BS->LocateDevicePath(&blkio_guid, &tmpdevpath,
141 &handle);
142 free(devpathcpy);
143 if (EFI_ERROR(status))
144 continue;
145 hout[nout] = handle;
146 aliases[nout] = hin[n];
147 } else
148 hout[nout] = hin[n];
149 nout++;
150 pdinfo[npdinfo].pd_open = 0;
151 pdinfo[npdinfo].pd_bcache = NULL;
152 pdinfo[npdinfo].pd_unit = npdinfo;
153 npdinfo++;
156 bcache_add_dev(npdinfo);
157 err = efi_register_handles(&efipart_dev, hout, aliases, nout);
158 free(hin);
159 return (err);
162 static int
163 efipart_print(int verbose)
165 char line[80];
166 EFI_BLOCK_IO *blkio;
167 EFI_HANDLE h;
168 EFI_STATUS status;
169 u_int unit;
170 int ret = 0;
172 printf("%s devices:", efipart_dev.dv_name);
173 if ((ret = pager_output("\n")) != 0)
174 return (ret);
176 for (unit = 0, h = efi_find_handle(&efipart_dev, 0);
177 h != NULL; h = efi_find_handle(&efipart_dev, ++unit)) {
178 sprintf(line, " %s%d:", efipart_dev.dv_name, unit);
179 ret = pager_output(line);
181 status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
182 if (!EFI_ERROR(status)) {
183 sprintf(line, " %llu blocks",
184 (unsigned long long)(blkio->Media->LastBlock + 1));
185 ret = pager_output(line);
186 if (blkio->Media->RemovableMedia)
187 ret = pager_output(" (removable)");
189 ret = pager_output("\n");
190 if (ret != 0)
191 break;
193 return (ret);
196 static int
197 efipart_open(struct open_file *f, ...)
199 va_list args;
200 struct devdesc *dev;
201 EFI_BLOCK_IO *blkio;
202 EFI_HANDLE h;
203 EFI_STATUS status;
205 va_start(args, f);
206 dev = va_arg(args, struct devdesc*);
207 va_end(args);
209 h = efi_find_handle(&efipart_dev, dev->d_unit);
210 if (h == NULL)
211 return (EINVAL);
213 status = BS->HandleProtocol(h, &blkio_guid, (void **)&blkio);
214 if (EFI_ERROR(status))
215 return (efi_status_to_errno(status));
217 if (!blkio->Media->MediaPresent)
218 return (EAGAIN);
220 dev->d_opendata = blkio;
221 PD(dev).pd_open++;
222 if (PD(dev).pd_bcache == NULL)
223 PD(dev).pd_bcache = bcache_allocate();
224 return (0);
227 static int
228 efipart_close(struct open_file *f)
230 struct devdesc *dev;
232 dev = (struct devdesc *)(f->f_devdata);
233 if (dev->d_opendata == NULL)
234 return (EINVAL);
236 dev->d_opendata = NULL;
237 PD(dev).pd_open--;
238 if (PD(dev).pd_open == 0) {
239 bcache_free(PD(dev).pd_bcache);
240 PD(dev).pd_bcache = NULL;
242 return (0);
245 static int
246 efipart_ioctl(struct open_file *f, u_long cmd, void *data)
248 struct devdesc *dev;
249 EFI_BLOCK_IO *blkio;
251 dev = (struct devdesc *)(f->f_devdata);
252 if (dev->d_opendata == NULL)
253 return (EINVAL);
254 blkio = dev->d_opendata;
256 switch (cmd) {
257 case DIOCGSECTORSIZE:
258 *(u_int *)data = blkio->Media->BlockSize;
259 break;
260 case DIOCGMEDIASIZE:
261 *(uint64_t *)data = blkio->Media->BlockSize *
262 (blkio->Media->LastBlock + 1);
263 break;
264 default:
265 return (ENOTTY);
268 return (0);
272 * efipart_readwrite()
273 * Internal equivalent of efipart_strategy(), which operates on the
274 * media-native block size. This function expects all I/O requests
275 * to be within the media size and returns an error if such is not
276 * the case.
278 static int
279 efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
280 char *buf)
282 EFI_STATUS status;
284 if (blkio == NULL)
285 return (ENXIO);
286 if (blk < 0 || blk > blkio->Media->LastBlock)
287 return (EIO);
288 if ((blk + nblks - 1) > blkio->Media->LastBlock)
289 return (EIO);
291 switch (rw & F_MASK) {
292 case F_READ:
293 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
294 nblks * blkio->Media->BlockSize, buf);
295 break;
296 case F_WRITE:
297 if (blkio->Media->ReadOnly)
298 return (EROFS);
299 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
300 nblks * blkio->Media->BlockSize, buf);
301 break;
302 default:
303 return (ENOSYS);
306 if (EFI_ERROR(status))
307 printf("%s: rw=%d, status=%lu\n", __func__, rw, (u_long)status);
308 return (efi_status_to_errno(status));
311 static int
312 efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
313 char *buf, size_t *rsize)
315 struct bcache_devdata bcd;
316 struct devdesc *dev;
318 dev = (struct devdesc *)devdata;
319 bcd.dv_strategy = efipart_realstrategy;
320 bcd.dv_devdata = devdata;
321 bcd.dv_cache = PD(dev).pd_bcache;
322 return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
325 static int
326 efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
327 char *buf, size_t *rsize)
329 struct devdesc *dev = (struct devdesc *)devdata;
330 EFI_BLOCK_IO *blkio;
331 uint64_t off;
332 char *blkbuf;
333 size_t blkoff, blksz;
334 int error;
336 if (dev == NULL || blk < 0)
337 return (EINVAL);
339 blkio = dev->d_opendata;
340 if (blkio == NULL)
341 return (ENXIO);
343 if (size == 0 || (size % 512) != 0)
344 return (EIO);
346 off = blk * 512;
347 /* make sure we don't read past disk end */
348 if ((off + size) / blkio->Media->BlockSize - 1 >
349 blkio->Media->LastBlock) {
350 size = blkio->Media->LastBlock + 1 -
351 off / blkio->Media->BlockSize;
352 size = size * blkio->Media->BlockSize;
355 if (rsize != NULL)
356 *rsize = size;
358 if ((size % blkio->Media->BlockSize == 0) &&
359 (off % blkio->Media->BlockSize == 0))
360 return (efipart_readwrite(blkio, rw,
361 off / blkio->Media->BlockSize,
362 size / blkio->Media->BlockSize, buf));
365 * The buffer size is not a multiple of the media block size.
367 blkbuf = malloc(blkio->Media->BlockSize);
368 if (blkbuf == NULL)
369 return (ENOMEM);
371 error = 0;
372 blk = off / blkio->Media->BlockSize;
373 blkoff = off % blkio->Media->BlockSize;
374 blksz = blkio->Media->BlockSize - blkoff;
375 while (size > 0) {
376 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf);
377 if (error)
378 break;
379 if (size < blksz)
380 blksz = size;
381 bcopy(blkbuf + blkoff, buf, blksz);
382 buf += blksz;
383 size -= blksz;
384 blk++;
385 blkoff = 0;
386 blksz = blkio->Media->BlockSize;
389 free(blkbuf);
390 return (error);