]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/boot/efi/libefi/efifs.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / boot / efi / libefi / efifs.c
1 /*-
2  * Copyright (c) 2001 Doug Rabson
3  * Copyright (c) 2006 Marcel Moolenaar
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/time.h>
33 #include <stddef.h>
34 #include <stdarg.h>
35
36 #include <bootstrap.h>
37
38 #include <efi.h>
39 #include <efilib.h>
40 #include <efiprot.h>
41
42 /* Perform I/O in blocks of size EFI_BLOCK_SIZE. */
43 #define EFI_BLOCK_SIZE  (1024 * 1024)
44
45 union fileinfo {
46         EFI_FILE_INFO info;
47         char bytes[sizeof(EFI_FILE_INFO) + 508];
48 };
49
50 static EFI_GUID sfs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
51 static EFI_GUID fs_guid = EFI_FILE_SYSTEM_INFO_ID;
52 static EFI_GUID fi_guid = EFI_FILE_INFO_ID;
53
54 static int
55 efifs_open(const char *upath, struct open_file *f)
56 {
57         struct devdesc *dev = f->f_devdata;
58         EFI_FILE_IO_INTERFACE *fsif;
59         EFI_FILE *file, *root;
60         EFI_HANDLE h;
61         EFI_STATUS status;
62         CHAR16 *cp, *path;
63
64         if (f->f_dev != &efifs_dev || dev->d_unit < 0)
65                 return (EINVAL);
66
67         h = efi_find_handle(f->f_dev, dev->d_unit);
68         if (h == NULL)
69                 return (EINVAL);
70
71         status = BS->HandleProtocol(h, &sfs_guid, (VOID **)&fsif);
72         if (EFI_ERROR(status))
73                 return (efi_status_to_errno(status));
74
75         /* Get the root directory. */
76         status = fsif->OpenVolume(fsif, &root);
77         if (EFI_ERROR(status))
78                 return (efi_status_to_errno(status));
79
80         while (*upath == '/')
81                 upath++;
82
83         /* Special case: opening the root directory. */
84         if (*upath == '\0') {
85                 f->f_fsdata = root;
86                 return (0);
87         }
88
89         path = malloc((strlen(upath) + 1) * sizeof(CHAR16));
90         if (path == NULL) {
91                 root->Close(root);
92                 return (ENOMEM);
93         }
94
95         cp = path;
96         while (*upath != '\0') {
97                 if (*upath == '/') {
98                         *cp = '\\';
99                         while (upath[1] == '/')
100                                 upath++;
101                 } else
102                         *cp = *upath;
103                 upath++;
104                 cp++;
105         }
106         *cp = 0;
107
108         /* Open the file. */
109         status = root->Open(root, &file, path,
110             EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0);
111         if (status == EFI_ACCESS_DENIED || status == EFI_WRITE_PROTECTED)
112                 status = root->Open(root, &file, path, EFI_FILE_MODE_READ, 0);
113         free(path);
114         root->Close(root);
115         if (EFI_ERROR(status))
116                 return (efi_status_to_errno(status));
117
118         f->f_fsdata = file;
119         return (0);
120 }
121
122 static int
123 efifs_close(struct open_file *f)
124 {
125         EFI_FILE *file = f->f_fsdata;
126
127         if (file == NULL)
128                 return (EBADF);
129
130         file->Close(file);
131         f->f_fsdata = NULL;
132         return (0);
133 }
134
135 static int
136 efifs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
137 {
138         EFI_FILE *file = f->f_fsdata;
139         EFI_STATUS status;
140         UINTN sz = size;
141         char *bufp;
142
143         if (file == NULL)
144                 return (EBADF);
145
146         bufp = buf;
147         while (size > 0) {
148                 sz = size;
149                 if (sz > EFI_BLOCK_SIZE)
150                         sz = EFI_BLOCK_SIZE;
151                 status = file->Read(file, &sz, bufp);
152                 if (EFI_ERROR(status))
153                         return (efi_status_to_errno(status));
154                 if (sz == 0)
155                         break;
156                 size -= sz;
157                 bufp += sz;
158         }
159         if (resid)
160                 *resid = size;
161         return (0);
162 }
163
164 static int
165 efifs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
166 {
167         EFI_FILE *file = f->f_fsdata;
168         EFI_STATUS status;
169         UINTN sz = size;
170         char *bufp;
171
172         if (file == NULL)
173                 return (EBADF);
174
175         bufp = buf;
176         while (size > 0) {
177                 sz = size;
178                 if (sz > EFI_BLOCK_SIZE)
179                         sz = EFI_BLOCK_SIZE;
180                 status = file->Write(file, &sz, bufp);
181                 if (EFI_ERROR(status))
182                         return (efi_status_to_errno(status));
183                 if (sz == 0)
184                         break;
185                 size -= sz;
186                 bufp += sz;
187         }
188         if (resid)
189                 *resid = size;
190         return (0);
191 }
192
193 static off_t
194 efifs_seek(struct open_file *f, off_t offset, int where)
195 {
196         EFI_FILE *file = f->f_fsdata;
197         EFI_STATUS status;
198         UINT64 base;
199
200         if (file == NULL)
201                 return (EBADF);
202
203         switch (where) {
204         case SEEK_SET:
205                 break;
206
207         case SEEK_END:
208                 status = file->SetPosition(file, ~0ULL);
209                 if (EFI_ERROR(status))
210                         return (-1);
211                 /* FALLTHROUGH */
212
213         case SEEK_CUR:
214                 status = file->GetPosition(file, &base);
215                 if (EFI_ERROR(status))
216                         return (-1);
217                 offset = (off_t)(base + offset);
218                 break;
219
220         default:
221                 return (-1);
222         }
223         if (offset < 0)
224                 return (-1);
225
226         status = file->SetPosition(file, (UINT64)offset);
227         return (EFI_ERROR(status) ? -1 : offset);
228 }
229
230 static int
231 efifs_stat(struct open_file *f, struct stat *sb)
232 {
233         EFI_FILE *file = f->f_fsdata;
234         union fileinfo fi;
235         EFI_STATUS status;
236         UINTN sz;
237
238         if (file == NULL)
239                 return (EBADF);
240
241         bzero(sb, sizeof(*sb));
242
243         sz = sizeof(fi);
244         status = file->GetInfo(file, &fi_guid, &sz, &fi);
245         if (EFI_ERROR(status))
246                 return (efi_status_to_errno(status));
247
248         sb->st_mode = S_IRUSR | S_IRGRP | S_IROTH;
249         if ((fi.info.Attribute & EFI_FILE_READ_ONLY) == 0)
250                 sb->st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
251         if (fi.info.Attribute & EFI_FILE_DIRECTORY)
252                 sb->st_mode |= S_IFDIR;
253         else
254                 sb->st_mode |= S_IFREG;
255         sb->st_nlink = 1;
256         sb->st_atime = efi_time(&fi.info.LastAccessTime);
257         sb->st_mtime = efi_time(&fi.info.ModificationTime);
258         sb->st_ctime = efi_time(&fi.info.CreateTime);
259         sb->st_size = fi.info.FileSize;
260         sb->st_blocks = fi.info.PhysicalSize / S_BLKSIZE;
261         sb->st_blksize = S_BLKSIZE;
262         sb->st_birthtime = sb->st_ctime;
263         return (0);
264 }
265
266 static int
267 efifs_readdir(struct open_file *f, struct dirent *d)
268 {
269         EFI_FILE *file = f->f_fsdata;
270         union fileinfo fi;
271         EFI_STATUS status;
272         UINTN sz;
273         int i;
274
275         if (file == NULL)
276                 return (EBADF);
277
278         sz = sizeof(fi);
279         status = file->Read(file, &sz, &fi);
280         if (EFI_ERROR(status))
281                 return (efi_status_to_errno(status));
282         if (sz == 0)
283                 return (ENOENT);
284
285         d->d_fileno = 0;
286         d->d_reclen = sizeof(*d);
287         if (fi.info.Attribute & EFI_FILE_DIRECTORY)
288                 d->d_type = DT_DIR;
289         else
290                 d->d_type = DT_REG;
291         for (i = 0; fi.info.FileName[i] != 0; i++)
292                 d->d_name[i] = fi.info.FileName[i];
293         d->d_name[i] = 0;
294         d->d_namlen = i;
295         return (0);
296 }
297
298 struct fs_ops efifs_fsops = {
299         .fs_name = "efifs",
300         .fo_open = efifs_open,
301         .fo_close = efifs_close,
302         .fo_read = efifs_read,
303         .fo_write = efifs_write,
304         .fo_seek = efifs_seek,
305         .fo_stat = efifs_stat,
306         .fo_readdir = efifs_readdir
307 };
308
309 static int
310 efifs_dev_init(void) 
311 {
312         EFI_HANDLE *handles;
313         EFI_STATUS status;
314         UINTN sz;
315         int err;
316
317         sz = 0;
318         status = BS->LocateHandle(ByProtocol, &sfs_guid, 0, &sz, 0);
319         if (status == EFI_BUFFER_TOO_SMALL) {
320                 handles = (EFI_HANDLE *)malloc(sz);
321                 status = BS->LocateHandle(ByProtocol, &sfs_guid, 0, &sz,
322                     handles);
323                 if (EFI_ERROR(status))
324                         free(handles);
325         }
326         if (EFI_ERROR(status))
327                 return (efi_status_to_errno(status));
328         err = efi_register_handles(&efifs_dev, handles,
329             sz / sizeof(EFI_HANDLE));
330         free(handles);
331         return (err);
332 }
333
334 /*
335  * Print information about disks
336  */
337 static void
338 efifs_dev_print(int verbose)
339 {
340         union {
341                 EFI_FILE_SYSTEM_INFO info;
342                 char buffer[1024];
343         } fi;
344         char line[80];
345         EFI_FILE_IO_INTERFACE *fsif;
346         EFI_FILE *volume;
347         EFI_HANDLE h;
348         EFI_STATUS status;
349         UINTN sz;
350         int i, unit;
351
352         for (unit = 0, h = efi_find_handle(&efifs_dev, 0);
353             h != NULL; h = efi_find_handle(&efifs_dev, ++unit)) {
354                 sprintf(line, "    %s%d: ", efifs_dev.dv_name, unit);
355                 pager_output(line);
356
357                 status = BS->HandleProtocol(h, &sfs_guid, (VOID **)&fsif);
358                 if (EFI_ERROR(status))
359                         goto err;
360
361                 status = fsif->OpenVolume(fsif, &volume);
362                 if (EFI_ERROR(status))
363                         goto err;
364
365                 sz = sizeof(fi);
366                 status = volume->GetInfo(volume, &fs_guid, &sz, &fi);
367                 volume->Close(volume);
368                 if (EFI_ERROR(status))
369                         goto err;
370
371                 if (fi.info.ReadOnly)
372                         pager_output("[RO] ");
373                 else
374                         pager_output("     ");
375                 for (i = 0; fi.info.VolumeLabel[i] != 0; i++)
376                         fi.buffer[i] = fi.info.VolumeLabel[i];
377                 fi.buffer[i] = 0;
378                 if (fi.buffer[0] != 0)
379                         pager_output(fi.buffer);
380                 else
381                         pager_output("EFI filesystem");
382                 pager_output("\n");
383                 continue;
384
385         err:
386                 sprintf(line, "[--] error %d: unable to obtain information\n",
387                     efi_status_to_errno(status));
388                 pager_output(line);
389         }
390 }
391
392 /*
393  * Attempt to open the disk described by (dev) for use by (f).
394  *
395  * Note that the philosophy here is "give them exactly what
396  * they ask for".  This is necessary because being too "smart"
397  * about what the user might want leads to complications.
398  * (eg. given no slice or partition value, with a disk that is
399  *  sliced - are they after the first BSD slice, or the DOS
400  *  slice before it?)
401  */
402 static int 
403 efifs_dev_open(struct open_file *f, ...)
404 {
405         va_list         args;
406         struct devdesc  *dev;
407
408         va_start(args, f);
409         dev = va_arg(args, struct devdesc*);
410         va_end(args);
411
412         if (dev->d_unit < 0)
413                 return(ENXIO);
414         return (0);
415 }
416
417 static int 
418 efifs_dev_close(struct open_file *f)
419 {
420
421         return (0);
422 }
423
424 static int 
425 efifs_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
426 {
427
428         return (ENOSYS);
429 }
430
431 struct devsw efifs_dev = {
432         .dv_name = "fs", 
433         .dv_type = DEVT_DISK, 
434         .dv_init = efifs_dev_init,
435         .dv_strategy = efifs_dev_strategy, 
436         .dv_open = efifs_dev_open, 
437         .dv_close = efifs_dev_close, 
438         .dv_ioctl = noioctl,
439         .dv_print = efifs_dev_print,
440         .dv_cleanup = NULL
441 };