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