xref: /inferno-os/appl/lib/disks.b (revision 6cde411a8ffd477459336cedf48034e46f56f913)
1implement Disks;
2
3# adapted from /sys/src/libdisk on Plan 9: subject to Lucent Public License 1.02
4
5include "sys.m";
6	sys: Sys;
7
8include "bufio.m";
9	bufio: Bufio;
10	Iobuf: import bufio;
11
12include "disks.m";
13
14scsiverbose := 0;
15
16Codefile: con "/lib/scsicodes";
17
18Code: adt {
19	v:	int;	# (asc<<8) | ascq
20	s:	string;
21};
22codes: array of Code;
23
24init()
25{
26	sys = load Sys Sys->PATH;
27	bufio = load Bufio Bufio->PATH;
28}
29
30#
31# Discover the disk geometry by various sleazeful means.
32#
33# First, if there is a partition table in sector 0,
34# see if all the partitions have the same end head
35# and sector; if so, we'll assume that that's the
36# right count.
37#
38# If that fails, we'll try looking at the geometry that the ATA
39# driver supplied, if any, and translate that as a
40# BIOS might.
41#
42# If that too fails, which should only happen on a SCSI
43# disk with no currently defined partitions, we'll try
44# various common (h, s) pairs used by BIOSes when faking
45# the geometries.
46#
47
48# table entry:
49	Oactive,			# active flag
50	Ostarth,			# starting head
51	Ostarts,			# starting sector
52	Ostartc,			# starting cylinder
53	Otype,			# partition type
54	Oendh,			# ending head
55	Oends,			# ending sector
56	Oendc: con iota;		# ending cylinder
57	Oxlba: con Oendc+1;	# starting LBA from start of disc or partition [4]
58	Oxsize: con Oxlba+4;	# size in sectors[4]
59
60# Table: entry[NTentry][TentrySize] magic[2]
61Omagic: con NTentry*TentrySize;
62
63partitiongeometry(d: ref Disk): int
64{
65	buf := array[512] of byte;
66	t := buf[Toffset:];
67
68	#
69	# look for an MBR first in the /dev/sdXX/data partition, otherwise
70	# attempt to fall back on the current partition.
71	#
72	rawfd := sys->open(d.prefix+"data", Sys->OREAD);
73	if(rawfd != nil
74	&& sys->seek(rawfd, big 0, 0) == big 0
75	&& sys->readn(rawfd, buf, 512) == 512
76	&& int t[Omagic] == Magic0
77	&& int t[Omagic+1] == Magic1) {
78		rawfd = nil;
79	}else{
80		rawfd = nil;
81		if(sys->seek(d.fd, big 0, 0) < big 0
82		|| sys->readn(d.fd, buf, 512) != 512
83		|| int t[Omagic] != Magic0
84		|| int t[Omagic+1] != Magic1)
85			return -1;
86	}
87
88	h := s := -1;
89	for(i:=0; i<NTentry*TentrySize; i += TentrySize) {
90		if(t[i+Otype] == byte 0)
91			continue;
92
93		t[i+Oends] &= byte 63;
94		if(h == -1) {
95			h = int t[i+Oendh];
96			s = int t[i+Oends];
97		} else {
98			#
99			# Only accept the partition info if every
100			# partition is consistent.
101			#
102			if(h != int t[i+Oendh] || s != int t[i+Oends])
103				return -1;
104		}
105	}
106
107	if(h == -1)
108		return -1;
109
110	d.h = h+1;	# heads count from 0
111	d.s = s;	# sectors count from 1
112	d.c = int (d.secs / big (d.h*d.s));
113	d.chssrc = "part";
114	return 0;
115}
116
117#
118# If there is ATA geometry, use it, perhaps massaged
119#
120drivergeometry(d: ref Disk): int
121{
122	if(d.c == 0 || d.h == 0 || d.s == 0)
123		return -1;
124
125	d.chssrc = "disk";
126	if(d.c < 1024)
127		return 0;
128
129	case d.h {
130	15 =>
131		d.h = 255;
132		d.c /= 17;
133
134	* =>
135		for(m := 2; m*d.h < 256; m *= 2) {
136			if(d.c/m < 1024) {
137				d.c /= m;
138				d.h *= m;
139				return 0;
140			}
141		}
142
143		# set to 255, 63 and be done with it
144		d.h = 255;
145		d.s = 63;
146		d.c = int (d.secs / big(d.h * d.s));
147	}
148	return 0;
149}
150
151#
152# There's no ATA geometry and no partitions.
153# Our guess is as good as anyone's.
154#
155Guess: adt {
156	h:	int;
157	s:	int;
158};
159guess: array of Guess = array[] of {
160	(64, 32),
161	(64, 63),
162	(128, 63),
163	(255, 63),
164};
165
166guessgeometry(d: ref Disk)
167{
168	d.chssrc = "guess";
169	c := 1024;
170	for(i:=0; i<len guess; i++)
171		if(big(c*guess[i].h*guess[i].s) >= d.secs) {
172			d.h = guess[i].h;
173			d.s = guess[i].s;
174			d.c = int(d.secs / big(d.h * d.s));
175			return;
176		}
177
178	# use maximum values
179	d.h = 255;
180	d.s = 63;
181	d.c = int(d.secs / big(d.h * d.s));
182}
183
184findgeometry(disk: ref Disk)
185{
186	disk.h = disk.s = disk.c = 0;
187	if(partitiongeometry(disk) < 0 &&
188	   drivergeometry(disk) < 0)
189		guessgeometry(disk);
190}
191
192openfile(d: ref Disk): ref Disk
193{
194	(ok, db) := sys->fstat(d.fd);
195	if(ok < 0)
196		return nil;
197
198	d.secsize = 512;
199	d.size = db.length;
200	d.secs = d.size / big d.secsize;
201	d.offset = big 0;
202
203	findgeometry(d);
204	return mkwidth(d);
205}
206
207opensd(d: ref Disk): ref Disk
208{
209	b := bufio->fopen(d.ctlfd, Bufio->OREAD);
210	while((p := b.gets('\n')) != nil){
211		p = p[0:len p - 1];
212		(nf, f) := sys->tokenize(p, " \t");	# might need str->unquote
213		if(nf >= 3 && hd f == "geometry") {
214			d.secsize = int hd tl tl f;
215			if(nf >= 6) {
216				d.c = int hd tl tl tl f;
217				d.h = int hd tl tl tl tl f;
218				d.s = int hd tl tl tl tl tl f;
219			}
220		}
221		if(nf >= 4 && hd f == "part" && hd tl f == d.part) {
222			d.offset = big hd tl tl f;
223			d.secs = big hd tl tl tl f - d.offset;
224		}
225	}
226
227
228	d.size = d.secs * big d.secsize;
229	if(d.size <= big 0) {
230		d.part = "";
231		d.dtype = "file";
232		return openfile(d);
233	}
234
235	findgeometry(d);
236	return mkwidth(d);
237}
238
239Disk.open(name: string, mode: int, noctl: int): ref Disk
240{
241	d := ref Disk;
242	d.rdonly = mode == Sys->OREAD;
243	d.fd = sys->open(name, Sys->OREAD);
244	if(d.fd == nil)
245		return nil;
246
247	if(mode != Sys->OREAD){
248		d.wfd = sys->open(name, Sys->OWRITE);
249		if(d.wfd == nil)
250			d.rdonly = 1;
251	}
252
253	if(noctl)
254		return openfile(d);
255
256	# check for floppy(3) disk
257	if(len name >= 7) {
258		q := name[len name-7:];
259		if(q[0] == 'f' && q[1] == 'd' && isdigit(q[2]) && q[3:] == "disk") {
260			if((d.ctlfd = sys->open(name[0:len name-4]+"ctl", Sys->ORDWR)) != nil) {
261				d.prefix = name[0:len name-4];	# fdN (unlike Plan 9)
262				d.dtype = "floppy";
263				return openfile(d);
264			}
265		}
266	}
267
268	# attempt to find sd(3) disk or partition
269	d.prefix = name;
270	for(i := len name; --i >= 0;)
271		if(name[i] == '/'){
272			d.prefix = name[0:i+1];
273			break;
274		}
275
276	if((d.ctlfd = sys->open(d.prefix+"ctl", Sys->ORDWR)) != nil) {
277		d.dtype = "sd";
278		d.part = name[len d.prefix:];
279		return opensd(d);
280	}
281
282	# assume we have just a normal file
283	d.dtype = "file";
284	return openfile(d);
285}
286
287mkwidth(d: ref Disk): ref Disk
288{
289	d.width = len sys->sprint("%bd", d.size);
290	return d;
291}
292
293isdigit(c: int): int
294{
295	return c >= '0' && c <= '9';
296}
297
298putchs(d: ref Disk, p: array of byte, lba: big)
299{
300	s := int (lba % big d.s);
301	h := int (lba / big d.s % big d.h);
302	c := int (lba / (big d.s * big d.h));
303
304	if(c >= 1024) {
305		c = 1023;
306		h = d.h - 1;
307		s = d.s - 1;
308	}
309
310	p[0] = byte h;
311	p[1] = byte (((s+1) & 16r3F) | ((c>>2) & 16rC0));
312	p[2] = byte c;
313}
314
315PCpart.bytes(p: self PCpart, d: ref Disk): array of byte
316{
317	a := array[TentrySize] of byte;
318	a[Oactive] = byte p.active;
319	a[Otype] = byte p.ptype;
320	putchs(d, a[Ostarth:], p.base+p.offset);
321	putchs(d, a[Oendh:], p.base+p.offset+p.size-big 1);
322	putle32(a[Oxlba:], p.offset);
323	putle32(a[Oxsize:], p.size);
324	return a;
325}
326
327PCpart.extract(a: array of byte, nil: ref Disk): PCpart
328{
329	p: PCpart;
330	p.active = int a[Oactive];
331	p.ptype = int a[Otype];
332	p.base = big 0;
333	p.offset = getle32(a[Oxlba:]);
334	p.size = getle32(a[Oxsize:]);
335	return p;
336}
337
338getle32(p: array of byte): big
339{
340	return (big p[3]<<24) | (big p[2]<<16) | (big p[1] << 8) | big p[0];
341}
342
343putle32(p: array of byte, i: big)
344{
345	p[0] = byte i;
346	p[1] = byte (i>>8);
347	p[2] = byte (i>>16);
348	p[3] = byte (i>>24);
349}
350
351Disk.readn(d: self ref Disk, buf: array of byte, nb: int): int
352{
353	return sys->readn(d.fd, buf, nb);
354}
355
356chstext(p: array of byte): string
357{
358	h := int p[0];
359	c := int p[2];
360	c |= (int p[1]&16rC0)<<2;
361	s := (int p[1] & 16r3F);
362	return sys->sprint("%d/%d/%d", c, h, s);
363}
364