1 /* $NetBSD: efifs.c,v 1.4 2009/07/20 04:59:03 kiyohara Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 Doug Rabson 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 AUTHOR 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 AUTHOR 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 * $FreeBSD: src/sys/boot/efi/libefi/efifs.c,v 1.8 2003/08/02 08:22:03 marcel Exp $ 29 */ 30 31 #include <sys/time.h> 32 #include <sys/dirent.h> 33 #include <lib/libsa/stand.h> 34 #include <lib/libsa/loadfile.h> 35 #include <lib/libkern/libkern.h> 36 #include <machine/stdarg.h> 37 38 #include <efi.h> 39 #include <efilib.h> 40 41 #include <bootstrap.h> 42 43 #include "efiboot.h" 44 45 /* Perform I/O in blocks of size EFI_BLOCK_SIZE. */ 46 #define EFI_BLOCK_SIZE (1024 * 1024) 47 48 49 int 50 efifs_open(const char *upath, struct open_file *f) 51 { 52 struct efi_devdesc *dev = f->f_devdata; 53 static EFI_GUID sfsid = SIMPLE_FILE_SYSTEM_PROTOCOL; 54 EFI_FILE_IO_INTERFACE *sfs; 55 EFI_FILE *root; 56 EFI_FILE *file; 57 EFI_STATUS status; 58 CHAR16 *cp; 59 CHAR16 *path; 60 61 /* 62 * We cannot blindly assume that f->f_devdata points to a 63 * efi_devdesc structure. Before we dereference 'dev', make 64 * sure that the underlying device is ours. 65 */ 66 if (f->f_dev != &devsw[0] || dev->d_handle == NULL) 67 return ENOENT; 68 69 status = BS->HandleProtocol(dev->d_handle, &sfsid, (VOID **)&sfs); 70 if (EFI_ERROR(status)) 71 return ENOENT; 72 73 /* 74 * Find the root directory. 75 */ 76 status = sfs->OpenVolume(sfs, &root); 77 78 /* 79 * Convert path to CHAR16, skipping leading separators. 80 */ 81 while (*upath == '/') 82 upath++; 83 if (!*upath) { 84 /* Opening the root directory, */ 85 f->f_fsdata = root; 86 return 0; 87 } 88 cp = path = alloc((strlen(upath) + 1) * sizeof(CHAR16)); 89 if (path == NULL) 90 return ENOMEM; 91 while (*upath) { 92 if (*upath == '/') 93 *cp = '\\'; 94 else 95 *cp = *upath; 96 upath++; 97 cp++; 98 } 99 *cp++ = 0; 100 101 /* 102 * Try to open it. 103 */ 104 status = root->Open(root, &file, path, EFI_FILE_MODE_READ, 0); 105 free(path); 106 if (EFI_ERROR(status)) { 107 root->Close(root); 108 return ENOENT; 109 } 110 111 root->Close(root); 112 f->f_fsdata = file; 113 return 0; 114 } 115 116 int 117 efifs_close(struct open_file *f) 118 { 119 EFI_FILE *file = f->f_fsdata; 120 121 file->Close(file); 122 return 0; 123 } 124 125 int 126 efifs_read(struct open_file *f, void *buf, size_t size, size_t *resid) 127 { 128 EFI_FILE *file = f->f_fsdata; 129 EFI_STATUS status; 130 UINTN sz = size; 131 char *bufp; 132 133 bufp = buf; 134 while (size > 0) { 135 sz = size; 136 if (sz > EFI_BLOCK_SIZE) 137 sz = EFI_BLOCK_SIZE; 138 status = file->Read(file, &sz, bufp); 139 140 #if !defined(LIBSA_NO_TWIDDLE) 141 twiddle(); 142 #endif 143 144 if (EFI_ERROR(status)) 145 return EIO; 146 if (sz == 0) 147 break; 148 size -= sz; 149 bufp += sz; 150 } 151 if (resid) 152 *resid = size; 153 return 0; 154 } 155 156 int 157 efifs_write(struct open_file *f, void *buf, size_t size, size_t *resid) 158 { 159 EFI_FILE *file = f->f_fsdata; 160 EFI_STATUS status; 161 UINTN sz = size; 162 char *bufp; 163 164 bufp = buf; 165 while (size > 0) { 166 sz = size; 167 if (sz > EFI_BLOCK_SIZE) 168 sz = EFI_BLOCK_SIZE; 169 status = file->Write(file, &sz, bufp); 170 171 #if !defined(LIBSA_NO_TWIDDLE) 172 twiddle(); 173 #endif 174 175 if (EFI_ERROR(status)) 176 return EIO; 177 if (sz == 0) 178 break; 179 size -= sz; 180 bufp += sz; 181 } 182 if (resid) 183 *resid = size; 184 return 0; 185 } 186 187 off_t 188 efifs_seek(struct open_file *f, off_t offset, int where) 189 { 190 EFI_FILE *file = f->f_fsdata; 191 EFI_STATUS status; 192 UINT64 base; 193 UINTN sz; 194 static EFI_GUID infoid = EFI_FILE_INFO_ID; 195 EFI_FILE_INFO info; 196 197 switch (where) { 198 case SEEK_SET: 199 base = 0; 200 break; 201 202 case SEEK_CUR: 203 status = file->GetPosition(file, &base); 204 if (EFI_ERROR(status)) 205 return -1; 206 break; 207 208 case SEEK_END: 209 sz = sizeof(info); 210 status = file->GetInfo(file, &infoid, &sz, &info); 211 if (EFI_ERROR(status)) 212 return -1; 213 base = info.FileSize; 214 break; 215 } 216 217 status = file->SetPosition(file, base + offset); 218 if (EFI_ERROR(status)) 219 return -1; 220 file->GetPosition(file, &base); 221 222 return base; 223 } 224 225 int 226 efifs_stat(struct open_file *f, struct stat *sb) 227 { 228 EFI_FILE *file = f->f_fsdata; 229 EFI_STATUS status; 230 char *buf; 231 UINTN sz; 232 static EFI_GUID infoid = EFI_FILE_INFO_ID; 233 EFI_FILE_INFO *info; 234 235 memset(sb, 0, sizeof(*sb)); 236 237 buf = alloc(1024); 238 sz = 1024; 239 240 status = file->GetInfo(file, &infoid, &sz, buf); 241 if (EFI_ERROR(status)) { 242 free(buf); 243 return -1; 244 } 245 246 info = (EFI_FILE_INFO *) buf; 247 248 if (info->Attribute & EFI_FILE_READ_ONLY) 249 sb->st_mode = S_IRUSR; 250 else 251 sb->st_mode = S_IRUSR | S_IWUSR; 252 if (info->Attribute & EFI_FILE_DIRECTORY) 253 sb->st_mode |= S_IFDIR; 254 else 255 sb->st_mode |= S_IFREG; 256 sb->st_size = info->FileSize; 257 258 free(buf); 259 return 0; 260 } 261 262 int 263 efifs_readdir(struct open_file *f, struct dirent *d) 264 { 265 EFI_FILE *file = f->f_fsdata; 266 EFI_STATUS status; 267 char *buf; 268 UINTN sz; 269 EFI_FILE_INFO *info; 270 int i; 271 272 buf = alloc(1024); 273 sz = 1024; 274 275 status = file->Read(file, &sz, buf); 276 if (EFI_ERROR(status) || sz < offsetof(EFI_FILE_INFO, FileName)) 277 return ENOENT; 278 279 info = (EFI_FILE_INFO *) buf; 280 281 d->d_fileno = 0; 282 d->d_reclen = sizeof(*d); 283 if (info->Attribute & EFI_FILE_DIRECTORY) 284 d->d_type = DT_DIR; 285 else 286 d->d_type = DT_REG; 287 d->d_namlen = ((info->Size - offsetof(EFI_FILE_INFO, FileName)) 288 / sizeof(CHAR16)); 289 for (i = 0; i < d->d_namlen; i++) 290 d->d_name[i] = info->FileName[i]; 291 d->d_name[i] = 0; 292 293 free(buf); 294 return 0; 295 } 296 297 static EFI_HANDLE *fs_handles; 298 UINTN fs_handle_count; 299 300 int 301 efifs_get_unit(EFI_HANDLE h) 302 { 303 UINTN u; 304 305 u = 0; 306 while (u < fs_handle_count && fs_handles[u] != h) 307 u++; 308 return ((u < fs_handle_count) ? u : -1); 309 } 310 311 int 312 efifs_dev_init(void) 313 { 314 EFI_STATUS status; 315 UINTN sz; 316 static EFI_GUID sfsid = SIMPLE_FILE_SYSTEM_PROTOCOL; 317 318 sz = 0; 319 status = BS->LocateHandle(ByProtocol, &sfsid, 0, &sz, 0); 320 if (status != EFI_BUFFER_TOO_SMALL) 321 return ENOENT; 322 fs_handles = (EFI_HANDLE *) alloc(sz); 323 status = BS->LocateHandle(ByProtocol, &sfsid, 0, 324 &sz, fs_handles); 325 if (EFI_ERROR(status)) { 326 free(fs_handles); 327 return ENOENT; 328 } 329 fs_handle_count = sz / sizeof(EFI_HANDLE); 330 331 return 0; 332 } 333 334 /* 335 * Print information about disks 336 */ 337 void 338 efifs_dev_print(int verbose) 339 { 340 int i; 341 char line[80]; 342 343 for (i = 0; i < fs_handle_count; i++) { 344 sprintf(line, " fs%d: EFI filesystem", i); 345 pager_output(line); 346 /* XXX more detail? */ 347 pager_output("\n"); 348 } 349 } 350 351 /* 352 * Attempt to open the disk described by (dev) for use by (f). 353 * 354 * Note that the philosophy here is "give them exactly what 355 * they ask for". This is necessary because being too "smart" 356 * about what the user might want leads to complications. 357 * (eg. given no slice or partition value, with a disk that is 358 * sliced - are they after the first BSD slice, or the DOS 359 * slice before it?) 360 */ 361 int 362 efifs_dev_open(struct open_file *f, ...) 363 { 364 va_list args; 365 struct efi_devdesc *dev; 366 int unit; 367 368 va_start(args, f); 369 dev = va_arg(args, struct efi_devdesc*); 370 va_end(args); 371 372 unit = dev->d_kind.efidisk.unit; 373 if (unit < 0 || unit >= fs_handle_count) { 374 printf("attempt to open nonexistent EFI filesystem\n"); 375 return(ENXIO); 376 } 377 378 dev->d_handle = fs_handles[unit]; 379 380 return 0; 381 } 382 383 int 384 efifs_dev_close(struct open_file *f) 385 { 386 387 return 0; 388 } 389 390 int 391 efifs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize) 392 { 393 return 0; 394 } 395 396