]>
Commit | Line | Data |
---|---|---|
14c7c974 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights | |
7 | * Reserved. This file contains Original Code and/or Modifications of | |
8 | * Original Code as defined in and that are subject to the Apple Public | |
9 | * Source License Version 1.1 (the "License"). You may not use this file | |
10 | * except in compliance with the License. Please obtain a copy of the | |
11 | * License at http://www.apple.com/publicsource and read it before using | |
12 | * this file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the | |
19 | * License for the specific language governing rights and limitations | |
20 | * under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | /* | |
25 | * Mach Operating System | |
26 | * Copyright (c) 1990 Carnegie-Mellon University | |
27 | * Copyright (c) 1989 Carnegie-Mellon University | |
28 | * All rights reserved. The CMU software License Agreement specifies | |
29 | * the terms and conditions for use and redistribution. | |
30 | */ | |
31 | ||
32 | /* | |
33 | * INTEL CORPORATION PROPRIETARY INFORMATION | |
34 | * | |
35 | * This software is supplied under the terms of a license agreement or | |
36 | * nondisclosure agreement with Intel Corporation and may not be copied | |
37 | * nor disclosed except in accordance with the terms of that agreement. | |
38 | * | |
39 | * Copyright 1988, 1989 Intel Corporation | |
40 | */ | |
41 | ||
42 | /* | |
43 | * Copyright 1993 NeXT Computer, Inc. | |
44 | * All rights reserved. | |
45 | */ | |
46 | ||
47 | #define DRIVER_PRIVATE | |
48 | ||
49 | #include "sys/types.h" | |
50 | #include "legacy/disk.h" | |
51 | #include "legacy/fdisk.h" | |
52 | #include "libsaio.h" | |
53 | #include "memory.h" | |
54 | ||
55 | /* | |
56 | * Type and constant definitions. | |
57 | */ | |
58 | typedef struct disk_blk0 boot_sector; | |
59 | #define BOOT_SIGNATURE DISK_SIGNATURE | |
60 | #define PART_TYPE_EXT 0x05 | |
61 | #define PART_TYPE_APPLE 0xa8 | |
62 | #define UFS_FRONT_PORCH 0 | |
63 | ||
64 | #if DEBUG | |
65 | #define DPRINT(x) { printf x; } | |
66 | #define DSPRINT(x) { printf x; sleep(1); } | |
67 | #else | |
68 | #define DPRINT(x) | |
69 | #define DSPRINT(x) | |
70 | #endif | |
71 | ||
72 | /* | |
73 | * Function prototypes. | |
74 | */ | |
75 | extern void spinActivityIndicator(); | |
76 | static void diskActivityHook(); | |
77 | static int Biosread(int biosdev, int secno); | |
78 | static struct fdisk_part * find_partition(u_int8_t type, | |
79 | u_int8_t biosdev, | |
80 | BOOL mba); | |
81 | ||
82 | /* | |
83 | * diskinfo unpacking. | |
84 | */ | |
85 | #define SPT(di) ((di) & 0xff) | |
86 | #define HEADS(di) ((((di)>>8) & 0xff) + 1) | |
87 | #define SPC(di) (SPT(di) * HEADS(di)) | |
88 | #define BPS 512 /* sector size of the device */ | |
89 | #define N_CACHE_SECS (BIOS_LEN / BPS) | |
90 | ||
91 | /* | |
92 | * Stores the geometry of the disk in order to | |
93 | * perform LBA to CHS translation for non EBIOS calls. | |
94 | */ | |
95 | static struct diskinfo { | |
96 | int spt; /* sectors per track */ | |
97 | int spc; /* sectors per cylinder */ | |
98 | } diskinfo; | |
99 | ||
100 | /* | |
101 | * Globals variables. | |
102 | */ | |
103 | int label_secsize = BPS; | |
104 | char * b[NBUFS]; | |
105 | daddr_t blknos[NBUFS]; | |
106 | struct iob iob[NFILES]; | |
107 | ||
108 | /* | |
109 | * intbuf points to the start of the sector cache. BIOS calls will | |
110 | * store the sectors read into this memory area. If cache_valid | |
111 | * is TRUE, then intbuf contents are valid. Otherwise, ignore the | |
112 | * cache and read from disk. | |
113 | * | |
114 | * biosbuf points to a sector within the sector cache. | |
115 | */ | |
116 | static char * const intbuf = (char *)ptov(BIOS_ADDR); | |
117 | static BOOL cache_valid = FALSE; | |
118 | static char * biosbuf; | |
119 | ||
120 | /*========================================================================== | |
121 | * | |
122 | */ | |
123 | void | |
124 | devopen(char * name, struct iob * io) | |
125 | { | |
126 | static int last_biosdev = -1; | |
127 | static daddr_t last_offset = 0; | |
128 | ||
129 | struct fdisk_part * part; | |
130 | long di; | |
131 | ||
132 | io->i_error = 0; | |
133 | io->dirbuf_blkno = -1; | |
134 | ||
135 | // Use cached values if possible. | |
136 | // | |
137 | if (io->biosdev == last_biosdev) { | |
138 | io->i_boff = last_offset; | |
139 | return; | |
140 | } | |
141 | ||
142 | // initialize disk parameters -- spt and spc | |
143 | // must do this before doing reads from the device. | |
144 | ||
145 | di = get_diskinfo(io->biosdev); | |
146 | if (di == 0) { | |
147 | io->i_error = ENXIO; | |
148 | return; | |
149 | } | |
150 | ||
151 | diskinfo.spt = SPT(di); | |
152 | diskinfo.spc = diskinfo.spt * HEADS(di); | |
153 | ||
154 | // FIXME - io->partition is ignored. Doesn't make sense anymore. | |
155 | // we also don't overwrite the 'name' argument. | |
156 | // Whats special about "$LBL" ? | |
157 | ||
158 | part = find_partition(PART_TYPE_APPLE, io->biosdev, FALSE); | |
159 | if (part == NULL) { | |
160 | io->i_error = EIO; | |
161 | DSPRINT(("Unable to find partition: IO error\n")); | |
162 | } else { | |
163 | last_offset = io->i_boff = part->relsect + UFS_FRONT_PORCH/BPS; | |
164 | last_biosdev = io->biosdev; | |
165 | DSPRINT(("partition offset: %x\n", io->i_boff)); | |
166 | } | |
167 | } | |
168 | ||
169 | /*========================================================================== | |
170 | * | |
171 | */ | |
172 | void devflush() | |
173 | { | |
174 | cache_valid = FALSE; // invalidate the sector cache (intbuf) | |
175 | } | |
176 | ||
177 | /*========================================================================== | |
178 | * | |
179 | */ | |
180 | int devread(struct iob * io) | |
181 | { | |
182 | long sector; | |
183 | int offset; | |
184 | // int dev; | |
185 | ||
186 | io->i_flgs |= F_RDDATA; | |
187 | ||
188 | io->i_error = 0; // assume the best | |
189 | ||
190 | // dev = io->i_ino.i_dev; | |
191 | ||
192 | sector = io->i_bn * (label_secsize/BPS); | |
193 | ||
194 | for (offset = 0; offset < io->i_cc; offset += BPS) { | |
195 | ||
196 | io->i_error = Biosread(io->biosdev, sector); | |
197 | if (io->i_error) | |
198 | return (-1); | |
199 | ||
200 | /* copy 1 sector from the internal buffer biosbuf into buf */ | |
201 | bcopy(biosbuf, &io->i_ma[offset], BPS); | |
202 | ||
203 | sector++; | |
204 | } | |
205 | ||
206 | io->i_flgs &= ~F_TYPEMASK; | |
207 | ||
208 | return (io->i_cc); | |
209 | } | |
210 | ||
211 | /*========================================================================== | |
212 | * Maps (E)BIOS return codes to message strings. | |
213 | */ | |
214 | struct bios_error_info { | |
215 | int errno; | |
216 | const char * string; | |
217 | }; | |
218 | ||
219 | #define ECC_CORRECTED_ERR 0x11 | |
220 | ||
221 | static struct bios_error_info bios_errors[] = { | |
222 | {0x10, "Media error"}, | |
223 | {0x11, "Corrected ECC error"}, | |
224 | {0x20, "Controller or device error"}, | |
225 | {0x40, "Seek failed"}, | |
226 | {0x80, "Device timeout"}, | |
227 | {0xAA, "Drive not ready"}, | |
228 | {0x00, 0} | |
229 | }; | |
230 | ||
231 | static const char * | |
232 | bios_error(int errno) | |
233 | { | |
234 | struct bios_error_info * bp; | |
235 | ||
236 | for (bp = bios_errors; bp->errno; bp++) { | |
237 | if (bp->errno == errno) | |
238 | return bp->string; | |
239 | } | |
240 | return "Error 0x%02x"; // No string, print error code only | |
241 | } | |
242 | ||
243 | /*========================================================================== | |
244 | * Use BIOS INT13 calls to read the sector specified. This function will | |
245 | * also perform read-ahead to cache a few subsequent sector to the sector | |
246 | * cache. | |
247 | * | |
248 | * The fields in diskinfo structure must be filled in before calling this | |
249 | * function. | |
250 | * | |
251 | * Return: | |
252 | * Return code from INT13/F2 or INT13/F42 call. If operation was | |
253 | * successful, 0 is returned. | |
254 | * | |
255 | */ | |
256 | static int | |
257 | Biosread(int biosdev, int secno) | |
258 | { | |
259 | static int xbiosdev, xcyl, xhead, xsec, xnsecs; | |
260 | ||
261 | extern unsigned char uses_ebios[]; | |
262 | ||
263 | int rc; | |
264 | int cyl, head, sec; | |
265 | int spt, spc; | |
266 | int tries = 0; | |
267 | ||
268 | DSPRINT(("Biosread %d \n", secno)); | |
269 | ||
270 | // To read the disk sectors, use EBIOS if we can. Otherwise, | |
271 | // revert to the standard BIOS calls. | |
272 | // | |
273 | if ((biosdev >= BIOS_DEV_HD) && uses_ebios[biosdev - BIOS_DEV_HD]) { | |
274 | if (cache_valid && | |
275 | (biosdev == xbiosdev) && | |
276 | (secno >= xsec) && | |
277 | (secno < (xsec + xnsecs))) | |
278 | { | |
279 | biosbuf = intbuf + (BPS * (secno - xsec)); | |
280 | return 0; | |
281 | } | |
282 | ||
283 | xnsecs = N_CACHE_SECS; | |
284 | xsec = secno; | |
285 | ||
286 | while ((rc = ebiosread(biosdev, secno, xnsecs)) && (++tries < 5)) | |
287 | { | |
288 | if (rc == ECC_CORRECTED_ERR) { | |
289 | /* Ignore corrected ECC errors */ | |
290 | break; | |
291 | } | |
292 | error(" EBIOS read error: %s\n", bios_error(rc), rc); | |
293 | error(" Block %d Sectors %d\n", secno, xnsecs); | |
294 | sleep(1); | |
295 | } | |
296 | } | |
297 | else { | |
298 | spt = diskinfo.spt; // From previous INT13/F8 call. | |
299 | spc = diskinfo.spc; | |
300 | ||
301 | cyl = secno / spc; | |
302 | head = (secno % spc) / spt; | |
303 | sec = secno % spt; | |
304 | ||
305 | if (cache_valid && | |
306 | (biosdev == xbiosdev) && | |
307 | (cyl == xcyl) && | |
308 | (head == xhead) && | |
309 | (sec >= xsec) && | |
310 | (sec < (xsec + xnsecs))) | |
311 | { | |
312 | // this sector is in intbuf cache | |
313 | biosbuf = intbuf + (BPS * (sec - xsec)); | |
314 | return 0; | |
315 | } | |
316 | ||
317 | // Cache up to a track worth of sectors, but do not cross a | |
318 | // track boundary. | |
319 | // | |
320 | xcyl = cyl; | |
321 | xhead = head; | |
322 | xsec = sec; | |
323 | xnsecs = ((sec + N_CACHE_SECS) > spt) ? (spt - sec) : N_CACHE_SECS; | |
324 | ||
325 | while ((rc = biosread(biosdev, cyl, head, sec, xnsecs)) && | |
326 | (++tries < 5)) | |
327 | { | |
328 | if (rc == ECC_CORRECTED_ERR) { | |
329 | /* Ignore corrected ECC errors */ | |
330 | break; | |
331 | } | |
332 | error(" BIOS read error: %s\n", bios_error(rc), rc); | |
333 | error(" Block %d, Cyl %d Head %d Sector %d\n", | |
334 | secno, cyl, head, sec); | |
335 | sleep(1); | |
336 | } | |
337 | } | |
338 | ||
339 | // If the BIOS reported success, mark the sector cache as valid. | |
340 | // | |
341 | if (rc == 0) { | |
342 | cache_valid = TRUE; | |
343 | } | |
344 | biosbuf = intbuf; | |
345 | xbiosdev = biosdev; | |
346 | ||
347 | diskActivityHook(); | |
348 | ||
349 | return rc; | |
350 | } | |
351 | ||
352 | /*========================================================================== | |
353 | * Replace this function if you want to change | |
354 | * the way disk activity is indicated to the user. | |
355 | */ | |
356 | void | |
357 | diskActivityHook(void) | |
358 | { | |
359 | spinActivityIndicator(); | |
360 | } | |
361 | ||
362 | /*========================================================================== | |
363 | * Returns YES if the partition type specified is an extended fdisk | |
364 | * partition. | |
365 | */ | |
366 | static BOOL | |
367 | isExtendedPartition(u_int8_t type) | |
368 | { | |
369 | int i; | |
370 | ||
371 | u_int8_t extended_partitions[] = { | |
372 | 0x05, /* Extended */ | |
373 | 0x0f, /* Win95 extended */ | |
374 | 0x85, /* Linux extended */ | |
375 | }; | |
376 | ||
377 | for (i = 0; | |
378 | i < sizeof(extended_partitions)/sizeof(extended_partitions[0]); | |
379 | i++) | |
380 | { | |
381 | if (extended_partitions[i] == type) | |
382 | return YES; | |
383 | } | |
384 | return NO; | |
385 | } | |
386 | ||
387 | /*========================================================================== | |
388 | * Traverse the fdisk partition tables on disk until a partition is found | |
389 | * that matches the specified type. | |
390 | * | |
391 | * Arguments: | |
392 | * type - Partition type to search for (e.g. 0xa7 for NeXTSTEP). | |
393 | * biosdev - BIOS device unit. 0x80 and up for hard-drive. | |
394 | * mba - If true, the partition found must be marked active. | |
395 | * | |
396 | * Return: | |
397 | * A pointer to the matching partition entry in biosbuf memory. | |
398 | * Note that the starting LBA field in the partition entry is | |
399 | * modified to contain the absolute sector address, rather than | |
400 | * the relative address. | |
401 | * A NULL is returned if a match is not found. | |
402 | * | |
403 | * There are certain fdisk rules that allows us to simplify the search. | |
404 | * | |
405 | * - There can be 0-1 extended partition entry in any partition table. | |
406 | * - In the MBR, there can be 0-4 primary partitions entries. | |
407 | * - In the extended partition, there can be 0-1 logical partition entry. | |
408 | * | |
409 | */ | |
410 | struct fdisk_part * | |
411 | find_partition(u_int8_t type, u_int8_t biosdev, BOOL mba) | |
412 | { | |
413 | #define MAX_ITERATIONS 128 | |
414 | ||
415 | static u_int32_t iter = 0; | |
416 | static u_int32_t offset_root; | |
417 | static u_int32_t offset; | |
418 | ||
419 | int n; | |
420 | int rc; | |
421 | boot_sector * bootsect; | |
422 | struct fdisk_part * match = 0; | |
423 | struct fdisk_part * parts; | |
424 | ||
425 | if (iter == 0) { | |
426 | if (rc = Biosread(biosdev, 0)) // Read MBR at sector zero. | |
427 | return 0; | |
428 | offset = 0; | |
429 | } | |
430 | ||
431 | bootsect = (boot_sector *) biosbuf; | |
432 | if (bootsect->signature != BOOT_SIGNATURE) | |
433 | return 0; | |
434 | ||
435 | // Find a primary or a logical partition that matches the partition | |
436 | // type specified. | |
437 | // | |
438 | for (n = 0, parts = (struct fdisk_part *) bootsect->parts; | |
439 | n < 4; | |
440 | n++, parts++) | |
441 | { | |
442 | DSPRINT(("fdisk: [%d] %02x\n", iter, parts->systid)); | |
443 | ||
444 | if (mba && ((parts->bootid & 0x80) == 0)) | |
445 | continue; | |
446 | ||
447 | if (parts->systid == type) { | |
448 | // | |
449 | // Found it!!! | |
450 | // Make the relsect field (LBA starting sector) absolute by | |
451 | // adding in the offset. | |
452 | // | |
453 | parts->relsect += offset; | |
454 | ||
455 | DSPRINT(("Found: %x (%d)\n", parts->relsect, parts->numsect)); | |
456 | ||
457 | return parts; | |
458 | } | |
459 | } | |
460 | ||
461 | // Find if there is an extended partition entry that points to | |
462 | // an extended partition table. Note that we only allow up to | |
463 | // one extended partition per partition table. | |
464 | // | |
465 | for (n = 0, parts = (struct fdisk_part *) bootsect->parts; | |
466 | n < 4; | |
467 | n++, parts++) | |
468 | { | |
469 | DSPRINT(("fdisk: [E%d] %02x\n", iter, parts->systid)); | |
470 | ||
471 | if (isExtendedPartition(parts->systid)) | |
472 | { | |
473 | if (iter > MAX_ITERATIONS) // limit recursion depth | |
474 | return 0; | |
475 | ||
476 | if (iter == 0) | |
477 | offset = offset_root = parts->relsect; | |
478 | else | |
479 | offset = parts->relsect + offset_root; | |
480 | ||
481 | iter++; | |
482 | ||
483 | // Load extended partition table. | |
484 | // | |
485 | if (((rc = Biosread(biosdev, offset)) == 0) && | |
486 | (bootsect->signature == BOOT_SIGNATURE)) | |
487 | { | |
488 | match = find_partition(type, biosdev, mba); | |
489 | } | |
490 | ||
491 | iter--; | |
492 | ||
493 | break; | |
494 | } | |
495 | } | |
496 | ||
497 | return match; | |
498 | } |