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