1 #include <u.h>
2 #include "fns.h"
3 #include "efi.h"
4
5 typedef struct {
6 UINT64 Revision;
7 void *Open;
8 void *Close;
9 void *Delete;
10 void *Read;
11 void *Write;
12 void *GetPosition;
13 void *SetPosition;
14 void *GetInfo;
15 void *SetInfo;
16 void *Flush;
17 void *OpenEx;
18 void *ReadEx;
19 void *WriteEx;
20 void *FlushEx;
21 } EFI_FILE_PROTOCOL;
22
23 typedef struct {
24 UINT64 Revision;
25 void *OpenVolume;
26 } EFI_SIMPLE_FILE_SYSTEM_PROTOCOL;
27
28 static
29 EFI_GUID EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID = {
30 0x0964e5b22, 0x6459, 0x11d2,
31 0x8e, 0x39, 0x00, 0xa0,
32 0xc9, 0x69, 0x72, 0x3b,
33 };
34
35 static EFI_GUID EFI_LOADED_IMAGE_PROTOCOL_GUID = {
36 0x5b1b31a1, 0x9562, 0x11d2,
37 0x8e, 0x3f, 0x00, 0xa0,
38 0xc9, 0x69, 0x72, 0x3b,
39 };
40
41 static
42 EFI_FILE_PROTOCOL *fsroot;
43
44 static void
towpath(CHAR16 * w,int nw,char * s)45 towpath(CHAR16 *w, int nw, char *s)
46 {
47 int i;
48
49 for(i=0; *s && i<nw-1; i++){
50 *w = *s++;
51 if(*w == '/')
52 *w = '\\';
53 w++;
54 }
55 *w = 0;
56 }
57
58 static void*
fsopen(char * name)59 fsopen(char *name)
60 {
61 CHAR16 wname[MAXPATH];
62 EFI_FILE_PROTOCOL *fp;
63
64 if(fsroot == nil)
65 return nil;
66
67 towpath(wname, MAXPATH, name);
68
69 fp = nil;
70 if(eficall(fsroot->Open, fsroot, &fp, wname, (UINT64)1, (UINT64)1))
71 return nil;
72 return fp;
73 }
74
75 static int
fsread(void * f,void * data,int len)76 fsread(void *f, void *data, int len)
77 {
78 UINTN size;
79
80 size = len > 4096 ? 4096 : len;
81 if(eficall(((EFI_FILE_PROTOCOL*)f)->Read, f, &size, data))
82 return 0;
83 return (int)size;
84 }
85
86 static void
fsclose(void * f)87 fsclose(void *f)
88 {
89 eficall(((EFI_FILE_PROTOCOL*)f)->Close, f);
90 }
91
92 int
fsinit(void ** pf)93 fsinit(void **pf)
94 {
95 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
96 EFI_LOADED_IMAGE_PROTOCOL *image;
97 EFI_FILE_PROTOCOL *root;
98 EFI_HANDLE *Handles;
99 void *f;
100 UINTN Count;
101 int i;
102
103 image = nil;
104
105 /* locate kernel and plan9.ini by deriving a fs protocol
106 * from the device the loader was read from.
107 * if that fails, fall back to old method.
108 */
109 if(eficall(ST->BootServices->HandleProtocol, IH,
110 &EFI_LOADED_IMAGE_PROTOCOL_GUID, &image) == 0 &&
111 eficall(ST->BootServices->HandleProtocol, image->DeviceHandle,
112 &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, &fs) == 0 &&
113 eficall(fs->OpenVolume, fs, &root) == 0){
114
115 fsroot = root;
116 f = fsopen("/plan9.ini");
117 if(f != nil){
118 if(pf != nil)
119 *pf = f;
120 else
121 fsclose(f);
122
123 goto gotit;
124 }
125 }
126
127 Count = 0;
128 Handles = nil;
129 if(eficall(ST->BootServices->LocateHandleBuffer,
130 ByProtocol, &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, nil, &Count, &Handles))
131 return -1;
132
133 /*
134 * assuming the ESP is the first entry in the handle buffer, so go backwards
135 * to scan for plan9.ini in other (9fat) filesystems first. if nothing is found
136 * we'll be defaulting to the ESP.
137 */
138 fsroot = nil;
139 for(i=Count-1; i>=0; i--){
140 root = nil;
141 fs = nil;
142 if(eficall(ST->BootServices->HandleProtocol,
143 Handles[i], &EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID, &fs))
144 continue;
145 if(eficall(fs->OpenVolume, fs, &root))
146 continue;
147 fsroot = root;
148 f = fsopen("/plan9.ini");
149 if(f != nil){
150 if(pf != nil)
151 *pf = f;
152 else
153 fsclose(f);
154 break;
155 }
156 }
157 if(fsroot == nil)
158 return -1;
159
160 gotit:
161 read = fsread;
162 close = fsclose;
163 open = fsopen;
164
165 return 0;
166 }
167