]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/fat.c
9bootfat: rename open() to fileinit and make it static as its really a internal funct...
[plan9front.git] / sys / src / boot / pc / fat.c
1 #include <u.h>
2 #include "fns.h"
3
4 #define GETSHORT(p) (*(ushort *)(p))
5 #define GETLONG(p) (*(uint *)(p))
6
7 enum {
8         Sectsz = 0x200,
9         Dirsz = 0x20,
10         Maxpath = 64,
11         Fat12 = 1,
12         Fat16 = 2,
13         Fat32 = 4,
14 };
15
16 typedef struct File File;
17 typedef struct Dir Dir;
18 typedef struct Pbs Pbs;
19 typedef struct Fat Fat;
20
21 struct Fat
22 {
23         ulong ver;
24         int drive;
25         ulong clustsize;
26         ulong eofmark;
27         ulong partlba;
28         ulong fatlba;
29         ulong dirstart; /* LBA for FAT16, cluster for FAT32 */
30         ulong dirents;
31         ulong datalba;
32 };
33
34 struct File
35 {
36         Fat *fat;
37         ulong lba;
38         ulong clust;
39         ulong lbaoff;
40         ulong len;
41         uchar *rp;
42         uchar *ep;
43         uchar buf[Sectsz];
44 };
45
46 struct Dir
47 {
48         char name[11];
49         uchar attr;
50         uchar reserved;
51         uchar ctime;
52         uchar ctime[2];
53         uchar cdate[2];
54         uchar adate[2];
55         uchar starthi[2];
56         uchar mtime[2];
57         uchar mdate[2];
58         uchar startlo[2];
59         uchar len[4];
60 };
61
62 struct Pbs
63 {
64         uchar magic[3];
65         uchar version[8];
66         uchar sectsize[2];
67         uchar clustsize;
68         uchar nreserv[2];
69         uchar nfats;
70         uchar rootsize[2];
71         uchar volsize[2];
72         uchar mediadesc;
73         uchar fatsize[2];
74         uchar trksize[2];
75         uchar nheads[2];
76         uchar nhidden[4];
77         uchar bigvolsize[4];
78         union
79         {
80                 struct
81                 {
82                         uchar driveno;
83                         uchar reserved0;
84                         uchar bootsig;
85                         uchar volid[4];
86                         uchar label[11];
87                         uchar type[8];
88                 } fat16;
89                 struct
90                 {
91                         uchar fatsize[4];
92                         uchar flags[2];
93                         uchar ver[2];
94                         uchar rootclust[4];
95                         uchar fsinfo[2];
96                         uchar bootbak[2];
97                         uchar reserved0[12];
98                         uchar driveno;
99                         uchar reserved1;
100                         uchar bootsig;
101                         uchar volid[4];
102                         uchar label[11];
103                         uchar type[8];
104                 } fat32;
105         };
106 };
107
108 int readsect(ulong drive, ulong lba, void *buf);
109
110 void
111 unload(void)
112 {
113 }
114
115 static ulong
116 readnext(File *fp, ulong clust)
117 {
118         Fat *fat = fp->fat;
119         uint b = fat->ver;
120         ulong sect, off;
121         
122         sect = clust * b / Sectsz;
123         off = clust * b % Sectsz;
124         if(readsect(fat->drive, fat->fatlba + sect, fp->buf))
125                 memset(fp->buf, 0xff, 4);
126         switch(fat->ver){
127         case Fat16:
128                 return GETSHORT(&fp->buf[off]);
129         case Fat32:
130                 return GETLONG(&fp->buf[off])& 0x0fffffff;
131         }
132         return 0;
133 }
134
135 int
136 read(void *f, void *data, int len)
137 {
138         File *fp = f;
139         Fat *fat = fp->fat;
140
141         if(fp->len > 0 && fp->rp >= fp->ep){
142                 if(fp->clust != ~0U){
143                         if(fp->lbaoff % fat->clustsize == 0){
144                                 if((fp->clust >> 4) == fat->eofmark)
145                                         return -1;
146                                 fp->lbaoff = (fp->clust - 2) * fat->clustsize;
147                                 putc('.');
148                                 fp->clust = readnext(fp, fp->clust);
149                                 fp->lba = fp->lbaoff + fat->datalba;
150                         }
151                         fp->lbaoff++;
152                 }
153                 if(readsect(fat->drive, fp->lba++, fp->rp = fp->buf))
154                         return -1;
155         }
156         if(fp->len < len)
157                 len = fp->len;
158         if(len > (fp->ep - fp->rp))
159                 len = fp->ep - fp->rp;
160         memmove(data, fp->rp, len);
161         fp->rp += len;
162         fp->len -= len;
163         return len;
164 }
165
166 void
167 close(void *)
168 {
169 }
170
171 static int
172 dirname(Dir *d, char buf[Maxpath])
173 {
174         char c, *x;
175
176         if(d->attr == 0x0F || *d->name <= 0)
177                 return -1;
178         memmove(buf, d->name, 8);
179         x = buf+8;
180         while(x > buf && x[-1] == ' ')
181                 x--;
182         if(d->name[8] != ' '){
183                 *x++ = '.';
184                 memmove(x, d->name+8, 3);
185                 x += 3;
186         }
187         while(x > buf && x[-1] == ' ')
188                 x--;
189         *x = 0;
190         x = buf;
191         while(c = *x){
192                 if(c >= 'A' && c <= 'Z'){
193                         c -= 'A';
194                         c += 'a';
195                 }
196                 *x++ = c;
197         }
198         return x - buf;
199 }
200
201 static ulong
202 dirclust(Dir *d)
203 {
204         return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
205 }
206
207 static void
208 fileinit(File *fp, Fat *fat, ulong lba)
209 {
210         fp->fat = fat;
211         fp->lba = lba;
212         fp->len = 0;
213         fp->lbaoff = 0;
214         fp->clust = ~0U;
215         fp->rp = fp->ep = fp->buf + Sectsz;
216 }
217
218 static int
219 fatwalk(File *fp, Fat *fat, char *path)
220 {
221         char name[Maxpath], *end;
222         int i, j;
223         Dir d;
224
225         if(fat->ver == Fat32){
226                 fileinit(fp, fat, 0);
227                 fp->clust = fat->dirstart;
228                 fp->len = ~0U;
229         }else{
230                 fileinit(fp, fat, fat->dirstart);
231                 fp->len = fat->dirents * Dirsz;
232         }
233         for(;;){
234                 if(readn(fp, &d, Dirsz) != Dirsz)
235                         break;
236                 if((i = dirname(&d, name)) <= 0)
237                         continue;
238                 while(*path == '/')
239                         path++;
240                 if((end = strchr(path, '/')) == 0)
241                         end = path + strlen(path);
242                 j = end - path;
243                 if(i == j && memcmp(name, path, j) == 0){
244                         fileinit(fp, fat, 0);
245                         fp->clust = dirclust(&d);
246                         fp->len = *((ulong*)d.len);
247                         if(*end == 0)
248                                 return 0;
249                         else if(d.attr & 0x10){
250                                 fp->len = fat->clustsize * Sectsz;
251                                 path = end;
252                                 continue;
253                         }
254                         break;
255                 }
256         }
257         return -1;
258 }
259
260 static int
261 conffat(Fat *fat, void *buf)
262 {
263         Pbs *p = buf;
264         uint fatsize, volsize, datasize, reserved;
265         uint ver, dirsize, dirents, clusters;
266         
267         /* sanity check */
268         if(GETSHORT(p->sectsize) != Sectsz){
269                 print("sectsize != 512\r\n");
270                 halt();
271         }
272         
273         /* load values from fat */
274         fatsize = GETSHORT(p->fatsize);
275         if(fatsize == 0)
276                 fatsize = GETLONG(p->fat32.fatsize);
277         volsize = GETSHORT(p->volsize);
278         if(volsize == 0)
279                 volsize = GETLONG(p->bigvolsize);
280         reserved = GETSHORT(p->nreserv);
281         dirents = GETSHORT(p->rootsize);
282         dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz;
283         datasize = volsize - (reserved + fatsize * p->nfats + dirsize);
284         clusters = datasize / p->clustsize;
285         
286         /* determine fat type */
287         if(clusters < 4085)
288                 ver = Fat12;
289         else if(clusters < 65525)
290                 ver = Fat16;
291         else
292                 ver = Fat32;
293         
294         /* another check */
295         if(ver == Fat12){
296                 print("TODO: implement FAT12\r\n");
297                 halt();
298         }
299         
300         /* fill FAT descriptor */
301         fat->ver = ver;
302         fat->dirents = dirents;
303         fat->clustsize = p->clustsize;
304         fat->fatlba = fat->partlba + reserved;
305         fat->dirstart  = fat->fatlba + fatsize * p->nfats;
306         if(ver == Fat32){
307                 fat->datalba = fat->dirstart;
308                 fat->dirstart  = GETLONG(p->fat32.rootclust);
309                 fat->eofmark = 0xffffff;
310         }else{
311                 fat->datalba = fat->dirstart + dirsize;
312                 fat->eofmark = 0xfff;
313         }       
314         return 0;
315 }
316
317 static int
318 findfat(Fat *fat, int drive)
319 {
320         struct {
321                 uchar status;
322                 uchar bchs[3];
323                 uchar typ;
324                 uchar echs[3];
325                 uchar lba[4];
326                 uchar len[4];
327         } *p;
328         uchar buf[Sectsz];
329         int i;
330
331         if(readsect(drive, 0, buf))
332                 return -1;
333         if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
334                 return -1;
335         p = (void*)&buf[0x1be];
336         for(i=0; i<4; i++){
337                 if(p[i].status != 0x80)
338                         continue;
339                 fat->drive = drive;
340                 fat->partlba = *((ulong*)p[i].lba);
341                 if(readsect(drive, fat->partlba, buf))
342                         continue;
343                 if(conffat(fat, buf))
344                         continue;
345                 return 0;
346         }
347         return -1;
348 }
349
350 void
351 start(void *sp)
352 {
353         char path[Maxpath], *kern;
354         int drive;
355         File fi;
356         Fat fat;
357         void *f;
358
359         /* drive passed in DL */
360         drive = ((ushort*)sp)[5] & 0xFF;
361
362         print("9bootfat\r\n");
363         if(findfat(&fat, drive)){
364                 print("no fat\r\n");
365                 halt();
366         }
367         if(fatwalk(f = &fi, &fat, "plan9.ini")){
368                 print("no config\r\n");
369                 f = 0;
370         }
371         for(;;){
372                 kern = configure(f, path); f = 0;
373                 if(fatwalk(&fi, &fat, kern)){
374                         print("not found\r\n");
375                         continue;
376                 }
377                 print(bootkern(&fi));
378                 print(crnl);
379         }
380 }
381