xref: /inferno-os/os/boot/pc/devsd.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
1 /*
2  * Storage Device.
3  */
4 #include "u.h"
5 #include "mem.h"
6 #include "lib.h"
7 #include "dat.h"
8 #include "fns.h"
9 #include "io.h"
10 #include "ureg.h"
11 #include "error.h"
12 
13 #include "sd.h"
14 #include "fs.h"
15 
16 #define parttrace 0
17 
18 
19 extern SDifc* sdifc[];
20 
21 static SDev* sdlist;
22 static SDunit** sdunit;
23 static int sdnunit;
24 static int _sdmask;
25 static int cdmask;
26 static int sdmask;
27 
28 enum {
29 	Rawcmd,
30 	Rawdata,
31 	Rawstatus,
32 };
33 
34 void
35 sdaddpart(SDunit* unit, char* name, uvlong start, uvlong end)
36 {
37 	SDpart *pp;
38 	int i, partno;
39 
40 	if(parttrace)
41 		print("add %d %s %s %lld %lld\n", unit->npart, unit->name, name, start, end);
42 	/*
43 	 * Check name not already used
44 	 * and look for a free slot.
45 	 */
46 	if(unit->part != nil){
47 		partno = -1;
48 		for(i = 0; i < SDnpart; i++){
49 			pp = &unit->part[i];
50 			if(!pp->valid){
51 				if(partno == -1)
52 					partno = i;
53 				break;
54 			}
55 			if(strcmp(name, pp->name) == 0){
56 				if(pp->start == start && pp->end == end){
57 					if(parttrace)
58 						print("already present\n");
59 					return;
60 				}
61 			}
62 		}
63 	}else{
64 		if((unit->part = malloc(sizeof(SDpart)*SDnpart)) == nil){
65 			if(parttrace)
66 				print("malloc failed\n");
67 			return;
68 		}
69 		partno = 0;
70 	}
71 
72 	/*
73 	 * Check there is a free slot and size and extent are valid.
74 	 */
75 	if(partno == -1 || start > end || end > unit->sectors){
76 		print("cannot add %s!%s [%llud,%llud) to disk [0,%llud): %s\n",
77 			unit->name, name, start, end, unit->sectors,
78 			partno==-1 ? "no free partitions" : "partition boundaries out of range");
79 		return;
80 	}
81 	pp = &unit->part[partno];
82 	pp->start = start;
83 	pp->end = end;
84 	strncpy(pp->name, name, NAMELEN);
85 	pp->valid = 1;
86 	unit->npart++;
87 }
88 
89 void
90 sddelpart(SDunit* unit,  char* name)
91 {
92 	int i;
93 	SDpart *pp;
94 
95 	if(parttrace)
96 		print("del %d %s %s\n", unit->npart, unit->name, name);
97 	/*
98 	 * Look for the partition to delete.
99 	 * Can't delete if someone still has it open.
100 	 * If it's the last valid partition zap the
101 	 * whole table.
102 	 */
103 	pp = unit->part;
104 	for(i = 0; i < SDnpart; i++){
105 		if(strncmp(name, pp->name, NAMELEN) == 0)
106 			break;
107 		pp++;
108 	}
109 	if(i >= SDnpart)
110 		return;
111 	pp->valid = 0;
112 
113 	unit->npart--;
114 	if(unit->npart == 0){
115 		free(unit->part);
116 		unit->part = nil;
117 	}
118 }
119 
120 static int
121 sdinitpart(SDunit* unit)
122 {
123 	unit->sectors = unit->secsize = 0;
124 	unit->npart = 0;
125 	if(unit->part){
126 		free(unit->part);
127 		unit->part = nil;
128 	}
129 
130 	if(unit->inquiry[0] & 0xC0)
131 		return 0;
132 	switch(unit->inquiry[0] & 0x1F){
133 	case 0x00:			/* DA */
134 	case 0x04:			/* WORM */
135 	case 0x05:			/* CD-ROM */
136 	case 0x07:			/* MO */
137 		break;
138 	default:
139 		return 0;
140 	}
141 
142 	if(unit->dev->ifc->online == nil || unit->dev->ifc->online(unit) == 0)
143 		return 0;
144 	sdaddpart(unit, "data", 0, unit->sectors);
145 	return 1;
146 }
147 
148 static SDunit*
149 sdgetunit(SDev* sdev, int subno)
150 {
151 	int index;
152 	SDunit *unit;
153 
154 	/*
155 	 * Associate a unit with a given device and sub-unit
156 	 * number on that device.
157 	 * The device will be probed if it has not already been
158 	 * successfully accessed.
159 	 */
160 	qlock(&sdqlock);
161 	index = sdev->index+subno;
162 	unit = sdunit[index];
163 	if(unit == nil){
164 		if((unit = malloc(sizeof(SDunit))) == nil){
165 			qunlock(&sdqlock);
166 			return nil;
167 		}
168 
169 		if(sdev->enabled == 0 && sdev->ifc->enable)
170 			sdev->ifc->enable(sdev);
171 		sdev->enabled = 1;
172 
173 		snprint(unit->name, NAMELEN, "sd%c%d", sdev->idno, subno);
174 		unit->subno = subno;
175 		unit->dev = sdev;
176 
177 		/*
178 		 * No need to lock anything here as this is only
179 		 * called before the unit is made available in the
180 		 * sdunit[] array.
181 		 */
182 		if(unit->dev->ifc->verify(unit) == 0){
183 			qunlock(&sdqlock);
184 			free(unit);
185 			return nil;
186 		}
187 		sdunit[index] = unit;
188 	}
189 	qunlock(&sdqlock);
190 
191 	return unit;
192 }
193 
194 static SDunit*
195 sdindex2unit(int index)
196 {
197 	SDev *sdev;
198 
199 	/*
200 	 * Associate a unit with a given index into the top-level
201 	 * device directory.
202 	 * The device will be probed if it has not already been
203 	 * successfully accessed.
204 	 */
205 	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
206 		if(index >= sdev->index && index < sdev->index+sdev->nunit)
207 			return sdgetunit(sdev, index-sdev->index);
208 	}
209 
210 	return nil;
211 }
212 
213 static void
214 _sddetach(void)
215 {
216 	SDev *sdev;
217 
218 	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
219 		if(sdev->enabled == 0)
220 			continue;
221 		if(sdev->ifc->disable)
222 			sdev->ifc->disable(sdev);
223 		sdev->enabled = 0;
224 	}
225 }
226 
227 static void
228 sddump(void)
229 {
230 	SDev *sdev;
231 
232 	print("sdevs:\n");
233 	for(sdev = sdlist; sdev != nil; sdev = sdev->next){
234 		print("sdev %c index %d nunit %d: ",
235 			sdev->idno, sdev->index, sdev->nunit);
236 		print("\n");
237 	}
238 }
239 
240 static int
241 _sdinit(void)
242 {
243 	ulong m;
244 	int i;
245 	SDev *sdev, *tail;
246 	SDunit *unit;
247 
248 	/*
249 	 * Probe all configured controllers and make a list
250 	 * of devices found, accumulating a possible maximum number
251 	 * of units attached and marking each device with an index
252 	 * into the linear top-level directory array of units.
253 	 */
254 	tail = nil;
255 	for(i = 0; sdifc[i] != nil; i++){
256 		if((sdev = sdifc[i]->pnp()) == nil)
257 			continue;
258 		if(sdlist != nil)
259 			tail->next = sdev;
260 		else
261 			sdlist = sdev;
262 		for(tail = sdev; tail->next != nil; tail = tail->next){
263 			tail->index = sdnunit;
264 			sdnunit += tail->nunit;
265 		}
266 		tail->index = sdnunit;
267 		sdnunit += tail->nunit;
268 	}
269 	/*
270 	 * Legacy and option code goes here. This will be hard...
271 	 */
272 
273 	/*
274 	 * The maximum number of possible units is known, allocate
275 	 * placeholders for their datastructures; the units will be
276 	 * probed and structures allocated when attached.
277 	 * Allocate controller names for the different types.
278 	 */
279 	if(sdnunit == 0)
280 		return 0;
281 	if((sdunit = malloc(sdnunit*sizeof(SDunit*))) == nil)
282 		return 0;
283 	sddetach = _sddetach;
284 
285 	for(i = 0; sdifc[i] != nil; i++){
286 		if(sdifc[i]->id)
287 			sdifc[i]->id(sdlist);
288 	}
289 	if (0)
290 		sddump();
291 
292 	m = 0;
293 	cdmask = sdmask = 0;
294 	for(i=0; i<sdnunit && i < 32; i++) {
295 		unit = sdindex2unit(i);
296 		if(unit == nil)
297 			continue;
298 		sdinitpart(unit);
299 		partition(unit);
300 		if(unit->npart > 0){	/* BUG */
301 			if((unit->inquiry[0] & 0x1F) == 0x05)
302 				cdmask |= (1<<i);
303 			else
304 				sdmask |= (1<<i);
305 			m |= (1<<i);
306 		}
307 	}
308 
309 //notesdinfo();
310 	_sdmask = m;
311 	return m;
312 }
313 
314 int
315 cdinit(void)
316 {
317 	if(sdnunit == 0)
318 		_sdinit();
319 	return cdmask;
320 }
321 
322 int
323 sdinit(void)
324 {
325 	if(sdnunit == 0)
326 		_sdinit();
327 	return sdmask;
328 }
329 
330 void
331 sdinitdev(int i, char *s)
332 {
333 	SDunit *unit;
334 
335 	unit = sdindex2unit(i);
336 	strcpy(s, unit->name);
337 }
338 
339 void
340 sdprintdevs(int i)
341 {
342 	char *s;
343 	SDunit *unit;
344 
345 	unit = sdindex2unit(i);
346 	for(i=0; i<unit->npart; i++){
347 		s = unit->part[i].name;
348 		if(strncmp(s, "dos", 3) == 0
349 		|| strncmp(s, "9fat", 4) == 0
350 		|| strncmp(s, "fs", 2) == 0)
351 			print(" %s!%s", unit->name, s);
352 	}
353 }
354 
355 SDpart*
356 sdfindpart(SDunit *unit, char *name)
357 {
358 	int i;
359 
360 	if(parttrace)
361 		print("findpart %d %s %s\t\n", unit->npart, unit->name, name);
362 	for(i=0; i<unit->npart; i++) {
363 		if(parttrace)
364 			print("%s...", unit->part[i].name);
365 		if(strcmp(unit->part[i].name, name) == 0){
366 			if(parttrace)
367 				print("\n");
368 			return &unit->part[i];
369 		}
370 	}
371 	if(parttrace)
372 		print("not found\n");
373 	return nil;
374 }
375 
376 typedef struct Scsicrud Scsicrud;
377 struct Scsicrud {
378 	Fs fs;
379 	vlong offset;
380 	SDunit *unit;
381 	SDpart *part;
382 };
383 
384 long
385 sdread(Fs *vcrud, void *v, long n)
386 {
387 	Scsicrud *crud;
388 	long x;
389 
390 	crud = (Scsicrud*)vcrud;
391 	x = sdbio(crud->unit, crud->part, v, n, crud->offset);
392 	if(x > 0)
393 		crud->offset += x;
394 	return x;
395 }
396 
397 vlong
398 sdseek(Fs *vcrud, vlong seek)
399 {
400 	((Scsicrud*)vcrud)->offset = seek;
401 	return seek;
402 }
403 
404 void*
405 sdgetfspart(int i, char *s, int chatty)
406 {
407 	SDunit *unit;
408 	SDpart *p;
409 	Scsicrud *crud;
410 
411 	if(cdmask&(1<<i)){
412 		if(strcmp(s, "cdboot") != 0)
413 			return nil;
414 	}else if(sdmask&(1<<i)){
415 		if(strcmp(s, "cdboot") == 0)
416 			return nil;
417 	}
418 
419 	unit = sdindex2unit(i);
420 	if((p = sdfindpart(unit, s)) == nil){
421 		if(chatty)
422 			print("unknown partition %s!%s\n", unit->name, s);
423 		return nil;
424 	}
425 	if(p->crud == nil) {
426 		crud = malloc(sizeof(Scsicrud));
427 		crud->fs.dev = i;
428 		crud->fs.diskread = sdread;
429 		crud->fs.diskseek = sdseek;
430 	//	crud->start = 0;
431 		crud->unit = unit;
432 		crud->part = p;
433 		if(dosinit(&crud->fs) < 0 && dosinit(&crud->fs) < 0 && kfsinit(&crud->fs) < 0){
434 			if(chatty)
435 				print("partition %s!%s does not contain a DOS or KFS file system\n",
436 					unit->name, s);
437 			return nil;
438 		}
439 		p->crud = crud;
440 	}
441 	return p->crud;
442 }
443 
444 /*
445  * Leave partitions around for devsd to pick up.
446  * (Needed by boot process; more extensive
447  * partitioning is done by termrc or cpurc).
448  */
449 void
450 sdaddconf(int i)
451 {
452 	SDunit *unit;
453 	SDpart *pp;
454 
455 	unit = sdindex2unit(i);
456 
457 	/*
458 	 * If there were no partitions (just data and partition), don't bother.
459 	 */
460 	if(unit->npart<= 1 || (unit->npart==2 && strcmp(unit->part[1].name, "partition")==0))
461 		return;
462 
463 	addconf("%spart=", unit->name);
464 	for(i=1, pp=&unit->part[i]; i<unit->npart; i++, pp++)	/* skip 0, which is "data" */
465 		addconf("%s%s %lld %lld", i==1 ? "" : "/", pp->name,
466 			pp->start, pp->end);
467 	addconf("\n");
468 }
469 
470 int
471 sdboot(int dev, char *pname, Boot *b)
472 {
473 	char *file;
474 	Fs *fs;
475 
476 	if((file = strchr(pname, '!')) == nil) {
477 		print("syntax is sdC0!partition!file\n");
478 		return -1;
479 	}
480 	*file++ = '\0';
481 
482 	fs = sdgetfspart(dev, pname, 1);
483 	if(fs == nil)
484 		return -1;
485 
486 	return fsboot(fs, file, b);
487 }
488 
489 long
490 sdbio(SDunit *unit, SDpart *pp, void* va, long len, vlong off)
491 {
492 	long l;
493 	ulong bno, max, nb, offset;
494 	static uchar *b;
495 	char *a;
496 	static ulong bsz;
497 
498 	a = va;
499 memset(a, 0xDA, len);
500 	qlock(&unit->ctl);
501 	if(unit->changed){
502 		qunlock(&unit->ctl);
503 		return 0;
504 	}
505 
506 	/*
507 	 * Check the request is within bounds.
508 	 * Removeable drives are locked throughout the I/O
509 	 * in case the media changes unexpectedly.
510 	 * Non-removeable drives are not locked during the I/O
511 	 * to allow the hardware to optimise if it can; this is
512 	 * a little fast and loose.
513 	 * It's assumed that non-removable media parameters
514 	 * (sectors, secsize) can't change once the drive has
515 	 * been brought online.
516 	 */
517 	bno = (off/unit->secsize) + pp->start;
518 	nb = ((off+len+unit->secsize-1)/unit->secsize) + pp->start - bno;
519 	max = SDmaxio/unit->secsize;
520 	if(nb > max)
521 		nb = max;
522 	if(bno+nb > pp->end)
523 		nb = pp->end - bno;
524 	if(bno >= pp->end || nb == 0){
525 		qunlock(&unit->ctl);
526 		return 0;
527 	}
528 	if(!(unit->inquiry[1] & 0x80))
529 		qunlock(&unit->ctl);
530 
531 	if(bsz < nb*unit->secsize){
532 		b = malloc(nb*unit->secsize);
533 		bsz = nb*unit->secsize;
534 	}
535 //	b = sdmalloc(nb*unit->secsize);
536 //	if(b == nil)
537 //		return 0;
538 
539 	offset = off%unit->secsize;
540 	if((l = unit->dev->ifc->bio(unit, 0, 0, b, nb, bno)) < 0) {
541 //		sdfree(b);
542 		return 0;
543 	}
544 
545 	if(l < offset)
546 		len = 0;
547 	else if(len > l - offset)
548 		len = l - offset;
549 	if(len)
550 		memmove(a, b+offset, len);
551 //	sdfree(b);
552 
553 	if(unit->inquiry[1] & 0x80)
554 		qunlock(&unit->ctl);
555 
556 	return len;
557 }
558 
559 #ifdef DMA
560 long
561 sdrio(SDreq *r, void* a, long n)
562 {
563 	if(n >= SDmaxio || n < 0)
564 		return 0;
565 
566 	r->data = nil;
567 	if(n){
568 		if((r->data = malloc(n)) == nil)
569 			return 0;
570 		if(r->write)
571 			memmove(r->data, a, n);
572 	}
573 	r->dlen = n;
574 
575 	if(r->unit->dev->ifc->rio(r) != SDok){
576 // cgascreenputs("1", 1);
577 		if(r->data != nil){
578 			sdfree(r->data);
579 			r->data = nil;
580 		}
581 		return 0;
582 	}
583 // cgascreenputs("2", 1);
584 
585 	if(!r->write && r->rlen > 0)
586 		memmove(a, r->data, r->rlen);
587 // cgascreenputs("3", 1);
588 	if(r->data != nil){
589 		sdfree(r->data);
590 		r->data = nil;
591 	}
592 
593 // cgascreenputs("4", 1);
594 	return r->rlen;
595 }
596 #endif /* DMA */
597 
598 void
599 sleep(void*, int (*fn)(void*), void *v)
600 {
601 	int x;
602 	x = spllo();
603 	while(!fn(v))
604 		;
605 	splx(x);
606 	return;
607 }
608 
609 void
610 tsleep(void*, int (*fn)(void*), void *v, int msec)
611 {
612 	int x;
613 	ulong start;
614 
615 	x = spllo();
616 	for(start = m->ticks; TK2MS(m->ticks - start) < msec
617 		&& !fn(v); )
618 		;
619 	splx(x);
620 	return;
621 }
622 
623 void*
624 sdmalloc(void *p, ulong sz)
625 {
626 	if(p != nil) {
627 		memset(p, 0, sz);
628 		return p;
629 	}
630 	return malloc(sz);
631 }
632