1/*- 2 * Copyright (c) 2010 Marcel Moolenaar 3 * All rights reserved. 4 * 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. 13 * 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. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include <sys/param.h> 31#include <sys/time.h> 32#include <stddef.h> 33#include <stdarg.h> 34 35#include <bootstrap.h> 36 37#include <efi.h> 38#include <efilib.h> 39#include <efiprot.h> 40 41static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL; 42 43static int efipart_init(void); 44static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *); 45static int efipart_open(struct open_file *, ...); 46static int efipart_close(struct open_file *); 47static void efipart_print(int); 48 49struct devsw efipart_dev = { 50 .dv_name = "part", 51 .dv_type = DEVT_DISK, 52 .dv_init = efipart_init, 53 .dv_strategy = efipart_strategy, 54 .dv_open = efipart_open, 55 .dv_close = efipart_close, 56 .dv_ioctl = noioctl, 57 .dv_print = efipart_print, 58 .dv_cleanup = NULL 59}; 60 61static int 62efipart_init(void) 63{ 64 EFI_BLOCK_IO *blkio; 65 EFI_HANDLE *hin, *hout; 66 EFI_STATUS status; 67 UINTN sz; 68 u_int n, nin, nout; 69 int err; 70 71 sz = 0; 72 hin = NULL; 73 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 0); 74 if (status == EFI_BUFFER_TOO_SMALL) { 75 hin = (EFI_HANDLE *)malloc(sz * 2); 76 status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, 77 hin); 78 if (EFI_ERROR(status)) 79 free(hin); 80 } 81 if (EFI_ERROR(status)) 82 return (efi_status_to_errno(status)); 83 84 /* Filter handles to only include FreeBSD partitions. */ 85 nin = sz / sizeof(EFI_HANDLE); 86 hout = hin + nin; 87 nout = 0; 88 89 for (n = 0; n < nin; n++) { 90 status = BS->HandleProtocol(hin[n], &blkio_guid, &blkio); 91 if (EFI_ERROR(status)) 92 continue; 93 if (!blkio->Media->LogicalPartition) 94 continue; 95 hout[nout] = hin[n]; 96 nout++; 97 } 98 99 err = efi_register_handles(&efipart_dev, hout, nout); 100 free(hin); 101 return (err); 102} 103 104static void 105efipart_print(int verbose) 106{ 107 char line[80]; 108 EFI_BLOCK_IO *blkio; 109 EFI_HANDLE h; 110 EFI_STATUS status; 111 u_int unit; 112 113 for (unit = 0, h = efi_find_handle(&efipart_dev, 0); 114 h != NULL; h = efi_find_handle(&efipart_dev, ++unit)) { 115 sprintf(line, " %s%d:", efipart_dev.dv_name, unit); 116 pager_output(line); 117 118 status = BS->HandleProtocol(h, &blkio_guid, &blkio); 119 if (!EFI_ERROR(status)) { 120 sprintf(line, " %llu blocks", 121 (unsigned long long)(blkio->Media->LastBlock + 1)); 122 pager_output(line); 123 if (blkio->Media->RemovableMedia) 124 pager_output(" (removable)"); 125 } 126 pager_output("\n"); 127 } 128} 129 130static int 131efipart_open(struct open_file *f, ...) 132{ 133 va_list args; 134 struct devdesc *dev; 135 EFI_BLOCK_IO *blkio; 136 EFI_HANDLE h; 137 EFI_STATUS status; 138 139 va_start(args, f); 140 dev = va_arg(args, struct devdesc*); 141 va_end(args); 142 143 h = efi_find_handle(&efipart_dev, dev->d_unit); 144 if (h == NULL) 145 return (EINVAL); 146 147 status = BS->HandleProtocol(h, &blkio_guid, &blkio); 148 if (EFI_ERROR(status)) 149 return (efi_status_to_errno(status)); 150 151 if (!blkio->Media->MediaPresent) 152 return (EAGAIN); 153 154 dev->d_opendata = blkio; 155 return (0); 156} 157 158static int 159efipart_close(struct open_file *f) 160{ 161 struct devdesc *dev; 162 163 dev = (struct devdesc *)(f->f_devdata); 164 if (dev->d_opendata == NULL) 165 return (EINVAL); 166 167 dev->d_opendata = NULL; 168 return (0); 169} 170 171/* 172 * efipart_readwrite() 173 * Internal equivalent of efipart_strategy(), which operates on the 174 * media-native block size. This function expects all I/O requests 175 * to be within the media size and returns an error if such is not 176 * the case. 177 */ 178static int 179efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks, 180 char *buf) 181{ 182 EFI_STATUS status; 183 184 if (blkio == NULL) 185 return (ENXIO); 186 if (blk < 0 || blk > blkio->Media->LastBlock) 187 return (EIO); 188 if ((blk + nblks - 1) > blkio->Media->LastBlock) 189 return (EIO); 190 191 switch (rw) { 192 case F_READ: 193 status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk, 194 nblks * blkio->Media->BlockSize, buf); 195 break; 196 case F_WRITE: 197 if (blkio->Media->ReadOnly) 198 return (EROFS); 199 status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk, 200 nblks * blkio->Media->BlockSize, buf); 201 break; 202 default: 203 return (ENOSYS); 204 } 205 206 if (EFI_ERROR(status)) 207 printf("%s: rw=%d, status=%lu\n", __func__, rw, (u_long)status); 208 return (efi_status_to_errno(status)); 209} 210 211static int 212efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size, char *buf, 213 size_t *rsize) 214{ 215 struct devdesc *dev = (struct devdesc *)devdata; 216 EFI_BLOCK_IO *blkio; 217 off_t off; 218 char *blkbuf; 219 size_t blkoff, blksz; 220 int error; 221 222 if (dev == NULL || blk < 0) 223 return (EINVAL); 224 225 blkio = dev->d_opendata; 226 if (blkio == NULL) 227 return (ENXIO); 228 229 if (size == 0 || (size % 512) != 0) 230 return (EIO); 231 232 if (rsize != NULL) 233 *rsize = size; 234 235 if (blkio->Media->BlockSize == 512) 236 return (efipart_readwrite(blkio, rw, blk, size / 512, buf)); 237 238 /* 239 * The block size of the media is not 512B per sector. 240 */ 241 blkbuf = malloc(blkio->Media->BlockSize); 242 if (blkbuf == NULL) 243 return (ENOMEM); 244 245 error = 0; 246 off = blk * 512; 247 blk = off / blkio->Media->BlockSize; 248 blkoff = off % blkio->Media->BlockSize; 249 blksz = blkio->Media->BlockSize - blkoff; 250 while (size > 0) { 251 error = efipart_readwrite(blkio, rw, blk, 1, blkbuf); 252 if (error) 253 break; 254 if (size < blksz) 255 blksz = size; 256 bcopy(blkbuf + blkoff, buf, blksz); 257 buf += blksz; 258 size -= blksz; 259 blk++; 260 blkoff = 0; 261 blksz = blkio->Media->BlockSize; 262 } 263 264 free(blkbuf); 265 return (error); 266} 267