Imported Upstream version 3.4
[debian/elilo] / fs / localfs.c
1 /*
2  *  Copyright (C) 2001-2003 Hewlett-Packard Co.
3  *      Contributed by Stephane Eranian <eranian@hpl.hp.com>
4  *
5  * This file is part of the ELILO, the EFI Linux boot loader.
6  *
7  *  ELILO is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2, or (at your option)
10  *  any later version.
11  *
12  *  ELILO is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with ELILO; see the file COPYING.  If not, write to the Free
19  *  Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20  *  02111-1307, USA.
21  *
22  * Please check out the elilo.txt for complete documentation on how
23  * to use this program.
24  */
25
26 #include <efi.h>
27 #include <efilib.h>
28
29 #include "elilo.h"
30 #include "fs/localfs.h"
31
32 /*
33  * LocalFS is just a shim layer on top of the 
34  * FileSystem Protocol which gives access to FAT32,16,12
35  */
36 #define FS_NAME L"vfat"
37
38
39 typedef struct {
40         EFI_HANDLE      dev;            /* device we're attached to */
41         EFI_FILE_HANDLE volume;         /* root of volume */
42 } localfs_priv_state_t;
43
44 #define LOCALFS_F2FD(f)         ((UINTN)(f))
45 #define LOCALFS_FD2F(fd)        ((EFI_FILE_HANDLE)(fd))
46
47 typedef union {
48         localfs_interface_t     pub_intf;
49         struct {
50                 localfs_interface_t     pub_intf;
51                 localfs_priv_state_t    priv_data;
52         } localfs_priv;
53 } localfs_t;
54
55 #define FS_PRIVATE(n)   (&(((localfs_t *)n)->localfs_priv.priv_data))
56
57
58 static EFI_GUID LocalFsProtocol = LOCALFS_PROTOCOL;
59
60 /*
61  * let's be clean here
62  */
63 typedef union {
64         EFI_HANDLE *dev;
65         localfs_t  *intf;
66 } dev_tab_t;
67
68 static dev_tab_t *dev_tab;      /* holds all devices we found */
69 static UINTN ndev;              /* how many entries in dev_tab */
70
71 static EFI_STATUS
72 localfs_name(localfs_interface_t *this, CHAR16 *name, UINTN maxlen)
73 {
74         if (name == NULL || maxlen < 1) return EFI_INVALID_PARAMETER;
75
76         StrnCpy(name, FS_NAME, maxlen-1);
77
78         name[maxlen-1] = CHAR_NULL;
79
80         return EFI_SUCCESS;
81 }
82
83
84 static EFI_STATUS
85 localfs_open(localfs_interface_t *this, CHAR16 *name, UINTN *fd)
86 {
87         localfs_priv_state_t *lfs;
88         EFI_STATUS              status;
89         EFI_FILE_HANDLE         fh;
90
91         if (this == NULL || name == NULL || fd == NULL) return EFI_INVALID_PARAMETER;
92
93         lfs = FS_PRIVATE(this);
94
95         DBG_PRT((L"localfs_open on %s\n", name));
96
97         status = lfs->volume->Open(lfs->volume, &fh, name, EFI_FILE_MODE_READ, 0);
98         if (status == EFI_SUCCESS) {
99                 *fd = LOCALFS_F2FD(fh);
100         } 
101         return status;
102 }
103
104 static EFI_STATUS
105 localfs_read(localfs_interface_t *this, UINTN fd, VOID *buf, UINTN *size)
106 {
107         localfs_priv_state_t *lfs;
108
109         if (this == NULL || fd == 0 || buf == NULL || size == NULL) return EFI_INVALID_PARAMETER;
110
111         lfs = FS_PRIVATE(this);
112
113         return lfs->volume->Read(LOCALFS_FD2F(fd), size, buf);
114 }
115
116 static EFI_STATUS
117 localfs_close(localfs_interface_t *this, UINTN fd)
118 {
119         localfs_priv_state_t *lfs;
120
121         if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER;
122
123         lfs = FS_PRIVATE(this);
124
125         return lfs->volume->Close(LOCALFS_FD2F(fd));
126 }
127
128 static EFI_STATUS
129 localfs_infosize(localfs_interface_t *this, UINTN fd, UINT64 *sz)
130 {
131         localfs_priv_state_t *lfs;
132         EFI_FILE_INFO        *info;
133
134         if (this == NULL || fd == 0 || sz == NULL) return EFI_INVALID_PARAMETER;
135
136         lfs = FS_PRIVATE(this);
137
138         info = LibFileInfo(LOCALFS_FD2F(fd));
139         if (info == NULL) return EFI_UNSUPPORTED;
140
141         *sz = info->FileSize;
142
143         FreePool(info);
144
145         return EFI_SUCCESS;
146 }
147
148 static EFI_STATUS
149 localfs_seek(localfs_interface_t *this, UINTN fd, UINT64 newpos)
150 {
151         localfs_priv_state_t *lfs;
152
153         if (this == NULL || fd == 0) return EFI_INVALID_PARAMETER;
154
155         lfs = FS_PRIVATE(this);
156
157         return lfs->volume->SetPosition(LOCALFS_FD2F(fd), newpos);
158 }
159
160 static VOID
161 localfs_init_state(localfs_t *localfs, EFI_HANDLE dev, EFI_FILE_HANDLE volume)
162 {
163         localfs_priv_state_t *lfs = FS_PRIVATE(localfs);
164
165         /* need to do some init here on localfs_intf */
166         Memset(localfs, 0, sizeof(*localfs));
167
168         localfs->pub_intf.localfs_name        = localfs_name;
169         localfs->pub_intf.localfs_open        = localfs_open;
170         localfs->pub_intf.localfs_read        = localfs_read;
171         localfs->pub_intf.localfs_close       = localfs_close;
172         localfs->pub_intf.localfs_infosize    = localfs_infosize;
173         localfs->pub_intf.localfs_seek        = localfs_seek;
174
175         lfs->dev    = dev;
176         lfs->volume = volume;
177 }
178
179 static EFI_STATUS
180 localfs_install_one(EFI_HANDLE dev, VOID **intf)
181 {
182
183         EFI_STATUS              status;
184         localfs_t               *localfs;
185         EFI_FILE_IO_INTERFACE   *volume;
186         EFI_FILE_HANDLE         volume_fh;
187
188         status = BS->HandleProtocol (dev, &LocalFsProtocol, (VOID **)&localfs);
189         if (status == EFI_SUCCESS) {
190                 ERR_PRT((L"Warning: found existing %s protocol on device", FS_NAME));
191                 goto found;
192         }
193         
194         status = BS->HandleProtocol (dev, &FileSystemProtocol, (VOID **)&volume);
195         if (EFI_ERROR(status)) return EFI_INVALID_PARAMETER;
196
197         status = volume->OpenVolume(volume, &volume_fh);
198         if (EFI_ERROR(status)) {
199                 ERR_PRT((L"cannot open volume"));
200                 return status;
201         }
202
203         localfs = (localfs_t *)alloc(sizeof(*localfs), EfiLoaderData);
204         if (localfs == NULL) {
205                 ERR_PRT((L"failed to allocate %s", FS_NAME));
206                 return EFI_OUT_OF_RESOURCES;
207         }
208         localfs_init_state(localfs, dev, volume_fh);
209
210         status = LibInstallProtocolInterfaces(&dev, &LocalFsProtocol, localfs, NULL);
211         if (EFI_ERROR(status)) {
212                 ERR_PRT((L"Cannot install %s protocol: %r", FS_NAME, status));
213                 free(localfs);
214                 return status;
215         }
216 found:
217         if (intf) *intf = (VOID *)localfs;
218
219         VERB_PRT(3,
220                 { EFI_DEVICE_PATH *dp; CHAR16 *str;
221                   dp  = DevicePathFromHandle(dev);
222                   str = DevicePathToStr(dp);
223                   Print(L"attached %s to %s\n", FS_NAME, str);
224                   FreePool(str);
225                 });
226
227         return EFI_SUCCESS;
228 }
229         
230 EFI_STATUS
231 localfs_install(VOID)
232 {
233         UINTN size = 0;
234         UINTN i;
235         EFI_STATUS status;
236         VOID *intf;
237
238         BS->LocateHandle(ByProtocol, &FileSystemProtocol, NULL, &size, NULL);
239         if (size == 0) return EFI_UNSUPPORTED; /* no device found, oh well */
240
241         DBG_PRT((L"size=%d", size));
242
243         dev_tab = (dev_tab_t *)alloc(size, EfiLoaderData);
244         if (dev_tab == NULL) {
245                 ERR_PRT((L"failed to allocate handle table"));
246                 return EFI_OUT_OF_RESOURCES;
247         }
248         
249         status = BS->LocateHandle(ByProtocol, &FileSystemProtocol, NULL, &size, (VOID **)dev_tab);
250         if (status != EFI_SUCCESS) {
251                 ERR_PRT((L"failed to get handles: %r", status));
252                 free(dev_tab);
253                 return status;
254         }
255         ndev = size / sizeof(EFI_HANDLE);
256
257         for(i=0; i < ndev; i++) {
258                 intf = NULL;
259                 localfs_install_one(dev_tab[i].dev, &intf);
260                 /* override device handle with interface pointer */
261                 dev_tab[i].intf = intf;
262         }
263
264         return EFI_SUCCESS;
265 }
266         
267 EFI_STATUS
268 localfs_uninstall(VOID)
269 {
270         
271         localfs_priv_state_t *lfs;
272         EFI_STATUS status;
273         UINTN i;
274
275         for(i=0; i < ndev; i++) {
276                 if (dev_tab[i].intf == NULL) continue;
277                 lfs = FS_PRIVATE(dev_tab[i].intf);
278                 status = BS->UninstallProtocolInterface(lfs->dev, &LocalFsProtocol, dev_tab[i].intf);
279                 if (EFI_ERROR(status)) {
280                         ERR_PRT((L"Uninstall %s error: %r", FS_NAME, status));
281                         continue;
282                 }
283                 VERB_PRT(3,
284                         { EFI_DEVICE_PATH *dp; CHAR16 *str;
285                         dp  = DevicePathFromHandle(lfs->dev);
286                         str = DevicePathToStr(dp);
287                         Print(L"uninstalled %s on %s\n", FS_NAME, str);
288                         FreePool(str);
289                         });
290                 free(dev_tab[i].intf);
291         }
292         if (dev_tab) free(dev_tab);
293
294         return EFI_SUCCESS;
295 }
296
297