xref: /netbsd-src/sys/arch/ia64/stand/efi/libefi/efifs.c (revision 6fc217346bb51c463d3a5a2a7883cb56515cd6d7)
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