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