]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/fat.c
9bootfat: fat12 support
[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                 };
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         uchar tmp[2], *p;
120         ulong idx, lba;
121
122         if(fat->ver == Fat12)
123                 idx = (3*clust)/2;
124         else
125                 idx = clust*fat->ver;
126         lba = fat->fatlba + (idx / Sectsz);
127         if(readsect(fat->drive, lba, fp->buf))
128                 memset(fp->buf, 0xff, Sectsz);
129         p = &fp->buf[idx % Sectsz];
130         if(p == &fp->buf[Sectsz-1]){
131                 tmp[0] = *p;
132                 if(readsect(fat->drive, ++lba, fp->buf))
133                         memset(fp->buf, 0xff, Sectsz);
134                 tmp[1] = fp->buf[0];
135                 p = tmp;
136         }
137         if(fat->ver == Fat32)
138                 return GETLONG(p) & 0xfffffff;
139         idx = GETSHORT(p);
140         if(fat->ver == Fat12){
141                 if(clust & 1)
142                         idx >>= 4;
143                 idx &= 0xfff;
144         }
145         return idx;
146 }
147
148 int
149 read(void *f, void *data, int len)
150 {
151         File *fp = f;
152         Fat *fat = fp->fat;
153
154         if(fp->len > 0 && fp->rp >= fp->ep){
155                 if(fp->clust != ~0U){
156                         if(fp->lbaoff % fat->clustsize == 0){
157                                 if(fp->clust < 2 || fp->clust >= fat->eofmark)
158                                         return -1;
159                                 fp->lbaoff = (fp->clust - 2) * fat->clustsize;
160                                 fp->clust = readnext(fp, fp->clust);
161                                 fp->lba = fp->lbaoff + fat->datalba;
162                         }
163                         fp->lbaoff++;
164                 }
165                 if(readsect(fat->drive, fp->lba++, fp->rp = fp->buf))
166                         return -1;
167         }
168         if(fp->len < len)
169                 len = fp->len;
170         if(len > (fp->ep - fp->rp))
171                 len = fp->ep - fp->rp;
172         memmove(data, fp->rp, len);
173         fp->rp += len;
174         fp->len -= len;
175         return len;
176 }
177
178 void
179 close(void *)
180 {
181 }
182
183 static int
184 dirname(Dir *d, char buf[Maxpath])
185 {
186         char c, *x;
187
188         if(d->attr == 0x0F || *d->name <= 0)
189                 return -1;
190         memmove(buf, d->name, 8);
191         x = buf+8;
192         while(x > buf && x[-1] == ' ')
193                 x--;
194         if(d->name[8] != ' '){
195                 *x++ = '.';
196                 memmove(x, d->name+8, 3);
197                 x += 3;
198         }
199         while(x > buf && x[-1] == ' ')
200                 x--;
201         *x = 0;
202         x = buf;
203         while(c = *x){
204                 if(c >= 'A' && c <= 'Z'){
205                         c -= 'A';
206                         c += 'a';
207                 }
208                 *x++ = c;
209         }
210         return x - buf;
211 }
212
213 static ulong
214 dirclust(Dir *d)
215 {
216         return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
217 }
218
219 static void
220 fileinit(File *fp, Fat *fat, ulong lba)
221 {
222         fp->fat = fat;
223         fp->lba = lba;
224         fp->len = 0;
225         fp->lbaoff = 0;
226         fp->clust = ~0U;
227         fp->rp = fp->ep = fp->buf + Sectsz;
228 }
229
230 static int
231 fatwalk(File *fp, Fat *fat, char *path)
232 {
233         char name[Maxpath], *end;
234         int i, j;
235         Dir d;
236
237         if(fat->ver == Fat32){
238                 fileinit(fp, fat, 0);
239                 fp->clust = fat->dirstart;
240                 fp->len = ~0U;
241         }else{
242                 fileinit(fp, fat, fat->dirstart);
243                 fp->len = fat->dirents * Dirsz;
244         }
245         for(;;){
246                 if(readn(fp, &d, Dirsz) != Dirsz)
247                         break;
248                 if((i = dirname(&d, name)) <= 0)
249                         continue;
250                 while(*path == '/')
251                         path++;
252                 if((end = strchr(path, '/')) == 0)
253                         end = path + strlen(path);
254                 j = end - path;
255                 if(i == j && memcmp(name, path, j) == 0){
256                         fileinit(fp, fat, 0);
257                         fp->clust = dirclust(&d);
258                         fp->len = GETLONG(d.len);
259                         if(*end == 0)
260                                 return 0;
261                         else if(d.attr & 0x10){
262                                 fp->len = fat->clustsize * Sectsz;
263                                 path = end;
264                                 continue;
265                         }
266                         break;
267                 }
268         }
269         return -1;
270 }
271
272 static int
273 conffat(Fat *fat, void *buf)
274 {
275         Pbs *p = buf;
276         uint fatsize, volsize, datasize, reserved;
277         uint ver, dirsize, dirents, clusters;
278
279         if(GETSHORT(p->sectsize) != Sectsz)
280                 return -1;
281         if(memcmp(p->type, "FAT", 3) && memcmp(p->fat32.type, "FAT", 3))
282                 return -1;
283         
284         /* load values from fat */
285         ver = 0;
286         fatsize = GETSHORT(p->fatsize);
287         if(fatsize == 0){
288                 fatsize = GETLONG(p->fat32.fatsize);
289                 ver = Fat32;
290         }
291         volsize = GETSHORT(p->volsize);
292         if(volsize == 0)
293                 volsize = GETLONG(p->bigvolsize);
294         reserved = GETSHORT(p->nreserv);
295         dirents = GETSHORT(p->rootsize);
296         dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz;
297         datasize = volsize - (reserved + fatsize * p->nfats + dirsize);
298         clusters = datasize / p->clustsize;
299         if(ver != Fat32)
300                 if(clusters < 0xff7)
301                         ver = Fat12;
302                 else
303                         ver = Fat16;
304         
305         /* fill FAT descriptor */
306         fat->ver = ver;
307         fat->dirents = dirents;
308         fat->clustsize = p->clustsize;
309         fat->fatlba = fat->partlba + reserved;
310         fat->dirstart  = fat->fatlba + fatsize * p->nfats;
311         if(ver == Fat32){
312                 fat->datalba = fat->dirstart;
313                 fat->dirstart  = GETLONG(p->fat32.rootclust);
314                 fat->eofmark = 0xffffff7;
315         }else{
316                 fat->datalba = fat->dirstart + dirsize;
317                 if(ver == Fat16)
318                         fat->eofmark = 0xfff7;
319                 else
320                         fat->eofmark = 0xff7;
321         }
322         return 0;
323 }
324
325 static int
326 findfat(Fat *fat, int drive, ulong xbase, ulong lba)
327 {
328         struct {
329                 uchar status;
330                 uchar bchs[3];
331                 uchar typ;
332                 uchar echs[3];
333                 uchar lba[4];
334                 uchar len[4];
335         } *p;
336         uchar buf[Sectsz];
337         int i;
338
339         if(xbase == 0)
340                 xbase = lba;
341         if(readsect(drive, lba, buf))
342                 return -1;
343         if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
344                 return -1;
345         if(lba == 0){
346                 fat->drive = drive;
347                 fat->partlba = 0;
348                 if(!conffat(fat, buf))
349                         return 0;
350         }
351         p = (void*)&buf[0x1be];
352         for(i=0; i<4; i++){
353                 switch(p[i].typ){
354                 case 0x05:
355                 case 0x0f:
356                 case 0x85:
357                         /* extended partitions */
358                         if(!findfat(fat, drive, xbase, xbase + GETLONG(p[i].lba)))
359                                 return 0;
360                         /* no break */
361                 case 0x00:
362                         continue;
363                 default:
364                         if(p[i].status != 0x80)
365                                 continue;
366                         fat->drive = drive;
367                         fat->partlba = lba + GETLONG(p[i].lba);
368                         if(readsect(drive, fat->partlba, buf))
369                                 continue;
370                         if(!conffat(fat, buf))
371                                 return 0;
372                 }
373         }
374         return -1;
375 }
376
377 void
378 start(void *sp)
379 {
380         char path[Maxpath], *kern;
381         int drive;
382         File fi;
383         Fat fat;
384         void *f;
385
386         /* drive passed in DL */
387         drive = ((ushort*)sp)[5] & 0xFF;
388
389         if(findfat(&fat, drive, 0, 0)){
390                 print("no fat\r\n");
391                 halt();
392         }
393         if(fatwalk(f = &fi, &fat, "plan9.ini")){
394                 print("no config\r\n");
395                 f = 0;
396         }
397         for(;;){
398                 kern = configure(f, path); f = 0;
399                 if(fatwalk(&fi, &fat, kern)){
400                         print("not found\r\n");
401                         continue;
402                 }
403                 print(bootkern(&fi));
404                 print(crnl);
405         }
406 }
407