]> git.lizzy.rs Git - plan9front.git/blob - sys/src/boot/pc/fat.c
9bootfat: rename Extend to File as fat files are not really extends anymore :)
[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 open(Fat *fat, void *f, ulong lba)
168 {
169         File *fp = f;
170
171         fp->fat = fat;
172         fp->lba = lba;
173         fp->len = 0;
174         fp->lbaoff = 0;
175         fp->clust = ~0U;
176         fp->rp = fp->ep = fp->buf + Sectsz;
177 }
178
179 void
180 close(void *)
181 {
182 }
183
184 static int
185 dirname(Dir *d, char buf[Maxpath])
186 {
187         char c, *x;
188
189         if(d->attr == 0x0F || *d->name <= 0)
190                 return -1;
191         memmove(buf, d->name, 8);
192         x = buf+8;
193         while(x > buf && x[-1] == ' ')
194                 x--;
195         if(d->name[8] != ' '){
196                 *x++ = '.';
197                 memmove(x, d->name+8, 3);
198                 x += 3;
199         }
200         while(x > buf && x[-1] == ' ')
201                 x--;
202         *x = 0;
203         x = buf;
204         while(c = *x){
205                 if(c >= 'A' && c <= 'Z'){
206                         c -= 'A';
207                         c += 'a';
208                 }
209                 *x++ = c;
210         }
211         return x - buf;
212 }
213
214 static ulong
215 dirclust(Dir *d)
216 {
217         return *((ushort*)d->starthi)<<16 | *((ushort*)d->startlo);
218 }
219
220 static int
221 fatwalk(File *fp, Fat *fat, char *path)
222 {
223         char name[Maxpath], *end;
224         int i, j;
225         Dir d;
226
227         if(fat->ver == Fat32){
228                 open(fat, fp, 0);
229                 fp->clust = fat->dirstart;
230                 fp->len = ~0U;
231         }else{
232                 open(fat, fp, fat->dirstart);
233                 fp->len = fat->dirents * Dirsz;
234         }
235         for(;;){
236                 if(readn(fp, &d, Dirsz) != Dirsz)
237                         break;
238                 if((i = dirname(&d, name)) <= 0)
239                         continue;
240                 while(*path == '/')
241                         path++;
242                 if((end = strchr(path, '/')) == 0)
243                         end = path + strlen(path);
244                 j = end - path;
245                 if(i == j && memcmp(name, path, j) == 0){
246                         open(fat, fp, 0);
247                         fp->clust = dirclust(&d);
248                         fp->len = *((ulong*)d.len);
249                         if(*end == 0)
250                                 return 0;
251                         else if(d.attr & 0x10){
252                                 fp->len = fat->clustsize * Sectsz;
253                                 path = end;
254                                 continue;
255                         }
256                         break;
257                 }
258         }
259         return -1;
260 }
261
262 static int
263 conffat(Fat *fat, void *buf)
264 {
265         Pbs *p = buf;
266         uint fatsize, volsize, datasize, reserved;
267         uint ver, dirsize, dirents, clusters;
268         
269         /* sanity check */
270         if(GETSHORT(p->sectsize) != Sectsz){
271                 print("sectsize != 512\r\n");
272                 halt();
273         }
274         
275         /* load values from fat */
276         fatsize = GETSHORT(p->fatsize);
277         if(fatsize == 0)
278                 fatsize = GETLONG(p->fat32.fatsize);
279         volsize = GETSHORT(p->volsize);
280         if(volsize == 0)
281                 volsize = GETLONG(p->bigvolsize);
282         reserved = GETSHORT(p->nreserv);
283         dirents = GETSHORT(p->rootsize);
284         dirsize = (dirents * Dirsz + Sectsz - 1) / Sectsz;
285         datasize = volsize - (reserved + fatsize * p->nfats + dirsize);
286         clusters = datasize / p->clustsize;
287         
288         /* determine fat type */
289         if(clusters < 4085)
290                 ver = Fat12;
291         else if(clusters < 65525)
292                 ver = Fat16;
293         else
294                 ver = Fat32;
295         
296         /* another check */
297         if(ver == Fat12){
298                 print("TODO: implement FAT12\r\n");
299                 halt();
300         }
301         
302         /* fill FAT descriptor */
303         fat->ver = ver;
304         fat->dirents = dirents;
305         fat->clustsize = p->clustsize;
306         fat->fatlba = fat->partlba + reserved;
307         fat->dirstart  = fat->fatlba + fatsize * p->nfats;
308         if(ver == Fat32){
309                 fat->datalba = fat->dirstart;
310                 fat->dirstart  = GETLONG(p->fat32.rootclust);
311                 fat->eofmark = 0xffffff;
312         }else{
313                 fat->datalba = fat->dirstart + dirsize;
314                 fat->eofmark = 0xfff;
315         }       
316         return 0;
317 }
318
319 static int
320 findfat(Fat *fat, int drive)
321 {
322         struct {
323                 uchar status;
324                 uchar bchs[3];
325                 uchar typ;
326                 uchar echs[3];
327                 uchar lba[4];
328                 uchar len[4];
329         } *p;
330         uchar buf[Sectsz];
331         int i;
332
333         if(readsect(drive, 0, buf))
334                 return -1;
335         if(buf[0x1fe] != 0x55 || buf[0x1ff] != 0xAA)
336                 return -1;
337         p = (void*)&buf[0x1be];
338         for(i=0; i<4; i++){
339                 if(p[i].status != 0x80)
340                         continue;
341                 fat->drive = drive;
342                 fat->partlba = *((ulong*)p[i].lba);
343                 if(readsect(drive, fat->partlba, buf))
344                         continue;
345                 if(conffat(fat, buf))
346                         continue;
347                 return 0;
348         }
349         return -1;
350 }
351
352 void
353 start(void *sp)
354 {
355         char path[Maxpath], *kern;
356         int drive;
357         File fi;
358         Fat fat;
359         void *f;
360
361         /* drive passed in DL */
362         drive = ((ushort*)sp)[5] & 0xFF;
363
364         print("9bootfat\r\n");
365         if(findfat(&fat, drive)){
366                 print("no fat\r\n");
367                 halt();
368         }
369         if(fatwalk(f = &fi, &fat, "plan9.ini")){
370                 print("no config\r\n");
371                 f = 0;
372         }
373         for(;;){
374                 kern = configure(f, path); f = 0;
375                 if(fatwalk(&fi, &fat, kern)){
376                         print("not found\r\n");
377                         continue;
378                 }
379                 print(bootkern(&fi));
380                 print(crnl);
381         }
382 }
383