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