Home | History | Annotate | Line # | Download | only in libefi
efifs.c revision 1.4
      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