|
|
1.1 ! root 1: /* ! 2: * Linux block driver support. ! 3: * ! 4: * Copyright (C) 1996 The University of Utah and the Computer Systems ! 5: * Laboratory at the University of Utah (CSL) ! 6: * ! 7: * This program is free software; you can redistribute it and/or modify ! 8: * it under the terms of the GNU General Public License as published by ! 9: * the Free Software Foundation; either version 2, or (at your option) ! 10: * any later version. ! 11: * ! 12: * This program is distributed in the hope that it will be useful, ! 13: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 14: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ! 15: * GNU General Public License for more details. ! 16: * ! 17: * You should have received a copy of the GNU General Public License ! 18: * along with this program; if not, write to the Free Software ! 19: * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ! 20: * ! 21: * Author: Shantanu Goel, University of Utah CSL ! 22: */ ! 23: ! 24: /* ! 25: * linux/drivers/block/ll_rw_blk.c ! 26: * ! 27: * Copyright (C) 1991, 1992 Linus Torvalds ! 28: * Copyright (C) 1994, Karl Keyte: Added support for disk statistics ! 29: */ ! 30: ! 31: /* ! 32: * linux/fs/block_dev.c ! 33: * ! 34: * Copyright (C) 1991, 1992 Linus Torvalds ! 35: */ ! 36: ! 37: /* ! 38: * linux/fs/buffer.c ! 39: * ! 40: * Copyright (C) 1991, 1992 Linus Torvalds ! 41: */ ! 42: ! 43: #include <sys/types.h> ! 44: #include <machine/spl.h> ! 45: #include <mach/mach_types.h> ! 46: #include <mach/kern_return.h> ! 47: #include <mach/mig_errors.h> ! 48: #include <mach/port.h> ! 49: #include <mach/vm_param.h> ! 50: #include <mach/notify.h> ! 51: ! 52: #include <ipc/ipc_port.h> ! 53: #include <ipc/ipc_space.h> ! 54: ! 55: #include <vm/vm_map.h> ! 56: #include <vm/vm_kern.h> ! 57: #include <vm/vm_page.h> ! 58: ! 59: #include <device/device_types.h> ! 60: #include <device/device_port.h> ! 61: #include <device/disk_status.h> ! 62: #include "device_reply.h" ! 63: ! 64: #include <linux_emul.h> ! 65: ! 66: #define MACH_INCLUDE ! 67: #include <linux/fs.h> ! 68: #include <linux/blk.h> ! 69: #include <linux/string.h> ! 70: #include <linux/errno.h> ! 71: #include <linux/fcntl.h> ! 72: #include <linux/major.h> ! 73: #include <linux/kdev_t.h> ! 74: #include <linux/delay.h> ! 75: #include <linux/malloc.h> ! 76: #include <linux/hdreg.h> ! 77: #include <asm/io.h> ! 78: ! 79: extern int linux_auto_config; ! 80: extern int linux_intr_pri; ! 81: extern int linux_to_mach_error (int); ! 82: ! 83: /* This task queue is not used in Mach: just for fixing undefined symbols. */ ! 84: DECLARE_TASK_QUEUE (tq_disk); ! 85: ! 86: /* Location of VTOC in units for sectors (512 bytes). */ ! 87: #define PDLOCATION 29 ! 88: ! 89: /* Linux kernel variables. */ ! 90: ! 91: /* Temporary data allocated on the stack. */ ! 92: struct temp_data ! 93: { ! 94: struct inode inode; ! 95: struct file file; ! 96: struct request req; ! 97: queue_head_t pages; ! 98: }; ! 99: ! 100: /* One of these exists for each ! 101: driver associated with a major number. */ ! 102: struct device_struct ! 103: { ! 104: const char *name; /* device name */ ! 105: struct file_operations *fops; /* operations vector */ ! 106: int busy:1; /* driver is being opened/closed */ ! 107: int want:1; /* someone wants to open/close driver */ ! 108: struct gendisk *gd; /* DOS partition information */ ! 109: int default_slice; /* what slice to use when none is given */ ! 110: struct disklabel **labels; /* disklabels for each DOS partition */ ! 111: }; ! 112: ! 113: /* An entry in the Mach name to Linux major number conversion table. */ ! 114: struct name_map ! 115: { ! 116: const char *name; /* Mach name for device */ ! 117: unsigned major; /* Linux major number */ ! 118: unsigned unit; /* Linux unit number */ ! 119: int read_only; /* 1 if device is read only */ ! 120: }; ! 121: ! 122: /* Driver operation table. */ ! 123: static struct device_struct blkdevs[MAX_BLKDEV]; ! 124: ! 125: /* Driver request function table. */ ! 126: struct blk_dev_struct blk_dev[MAX_BLKDEV] = ! 127: { ! 128: { NULL, NULL }, /* 0 no_dev */ ! 129: { NULL, NULL }, /* 1 dev mem */ ! 130: { NULL, NULL }, /* 2 dev fd */ ! 131: { NULL, NULL }, /* 3 dev ide0 or hd */ ! 132: { NULL, NULL }, /* 4 dev ttyx */ ! 133: { NULL, NULL }, /* 5 dev tty */ ! 134: { NULL, NULL }, /* 6 dev lp */ ! 135: { NULL, NULL }, /* 7 dev pipes */ ! 136: { NULL, NULL }, /* 8 dev sd */ ! 137: { NULL, NULL }, /* 9 dev st */ ! 138: { NULL, NULL }, /* 10 */ ! 139: { NULL, NULL }, /* 11 */ ! 140: { NULL, NULL }, /* 12 */ ! 141: { NULL, NULL }, /* 13 */ ! 142: { NULL, NULL }, /* 14 */ ! 143: { NULL, NULL }, /* 15 */ ! 144: { NULL, NULL }, /* 16 */ ! 145: { NULL, NULL }, /* 17 */ ! 146: { NULL, NULL }, /* 18 */ ! 147: { NULL, NULL }, /* 19 */ ! 148: { NULL, NULL }, /* 20 */ ! 149: { NULL, NULL }, /* 21 */ ! 150: { NULL, NULL } /* 22 dev ide1 */ ! 151: }; ! 152: ! 153: /* ! 154: * blk_size contains the size of all block-devices in units of 1024 byte ! 155: * sectors: ! 156: * ! 157: * blk_size[MAJOR][MINOR] ! 158: * ! 159: * if (!blk_size[MAJOR]) then no minor size checking is done. ! 160: */ ! 161: int *blk_size[MAX_BLKDEV] = { NULL, NULL, }; ! 162: ! 163: /* ! 164: * blksize_size contains the size of all block-devices: ! 165: * ! 166: * blksize_size[MAJOR][MINOR] ! 167: * ! 168: * if (!blksize_size[MAJOR]) then 1024 bytes is assumed. ! 169: */ ! 170: int *blksize_size[MAX_BLKDEV] = { NULL, NULL, }; ! 171: ! 172: /* ! 173: * hardsect_size contains the size of the hardware sector of a device. ! 174: * ! 175: * hardsect_size[MAJOR][MINOR] ! 176: * ! 177: * if (!hardsect_size[MAJOR]) ! 178: * then 512 bytes is assumed. ! 179: * else ! 180: * sector_size is hardsect_size[MAJOR][MINOR] ! 181: * This is currently set by some scsi device and read by the msdos fs driver ! 182: * This might be a some uses later. ! 183: */ ! 184: int *hardsect_size[MAX_BLKDEV] = { NULL, NULL, }; ! 185: ! 186: /* This specifies how many sectors to read ahead on the disk. ! 187: This is unused in Mach. It is here to make drivers compile. */ ! 188: int read_ahead[MAX_BLKDEV] = {0, }; ! 189: ! 190: /* Use to wait on when there are no free requests. ! 191: This is unused in Mach. It is here to make drivers compile. */ ! 192: struct wait_queue *wait_for_request = NULL; ! 193: ! 194: /* Map for allocating device memory. */ ! 195: extern vm_map_t device_io_map; ! 196: ! 197: /* Initialize block drivers. */ ! 198: int ! 199: blk_dev_init () ! 200: { ! 201: #ifdef CONFIG_BLK_DEV_IDE ! 202: ide_init (); ! 203: #endif ! 204: #ifdef CONFIG_BLK_DEV_FD ! 205: floppy_init (); ! 206: #else ! 207: outb_p (0xc, 0x3f2); ! 208: #endif ! 209: return 0; ! 210: } ! 211: ! 212: /* Return 1 if major number MAJOR corresponds to a disk device. */ ! 213: static inline int ! 214: disk_major (int major) ! 215: { ! 216: return (major == IDE0_MAJOR ! 217: || major == IDE1_MAJOR ! 218: || major == IDE2_MAJOR ! 219: || major == IDE3_MAJOR ! 220: || major == SCSI_DISK_MAJOR); ! 221: } ! 222: ! 223: /* Linux kernel block support routines. */ ! 224: ! 225: /* Register a driver for major number MAJOR, ! 226: with name NAME, and operations vector FOPS. */ ! 227: int ! 228: register_blkdev (unsigned major, const char *name, ! 229: struct file_operations *fops) ! 230: { ! 231: int err = 0; ! 232: ! 233: if (major == 0) ! 234: { ! 235: for (major = MAX_BLKDEV - 1; major > 0; major--) ! 236: if (blkdevs[major].fops == NULL) ! 237: goto out; ! 238: return -LINUX_EBUSY; ! 239: } ! 240: if (major >= MAX_BLKDEV) ! 241: return -LINUX_EINVAL; ! 242: if (blkdevs[major].fops && blkdevs[major].fops != fops) ! 243: return -LINUX_EBUSY; ! 244: ! 245: out: ! 246: blkdevs[major].name = name; ! 247: blkdevs[major].fops = fops; ! 248: blkdevs[major].busy = 0; ! 249: blkdevs[major].want = 0; ! 250: blkdevs[major].gd = NULL; ! 251: blkdevs[major].default_slice = 0; ! 252: blkdevs[major].labels = NULL; ! 253: return 0; ! 254: } ! 255: ! 256: /* Unregister the driver associated with ! 257: major number MAJOR and having the name NAME. */ ! 258: int ! 259: unregister_blkdev (unsigned major, const char *name) ! 260: { ! 261: int err; ! 262: ! 263: if (major >= MAX_BLKDEV) ! 264: return -LINUX_EINVAL; ! 265: if (! blkdevs[major].fops || strcmp (blkdevs[major].name, name)) ! 266: return -LINUX_EINVAL; ! 267: blkdevs[major].fops = NULL; ! 268: if (blkdevs[major].labels) ! 269: { ! 270: assert (blkdevs[major].gd); ! 271: kfree ((vm_offset_t) blkdevs[major].labels, ! 272: (sizeof (struct disklabel *) ! 273: * blkdevs[major].gd->max_p * blkdevs[major].gd->max_nr)); ! 274: } ! 275: return 0; ! 276: } ! 277: ! 278: void ! 279: set_blocksize (kdev_t dev, int size) ! 280: { ! 281: extern int *blksize_size[]; ! 282: ! 283: if (! blksize_size[MAJOR (dev)]) ! 284: return; ! 285: ! 286: switch (size) ! 287: { ! 288: case 512: ! 289: case 1024: ! 290: case 2048: ! 291: case 4096: ! 292: break; ! 293: default: ! 294: panic ("Invalid blocksize passed to set_blocksize"); ! 295: break; ! 296: } ! 297: blksize_size[MAJOR (dev)][MINOR (dev)] = size; ! 298: } ! 299: ! 300: /* Allocate a buffer SIZE bytes long. */ ! 301: static void * ! 302: alloc_buffer (int size) ! 303: { ! 304: vm_page_t m; ! 305: struct temp_data *d; ! 306: ! 307: assert (size <= PAGE_SIZE); ! 308: ! 309: if (! linux_auto_config) ! 310: { ! 311: while ((m = vm_page_grab ()) == 0) ! 312: VM_PAGE_WAIT (0); ! 313: d = current_thread ()->pcb->data; ! 314: assert (d); ! 315: queue_enter (&d->pages, m, vm_page_t, pageq); ! 316: return (void *) m->phys_addr; ! 317: } ! 318: return (void *) __get_free_pages (GFP_KERNEL, 0, ~0UL); ! 319: } ! 320: ! 321: /* Free buffer P which is SIZE bytes long. */ ! 322: static void ! 323: free_buffer (void *p, int size) ! 324: { ! 325: int i; ! 326: struct temp_data *d; ! 327: vm_page_t m; ! 328: ! 329: assert (size <= PAGE_SIZE); ! 330: ! 331: if (! linux_auto_config) ! 332: { ! 333: d = current_thread ()->pcb->data; ! 334: assert (d); ! 335: queue_iterate (&d->pages, m, vm_page_t, pageq) ! 336: { ! 337: if (m->phys_addr == (vm_offset_t) p) ! 338: { ! 339: queue_remove (&d->pages, m, vm_page_t, pageq); ! 340: vm_page_lock_queues (); ! 341: vm_page_free (m); ! 342: vm_page_lock_queues (); ! 343: return; ! 344: } ! 345: } ! 346: panic ("free_buffer"); ! 347: } ! 348: free_pages ((unsigned long) p, 0); ! 349: } ! 350: ! 351: /* Allocate a buffer of SIZE bytes and ! 352: associate it with block number BLOCK of device DEV. */ ! 353: struct buffer_head * ! 354: getblk (kdev_t dev, int block, int size) ! 355: { ! 356: struct buffer_head *bh; ! 357: static struct buffer_head bhead; ! 358: ! 359: assert (size <= PAGE_SIZE); ! 360: ! 361: if (! linux_auto_config) ! 362: bh = (struct buffer_head *) kalloc (sizeof (struct buffer_head)); ! 363: else ! 364: bh = &bhead; ! 365: if (bh) ! 366: { ! 367: memset (bh, 0, sizeof (struct buffer_head)); ! 368: bh->b_data = alloc_buffer (size); ! 369: if (! bh->b_data) ! 370: { ! 371: if (! linux_auto_config) ! 372: kfree ((vm_offset_t) bh, sizeof (struct buffer_head)); ! 373: return NULL; ! 374: } ! 375: bh->b_dev = dev; ! 376: bh->b_size = size; ! 377: bh->b_state = 1 << BH_Lock; ! 378: bh->b_blocknr = block; ! 379: } ! 380: return bh; ! 381: } ! 382: ! 383: /* Release buffer BH previously allocated by getblk. */ ! 384: void ! 385: __brelse (struct buffer_head *bh) ! 386: { ! 387: free_buffer (bh->b_data, bh->b_size); ! 388: if (! linux_auto_config) ! 389: kfree ((vm_offset_t) bh, sizeof (*bh)); ! 390: } ! 391: ! 392: /* Allocate a buffer of SIZE bytes and fill it with data ! 393: from device DEV starting at block number BLOCK. */ ! 394: struct buffer_head * ! 395: bread (kdev_t dev, int block, int size) ! 396: { ! 397: int err; ! 398: struct buffer_head *bh; ! 399: ! 400: bh = getblk (dev, block, size); ! 401: if (bh) ! 402: { ! 403: ll_rw_block (READ, 1, &bh); ! 404: wait_on_buffer (bh); ! 405: if (! buffer_uptodate (bh)) ! 406: { ! 407: __brelse (bh); ! 408: return NULL; ! 409: } ! 410: } ! 411: return bh; ! 412: } ! 413: ! 414: /* Return the block size for device DEV in *BSIZE and ! 415: log2(block size) in *BSHIFT. */ ! 416: static void ! 417: get_block_size (kdev_t dev, int *bsize, int *bshift) ! 418: { ! 419: int i; ! 420: ! 421: *bsize = BLOCK_SIZE; ! 422: if (blksize_size[MAJOR (dev)] ! 423: && blksize_size[MAJOR (dev)][MINOR (dev)]) ! 424: *bsize = blksize_size[MAJOR (dev)][MINOR (dev)]; ! 425: for (i = *bsize, *bshift = 0; i != 1; i >>= 1, (*bshift)++) ! 426: ; ! 427: } ! 428: ! 429: /* Enqueue request REQ on a driver's queue. */ ! 430: static inline void ! 431: enqueue_request (struct request *req) ! 432: { ! 433: struct request *tmp; ! 434: struct blk_dev_struct *dev; ! 435: ! 436: dev = blk_dev + MAJOR (req->rq_dev); ! 437: cli (); ! 438: tmp = dev->current_request; ! 439: if (! tmp) ! 440: { ! 441: dev->current_request = req; ! 442: (*dev->request_fn) (); ! 443: sti (); ! 444: return; ! 445: } ! 446: while (tmp->next) ! 447: { ! 448: if ((IN_ORDER (tmp, req) || ! IN_ORDER (tmp, tmp->next)) ! 449: && IN_ORDER (req, tmp->next)) ! 450: break; ! 451: tmp = tmp->next; ! 452: } ! 453: req->next = tmp->next; ! 454: tmp->next = req; ! 455: if (scsi_blk_major (MAJOR (req->rq_dev))) ! 456: (*dev->request_fn) (); ! 457: sti (); ! 458: } ! 459: ! 460: /* Perform the I/O operation RW on the buffer list BH ! 461: containing NR buffers. */ ! 462: void ! 463: ll_rw_block (int rw, int nr, struct buffer_head **bh) ! 464: { ! 465: int i, bshift, bsize; ! 466: unsigned major; ! 467: struct request *r; ! 468: static struct request req; ! 469: ! 470: major = MAJOR (bh[0]->b_dev); ! 471: assert (major < MAX_BLKDEV); ! 472: ! 473: get_block_size (bh[0]->b_dev, &bsize, &bshift); ! 474: ! 475: if (! linux_auto_config) ! 476: { ! 477: assert (current_thread ()->pcb->data); ! 478: r = &((struct temp_data *) current_thread ()->pcb->data)->req; ! 479: } ! 480: else ! 481: r = &req; ! 482: ! 483: for (i = 0, r->nr_sectors = 0; i < nr - 1; i++) ! 484: { ! 485: r->nr_sectors += bh[i]->b_size >> 9; ! 486: bh[i]->b_reqnext = bh[i + 1]; ! 487: } ! 488: r->nr_sectors += bh[i]->b_size >> 9; ! 489: bh[i]->b_reqnext = NULL; ! 490: ! 491: r->rq_status = RQ_ACTIVE; ! 492: r->rq_dev = bh[0]->b_dev; ! 493: r->cmd = rw; ! 494: r->errors = 0; ! 495: r->sector = bh[0]->b_blocknr << (bshift - 9); ! 496: r->current_nr_sectors = bh[0]->b_size >> 9; ! 497: r->buffer = bh[0]->b_data; ! 498: r->bh = bh[0]; ! 499: r->bhtail = bh[nr - 1]; ! 500: r->sem = NULL; ! 501: r->next = NULL; ! 502: ! 503: enqueue_request (r); ! 504: } ! 505: ! 506: #define BSIZE (1 << bshift) ! 507: #define BMASK (BSIZE - 1) ! 508: ! 509: /* Perform read/write operation RW on device DEV ! 510: starting at *off to/from buffer *BUF of size *RESID. ! 511: The device block size is given by BSHIFT. *OFF and ! 512: *RESID may be non-multiples of the block size. ! 513: *OFF, *BUF and *RESID are updated if the operation ! 514: completed successfully. */ ! 515: static int ! 516: rdwr_partial (int rw, kdev_t dev, loff_t *off, ! 517: char **buf, int *resid, int bshift) ! 518: { ! 519: int c, err = 0, o; ! 520: long sect, nsect; ! 521: struct buffer_head bhead, *bh = &bhead; ! 522: struct gendisk *gd; ! 523: ! 524: memset (bh, 0, sizeof (struct buffer_head)); ! 525: bh->b_state = 1 << BH_Lock; ! 526: bh->b_dev = dev; ! 527: bh->b_blocknr = *off >> bshift; ! 528: bh->b_size = BSIZE; ! 529: ! 530: /* Check if this device has non even number of blocks. */ ! 531: for (gd = gendisk_head, nsect = -1; gd; gd = gd->next) ! 532: if (gd->major == MAJOR (dev)) ! 533: { ! 534: nsect = gd->part[MINOR (dev)].nr_sects; ! 535: break; ! 536: } ! 537: if (nsect > 0) ! 538: { ! 539: sect = bh->b_blocknr << (bshift - 9); ! 540: assert ((nsect - sect) > 0); ! 541: if (nsect - sect < (BSIZE >> 9)) ! 542: bh->b_size = (nsect - sect) << 9; ! 543: } ! 544: bh->b_data = alloc_buffer (bh->b_size); ! 545: if (! bh->b_data) ! 546: return -LINUX_ENOMEM; ! 547: ll_rw_block (READ, 1, &bh); ! 548: wait_on_buffer (bh); ! 549: if (buffer_uptodate (bh)) ! 550: { ! 551: o = *off & BMASK; ! 552: c = bh->b_size - o; ! 553: assert (*resid <= c); ! 554: if (c > *resid) ! 555: c = *resid; ! 556: if (rw == READ) ! 557: memcpy (*buf, bh->b_data + o, c); ! 558: else ! 559: { ! 560: memcpy (bh->b_data + o, *buf, c); ! 561: bh->b_state = (1 << BH_Dirty) | (1 << BH_Lock); ! 562: ll_rw_block (WRITE, 1, &bh); ! 563: wait_on_buffer (bh); ! 564: if (! buffer_uptodate (bh)) ! 565: { ! 566: err = -LINUX_EIO; ! 567: goto out; ! 568: } ! 569: } ! 570: *buf += c; ! 571: *resid -= c; ! 572: *off += c; ! 573: } ! 574: else ! 575: err = -LINUX_EIO; ! 576: out: ! 577: free_buffer (bh->b_data, bh->b_size); ! 578: return err; ! 579: } ! 580: ! 581: #define BH_Bounce 16 ! 582: #define MAX_BUF VM_MAP_COPY_PAGE_LIST_MAX ! 583: ! 584: /* Perform read/write operation RW on device DEV ! 585: starting at *off to/from buffer *BUF of size *RESID. ! 586: The device block size is given by BSHIFT. *OFF and ! 587: *RESID must be multiples of the block size. ! 588: *OFF, *BUF and *RESID are updated if the operation ! 589: completed successfully. */ ! 590: static int ! 591: rdwr_full (int rw, kdev_t dev, loff_t *off, char **buf, int *resid, int bshift) ! 592: { ! 593: int cc, err = 0, i, j, nb, nbuf; ! 594: long blk; ! 595: struct buffer_head bhead[MAX_BUF], *bh, *bhp[MAX_BUF]; ! 596: ! 597: assert ((*off & BMASK) == 0); ! 598: assert (*resid >= bsize); ! 599: ! 600: nbuf = *resid >> bshift; ! 601: blk = *off >> bshift; ! 602: for (i = nb = 0, bh = bhead; nb < nbuf; bh++) ! 603: { ! 604: memset (bh, 0, sizeof (*bh)); ! 605: bh->b_dev = dev; ! 606: bh->b_blocknr = blk; ! 607: set_bit (BH_Lock, &bh->b_state); ! 608: if (rw == WRITE) ! 609: set_bit (BH_Dirty, &bh->b_state); ! 610: cc = PAGE_SIZE - (((int) *buf) & PAGE_MASK); ! 611: if (cc >= BSIZE && ((int) *buf & 511) == 0) ! 612: cc &= ~BMASK; ! 613: else ! 614: { ! 615: cc = PAGE_SIZE; ! 616: set_bit (BH_Bounce, &bh->b_state); ! 617: } ! 618: if (cc > ((nbuf - nb) << bshift)) ! 619: cc = (nbuf - nb) << bshift; ! 620: if (! test_bit (BH_Bounce, &bh->b_state)) ! 621: bh->b_data = (char *) pmap_extract (vm_map_pmap (device_io_map), ! 622: (((vm_offset_t) *buf) ! 623: + (nb << bshift))); ! 624: else ! 625: { ! 626: bh->b_data = alloc_buffer (cc); ! 627: if (! bh->b_data) ! 628: { ! 629: err = -LINUX_ENOMEM; ! 630: break; ! 631: } ! 632: if (rw == WRITE) ! 633: memcpy (bh->b_data, *buf + (nb << bshift), cc); ! 634: } ! 635: bh->b_size = cc; ! 636: bhp[i] = bh; ! 637: nb += cc >> bshift; ! 638: blk += nb; ! 639: if (++i == MAX_BUF) ! 640: break; ! 641: } ! 642: if (! err) ! 643: { ! 644: ll_rw_block (rw, i, bhp); ! 645: wait_on_buffer (bhp[i - 1]); ! 646: } ! 647: for (bh = bhead, cc = 0, j = 0; j < i; cc += bh->b_size, bh++, j++) ! 648: { ! 649: if (! err && buffer_uptodate (bh) ! 650: && rw == READ && test_bit (BH_Bounce, &bh->b_state)) ! 651: memcpy (*buf + cc, bh->b_data, bh->b_size); ! 652: else if (! err && ! buffer_uptodate (bh)) ! 653: err = -LINUX_EIO; ! 654: if (test_bit (BH_Bounce, &bh->b_state)) ! 655: free_buffer (bh->b_data, bh->b_size); ! 656: } ! 657: if (! err) ! 658: { ! 659: *buf += cc; ! 660: *resid -= cc; ! 661: *off += cc; ! 662: } ! 663: return err; ! 664: } ! 665: ! 666: /* Perform read/write operation RW on device DEV ! 667: starting at *off to/from buffer BUF of size COUNT. ! 668: *OFF is updated if the operation completed successfully. */ ! 669: static int ! 670: do_rdwr (int rw, kdev_t dev, loff_t *off, char *buf, int count) ! 671: { ! 672: int bsize, bshift, err = 0, resid = count; ! 673: ! 674: get_block_size (dev, &bsize, &bshift); ! 675: if (*off & BMASK) ! 676: err = rdwr_partial (rw, dev, off, &buf, &resid, bshift); ! 677: while (resid >= bsize && ! err) ! 678: err = rdwr_full (rw, dev, off, &buf, &resid, bshift); ! 679: if (! err && resid) ! 680: err = rdwr_partial (rw, dev, off, &buf, &resid, bshift); ! 681: return err ? err : count - resid; ! 682: } ! 683: ! 684: int ! 685: block_write (struct inode *inode, struct file *filp, ! 686: const char *buf, int count) ! 687: { ! 688: return do_rdwr (WRITE, inode->i_rdev, &filp->f_pos, (char *) buf, count); ! 689: } ! 690: ! 691: int ! 692: block_read (struct inode *inode, struct file *filp, char *buf, int count) ! 693: { ! 694: return do_rdwr (READ, inode->i_rdev, &filp->f_pos, buf, count); ! 695: } ! 696: ! 697: /* ! 698: * This routine checks whether a removable media has been changed, ! 699: * and invalidates all buffer-cache-entries in that case. This ! 700: * is a relatively slow routine, so we have to try to minimize using ! 701: * it. Thus it is called only upon a 'mount' or 'open'. This ! 702: * is the best way of combining speed and utility, I think. ! 703: * People changing diskettes in the middle of an operation deserve ! 704: * to loose :-) ! 705: */ ! 706: int ! 707: check_disk_change (kdev_t dev) ! 708: { ! 709: unsigned i; ! 710: struct file_operations * fops; ! 711: ! 712: i = MAJOR(dev); ! 713: if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL) ! 714: return 0; ! 715: if (fops->check_media_change == NULL) ! 716: return 0; ! 717: if (! (*fops->check_media_change) (dev)) ! 718: return 0; ! 719: ! 720: /* printf ("Disk change detected on device %s\n", kdevname(dev));*/ ! 721: ! 722: if (fops->revalidate) ! 723: (*fops->revalidate) (dev); ! 724: ! 725: return 1; ! 726: } ! 727: ! 728: /* Mach device interface routines. */ ! 729: ! 730: /* Mach name to Linux major/minor number mapping table. */ ! 731: static struct name_map name_to_major[] = ! 732: { ! 733: /* IDE disks */ ! 734: { "hd0", IDE0_MAJOR, 0, 0 }, ! 735: { "hd1", IDE0_MAJOR, 1, 0 }, ! 736: { "hd2", IDE1_MAJOR, 0, 0 }, ! 737: { "hd3", IDE1_MAJOR, 1, 0 }, ! 738: { "hd4", IDE2_MAJOR, 0, 0 }, ! 739: { "hd5", IDE2_MAJOR, 1, 0 }, ! 740: { "hd6", IDE3_MAJOR, 0, 0 }, ! 741: { "hd7", IDE3_MAJOR, 1, 0 }, ! 742: ! 743: /* IDE CDROMs */ ! 744: { "wcd0", IDE0_MAJOR, 0, 1 }, ! 745: { "wcd1", IDE0_MAJOR, 1, 1 }, ! 746: { "wcd2", IDE1_MAJOR, 0, 1 }, ! 747: { "wcd3", IDE1_MAJOR, 1, 1 }, ! 748: { "wcd4", IDE2_MAJOR, 0, 1 }, ! 749: { "wcd5", IDE2_MAJOR, 1, 1 }, ! 750: { "wcd6", IDE3_MAJOR, 0, 1 }, ! 751: { "wcd7", IDE3_MAJOR, 1, 1 }, ! 752: ! 753: /* SCSI disks */ ! 754: { "sd0", SCSI_DISK_MAJOR, 0, 0 }, ! 755: { "sd1", SCSI_DISK_MAJOR, 1, 0 }, ! 756: { "sd2", SCSI_DISK_MAJOR, 2, 0 }, ! 757: { "sd3", SCSI_DISK_MAJOR, 3, 0 }, ! 758: { "sd4", SCSI_DISK_MAJOR, 4, 0 }, ! 759: { "sd5", SCSI_DISK_MAJOR, 5, 0 }, ! 760: { "sd6", SCSI_DISK_MAJOR, 6, 0 }, ! 761: { "sd7", SCSI_DISK_MAJOR, 7, 0 }, ! 762: ! 763: /* SCSI CDROMs */ ! 764: { "cd0", SCSI_CDROM_MAJOR, 0, 1 }, ! 765: { "cd1", SCSI_CDROM_MAJOR, 1, 1 }, ! 766: ! 767: /* Floppy disks */ ! 768: { "fd0", FLOPPY_MAJOR, 0, 0 }, ! 769: { "fd1", FLOPPY_MAJOR, 1, 0 }, ! 770: }; ! 771: ! 772: #define NUM_NAMES (sizeof (name_to_major) / sizeof (name_to_major[0])) ! 773: ! 774: /* One of these is associated with each open instance of a device. */ ! 775: struct block_data ! 776: { ! 777: const char *name; /* Mach name for device */ ! 778: int want:1; /* someone is waiting for I/O to complete */ ! 779: int open_count; /* number of opens */ ! 780: int iocount; /* number of pending I/O operations */ ! 781: int part; /* BSD partition number (-1 if none) */ ! 782: int flags; /* Linux file flags */ ! 783: int mode; /* Linux file mode */ ! 784: kdev_t dev; /* Linux device number */ ! 785: ipc_port_t port; /* port representing device */ ! 786: struct device_struct *ds; /* driver operation table entry */ ! 787: struct device device; /* generic device header */ ! 788: struct name_map *np; /* name to inode map */ ! 789: struct block_data *next; /* forward link */ ! 790: }; ! 791: ! 792: /* List of open devices. */ ! 793: static struct block_data *open_list; ! 794: ! 795: /* Forward declarations. */ ! 796: ! 797: extern struct device_emulation_ops linux_block_emulation_ops; ! 798: ! 799: static io_return_t device_close (void *); ! 800: ! 801: /* Return a send right for block device BD. */ ! 802: static ipc_port_t ! 803: dev_to_port (void *bd) ! 804: { ! 805: return (bd ! 806: ? ipc_port_make_send (((struct block_data *) bd)->port) ! 807: : IP_NULL); ! 808: } ! 809: ! 810: /* Return 1 if C is a letter of the alphabet. */ ! 811: static inline int ! 812: isalpha (int c) ! 813: { ! 814: return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); ! 815: } ! 816: ! 817: /* Return 1 if C is a digit. */ ! 818: static inline int ! 819: isdigit (int c) ! 820: { ! 821: return c >= '0' && c <= '9'; ! 822: } ! 823: ! 824: /* Find the name map entry for device NAME. ! 825: Set *SLICE to be the DOS partition and ! 826: *PART the BSD/Mach partition, if any. */ ! 827: static struct name_map * ! 828: find_name (char *name, int *slice, int *part) ! 829: { ! 830: char *p, *q; ! 831: int i, len; ! 832: struct name_map *np; ! 833: ! 834: /* Parse name into name, unit, DOS partition (slice) and partition. */ ! 835: for (*slice = 0, *part = -1, p = name; isalpha (*p); p++) ! 836: ; ! 837: if (p == name || ! isdigit (*p)) ! 838: return NULL; ! 839: do ! 840: p++; ! 841: while (isdigit (*p)); ! 842: if (*p) ! 843: { ! 844: q = p; ! 845: if (*q == 's' && isdigit (*(q + 1))) ! 846: { ! 847: q++; ! 848: do ! 849: *slice = *slice * 10 + *q++ - '0'; ! 850: while (isdigit (*q)); ! 851: if (! *q) ! 852: goto find_major; ! 853: } ! 854: if (! isalpha (*q) || *(q + 1)) ! 855: return NULL; ! 856: *part = *q - 'a'; ! 857: } ! 858: ! 859: find_major: ! 860: /* Convert name to major number. */ ! 861: for (i = 0, np = name_to_major; i < NUM_NAMES; i++, np++) ! 862: { ! 863: len = strlen (np->name); ! 864: if (len == (p - name) && ! strncmp (np->name, name, len)) ! 865: return np; ! 866: } ! 867: return NULL; ! 868: } ! 869: ! 870: /* Attempt to read a BSD disklabel from device DEV. */ ! 871: static struct disklabel * ! 872: read_bsd_label (kdev_t dev) ! 873: { ! 874: int bsize, bshift; ! 875: struct buffer_head *bh; ! 876: struct disklabel *dlp, *lp = NULL; ! 877: ! 878: get_block_size (dev, &bsize, &bshift); ! 879: bh = bread (dev, LBLLOC >> (bshift - 9), bsize); ! 880: if (bh) ! 881: { ! 882: dlp = (struct disklabel *) (bh->b_data + ((LBLLOC << 9) & (bsize - 1))); ! 883: if (dlp->d_magic == DISKMAGIC && dlp->d_magic2 == DISKMAGIC) ! 884: { ! 885: lp = (struct disklabel *) kalloc (sizeof (*lp)); ! 886: assert (lp); ! 887: memcpy (lp, dlp, sizeof (*lp)); ! 888: } ! 889: __brelse (bh); ! 890: } ! 891: return lp; ! 892: } ! 893: ! 894: /* Attempt to read a VTOC from device DEV. */ ! 895: static struct disklabel * ! 896: read_vtoc (kdev_t dev) ! 897: { ! 898: int bshift, bsize, i; ! 899: struct buffer_head *bh; ! 900: struct evtoc *evp; ! 901: struct disklabel *lp = NULL; ! 902: ! 903: get_block_size (dev, &bsize, &bshift); ! 904: bh = bread (dev, PDLOCATION >> (bshift - 9), bsize); ! 905: if (bh) ! 906: { ! 907: evp = (struct evtoc *) (bh->b_data + ((PDLOCATION << 9) & (bsize - 1))); ! 908: if (evp->sanity == VTOC_SANE) ! 909: { ! 910: lp = (struct disklabel *) kalloc (sizeof (*lp)); ! 911: assert (lp); ! 912: lp->d_npartitions = evp->nparts; ! 913: if (lp->d_npartitions > MAXPARTITIONS) ! 914: lp->d_npartitions = MAXPARTITIONS; ! 915: for (i = 0; i < lp->d_npartitions; i++) ! 916: { ! 917: lp->d_partitions[i].p_size = evp->part[i].p_size; ! 918: lp->d_partitions[i].p_offset = evp->part[i].p_start; ! 919: lp->d_partitions[i].p_fstype = FS_BSDFFS; ! 920: } ! 921: } ! 922: __brelse (bh); ! 923: } ! 924: return lp; ! 925: } ! 926: ! 927: /* Initialize BSD/Mach partition table for device ! 928: specified by NP, DS and *DEV. Check SLICE and *PART for validity. */ ! 929: static kern_return_t ! 930: init_partition (struct name_map *np, kdev_t *dev, ! 931: struct device_struct *ds, int slice, int *part) ! 932: { ! 933: int err, i, j; ! 934: struct disklabel *lp; ! 935: struct gendisk *gd = ds->gd; ! 936: struct partition *p; ! 937: struct temp_data *d = current_thread ()->pcb->data; ! 938: ! 939: if (! gd) ! 940: { ! 941: *part = -1; ! 942: return 0; ! 943: } ! 944: if (ds->labels) ! 945: goto check; ! 946: ds->labels = (struct disklabel **) kalloc (sizeof (struct disklabel *) ! 947: * gd->max_nr * gd->max_p); ! 948: if (! ds->labels) ! 949: return D_NO_MEMORY; ! 950: memset ((void *) ds->labels, 0, ! 951: sizeof (struct disklabel *) * gd->max_nr * gd->max_p); ! 952: for (i = 1; i < gd->max_p; i++) ! 953: { ! 954: d->inode.i_rdev = *dev | i; ! 955: if (gd->part[MINOR (d->inode.i_rdev)].nr_sects <= 0 ! 956: || gd->part[MINOR (d->inode.i_rdev)].start_sect < 0) ! 957: continue; ! 958: linux_intr_pri = SPL5; ! 959: d->file.f_flags = 0; ! 960: d->file.f_mode = O_RDONLY; ! 961: if (ds->fops->open && (*ds->fops->open) (&d->inode, &d->file)) ! 962: continue; ! 963: lp = read_bsd_label (d->inode.i_rdev); ! 964: if (! lp) ! 965: lp = read_vtoc (d->inode.i_rdev); ! 966: if (ds->fops->release) ! 967: (*ds->fops->release) (&d->inode, &d->file); ! 968: if (lp) ! 969: { ! 970: if (ds->default_slice == 0) ! 971: ds->default_slice = i; ! 972: for (j = 0, p = lp->d_partitions; j < lp->d_npartitions; j++, p++) ! 973: { ! 974: if (p->p_offset < 0 || p->p_size <= 0) ! 975: continue; ! 976: ! 977: /* Sanity check. */ ! 978: if (p->p_size > gd->part[MINOR (d->inode.i_rdev)].nr_sects) ! 979: p->p_size = gd->part[MINOR (d->inode.i_rdev)].nr_sects; ! 980: } ! 981: } ! 982: ds->labels[MINOR (d->inode.i_rdev)] = lp; ! 983: } ! 984: ! 985: check: ! 986: if (*part >= 0 && slice == 0) ! 987: slice = ds->default_slice; ! 988: if (*part >= 0 && slice == 0) ! 989: return D_NO_SUCH_DEVICE; ! 990: *dev = MKDEV (MAJOR (*dev), MINOR (*dev) | slice); ! 991: if (slice >= gd->max_p ! 992: || gd->part[MINOR (*dev)].start_sect < 0 ! 993: || gd->part[MINOR (*dev)].nr_sects <= 0) ! 994: return D_NO_SUCH_DEVICE; ! 995: if (*part >= 0) ! 996: { ! 997: lp = ds->labels[MINOR (*dev)]; ! 998: if (! lp ! 999: || *part >= lp->d_npartitions ! 1000: || lp->d_partitions[*part].p_offset < 0 ! 1001: || lp->d_partitions[*part].p_size <= 0) ! 1002: return D_NO_SUCH_DEVICE; ! 1003: } ! 1004: return 0; ! 1005: } ! 1006: ! 1007: #define DECL_DATA struct temp_data td ! 1008: #define INIT_DATA() \ ! 1009: { \ ! 1010: queue_init (&td.pages); \ ! 1011: td.inode.i_rdev = bd->dev; \ ! 1012: td.file.f_mode = bd->mode; \ ! 1013: td.file.f_flags = bd->flags; \ ! 1014: current_thread ()->pcb->data = &td; \ ! 1015: } ! 1016: ! 1017: static io_return_t ! 1018: device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type, ! 1019: dev_mode_t mode, char *name, device_t *devp) ! 1020: { ! 1021: int part, slice, err; ! 1022: unsigned major, minor; ! 1023: kdev_t dev; ! 1024: ipc_port_t notify; ! 1025: struct block_data *bd = NULL, *bdp; ! 1026: struct device_struct *ds; ! 1027: struct gendisk *gd; ! 1028: struct name_map *np; ! 1029: DECL_DATA; ! 1030: ! 1031: np = find_name (name, &slice, &part); ! 1032: if (! np) ! 1033: return D_NO_SUCH_DEVICE; ! 1034: major = np->major; ! 1035: ds = &blkdevs[major]; ! 1036: ! 1037: /* Check that driver exists. */ ! 1038: if (! ds->fops) ! 1039: return D_NO_SUCH_DEVICE; ! 1040: ! 1041: /* Wait for any other open/close calls to finish. */ ! 1042: ds = &blkdevs[major]; ! 1043: while (ds->busy) ! 1044: { ! 1045: ds->want = 1; ! 1046: assert_wait ((event_t) ds, FALSE); ! 1047: schedule (); ! 1048: } ! 1049: ds->busy = 1; ! 1050: ! 1051: /* Compute minor number. */ ! 1052: if (! ds->gd) ! 1053: { ! 1054: for (gd = gendisk_head; gd && gd->major != major; gd = gd->next) ! 1055: ; ! 1056: ds->gd = gd; ! 1057: } ! 1058: minor = np->unit; ! 1059: gd = ds->gd; ! 1060: if (gd) ! 1061: minor <<= gd->minor_shift; ! 1062: dev = MKDEV (major, minor); ! 1063: ! 1064: queue_init (&td.pages); ! 1065: current_thread ()->pcb->data = &td; ! 1066: ! 1067: /* Check partition. */ ! 1068: err = init_partition (np, &dev, ds, slice, &part); ! 1069: if (err) ! 1070: goto out; ! 1071: ! 1072: /* Initialize file structure. */ ! 1073: switch (mode & (D_READ|D_WRITE)) ! 1074: { ! 1075: case D_WRITE: ! 1076: td.file.f_mode = O_WRONLY; ! 1077: break; ! 1078: ! 1079: case D_READ|D_WRITE: ! 1080: td.file.f_mode = O_RDWR; ! 1081: break; ! 1082: ! 1083: default: ! 1084: td.file.f_mode = O_RDONLY; ! 1085: break; ! 1086: } ! 1087: td.file.f_flags = (mode & D_NODELAY) ? O_NDELAY : 0; ! 1088: ! 1089: /* Check if the device is currently open. */ ! 1090: for (bdp = open_list; bdp; bdp = bdp->next) ! 1091: if (bdp->dev == dev ! 1092: && bdp->part == part ! 1093: && bdp->mode == td.file.f_mode ! 1094: && bdp->flags == td.file.f_flags) ! 1095: { ! 1096: bd = bdp; ! 1097: goto out; ! 1098: } ! 1099: ! 1100: /* Open the device. */ ! 1101: if (ds->fops->open) ! 1102: { ! 1103: td.inode.i_rdev = dev; ! 1104: linux_intr_pri = SPL5; ! 1105: err = (*ds->fops->open) (&td.inode, &td.file); ! 1106: if (err) ! 1107: { ! 1108: err = linux_to_mach_error (err); ! 1109: goto out; ! 1110: } ! 1111: } ! 1112: ! 1113: /* Allocate and initialize device data. */ ! 1114: bd = (struct block_data *) kalloc (sizeof (struct block_data)); ! 1115: if (! bd) ! 1116: { ! 1117: err = D_NO_MEMORY; ! 1118: goto bad; ! 1119: } ! 1120: bd->want = 0; ! 1121: bd->open_count = 0; ! 1122: bd->iocount = 0; ! 1123: bd->part = part; ! 1124: bd->ds = ds; ! 1125: bd->device.emul_data = bd; ! 1126: bd->device.emul_ops = &linux_block_emulation_ops; ! 1127: bd->dev = dev; ! 1128: bd->mode = td.file.f_mode; ! 1129: bd->flags = td.file.f_flags; ! 1130: bd->port = ipc_port_alloc_kernel (); ! 1131: if (bd->port == IP_NULL) ! 1132: { ! 1133: err = KERN_RESOURCE_SHORTAGE; ! 1134: goto bad; ! 1135: } ! 1136: ipc_kobject_set (bd->port, (ipc_kobject_t) &bd->device, IKOT_DEVICE); ! 1137: notify = ipc_port_make_sonce (bd->port); ! 1138: ip_lock (bd->port); ! 1139: ipc_port_nsrequest (bd->port, 1, notify, ¬ify); ! 1140: assert (notify == IP_NULL); ! 1141: goto out; ! 1142: ! 1143: bad: ! 1144: if (ds->fops->release) ! 1145: (*ds->fops->release) (&td.inode, &td.file); ! 1146: ! 1147: out: ! 1148: ds->busy = 0; ! 1149: if (ds->want) ! 1150: { ! 1151: ds->want = 0; ! 1152: thread_wakeup ((event_t) ds); ! 1153: } ! 1154: ! 1155: if (bd && bd->open_count > 0) ! 1156: { ! 1157: if (err) ! 1158: *devp = NULL; ! 1159: else ! 1160: { ! 1161: *devp = &bd->device; ! 1162: bd->open_count++; ! 1163: } ! 1164: return err; ! 1165: } ! 1166: ! 1167: if (err) ! 1168: { ! 1169: if (bd) ! 1170: { ! 1171: if (bd->port != IP_NULL) ! 1172: { ! 1173: ipc_kobject_set (bd->port, IKO_NULL, IKOT_NONE); ! 1174: ipc_port_dealloc_kernel (bd->port); ! 1175: } ! 1176: kfree ((vm_offset_t) bd, sizeof (struct block_data)); ! 1177: bd = NULL; ! 1178: } ! 1179: } ! 1180: else ! 1181: { ! 1182: bd->open_count = 1; ! 1183: bd->next = open_list; ! 1184: open_list = bd; ! 1185: } ! 1186: ! 1187: if (IP_VALID (reply_port)) ! 1188: ds_device_open_reply (reply_port, reply_port_type, err, dev_to_port (bd)); ! 1189: else if (! err) ! 1190: device_close (bd); ! 1191: ! 1192: return MIG_NO_REPLY; ! 1193: } ! 1194: ! 1195: static io_return_t ! 1196: device_close (void *d) ! 1197: { ! 1198: struct block_data *bd = d, *bdp, **prev; ! 1199: struct device_struct *ds = bd->ds; ! 1200: DECL_DATA; ! 1201: ! 1202: INIT_DATA (); ! 1203: ! 1204: /* Wait for any other open/close to complete. */ ! 1205: while (ds->busy) ! 1206: { ! 1207: ds->want = 1; ! 1208: assert_wait ((event_t) ds, FALSE); ! 1209: schedule (); ! 1210: } ! 1211: ds->busy = 1; ! 1212: ! 1213: if (--bd->open_count == 0) ! 1214: { ! 1215: /* Wait for pending I/O to complete. */ ! 1216: while (bd->iocount > 0) ! 1217: { ! 1218: bd->want = 1; ! 1219: assert_wait ((event_t) bd, FALSE); ! 1220: schedule (); ! 1221: } ! 1222: ! 1223: /* Remove device from open list. */ ! 1224: prev = &open_list; ! 1225: bdp = open_list; ! 1226: while (bdp) ! 1227: { ! 1228: if (bdp == bd) ! 1229: { ! 1230: *prev = bdp->next; ! 1231: break; ! 1232: } ! 1233: prev = &bdp->next; ! 1234: bdp = bdp->next; ! 1235: } ! 1236: ! 1237: assert (bdp == bd); ! 1238: ! 1239: if (ds->fops->release) ! 1240: (*ds->fops->release) (&td.inode, &td.file); ! 1241: ! 1242: ipc_kobject_set (bd->port, IKO_NULL, IKOT_NONE); ! 1243: ipc_port_dealloc_kernel (bd->port); ! 1244: kfree ((vm_offset_t) bd, sizeof (struct block_data)); ! 1245: } ! 1246: ! 1247: ds->busy = 0; ! 1248: if (ds->want) ! 1249: { ! 1250: ds->want = 0; ! 1251: thread_wakeup ((event_t) ds); ! 1252: } ! 1253: return D_SUCCESS; ! 1254: } ! 1255: ! 1256: #define MAX_COPY (VM_MAP_COPY_PAGE_LIST_MAX << PAGE_SHIFT) ! 1257: ! 1258: /* Check block BN and size COUNT for I/O validity ! 1259: to from device BD. Set *OFF to the byte offset ! 1260: where I/O is to begin and return the size of transfer. */ ! 1261: static int ! 1262: check_limit (struct block_data *bd, loff_t *off, long bn, int count) ! 1263: { ! 1264: int major, minor; ! 1265: long maxsz, sz; ! 1266: struct disklabel *lp = NULL; ! 1267: ! 1268: if (count <= 0) ! 1269: return count; ! 1270: ! 1271: major = MAJOR (bd->dev); ! 1272: minor = MINOR (bd->dev); ! 1273: ! 1274: if (bd->ds->gd) ! 1275: { ! 1276: if (bd->part >= 0) ! 1277: { ! 1278: assert (bd->ds->labels); ! 1279: assert (bd->ds->labels[minor]); ! 1280: lp = bd->ds->labels[minor]; ! 1281: maxsz = lp->d_partitions[bd->part].p_size; ! 1282: } ! 1283: else ! 1284: maxsz = bd->ds->gd->part[minor].nr_sects; ! 1285: } ! 1286: else ! 1287: { ! 1288: assert (blk_size[major]); ! 1289: maxsz = blk_size[major][minor] << (BLOCK_SIZE_BITS - 9); ! 1290: } ! 1291: assert (maxsz > 0); ! 1292: sz = maxsz - bn; ! 1293: if (sz <= 0) ! 1294: return sz; ! 1295: if (sz < ((count + 511) >> 9)) ! 1296: count = sz << 9; ! 1297: if (lp) ! 1298: bn += (lp->d_partitions[bd->part].p_offset ! 1299: - bd->ds->gd->part[minor].start_sect); ! 1300: *off = (loff_t) bn << 9; ! 1301: bd->iocount++; ! 1302: return count; ! 1303: } ! 1304: ! 1305: static io_return_t ! 1306: device_write (void *d, ipc_port_t reply_port, ! 1307: mach_msg_type_name_t reply_port_type, dev_mode_t mode, ! 1308: recnum_t bn, io_buf_ptr_t data, unsigned int orig_count, ! 1309: int *bytes_written) ! 1310: { ! 1311: int resid, amt, i; ! 1312: int count = (int) orig_count; ! 1313: io_return_t err = 0; ! 1314: vm_map_copy_t copy; ! 1315: vm_offset_t addr, uaddr; ! 1316: vm_size_t len, size; ! 1317: struct block_data *bd = d; ! 1318: DECL_DATA; ! 1319: ! 1320: INIT_DATA (); ! 1321: ! 1322: *bytes_written = 0; ! 1323: ! 1324: if (bd->mode == O_RDONLY) ! 1325: return D_INVALID_OPERATION; ! 1326: if (! bd->ds->fops->write) ! 1327: return D_READ_ONLY; ! 1328: count = check_limit (bd, &td.file.f_pos, bn, count); ! 1329: if (count < 0) ! 1330: return D_INVALID_SIZE; ! 1331: if (count == 0) ! 1332: { ! 1333: vm_map_copy_discard (copy); ! 1334: return 0; ! 1335: } ! 1336: ! 1337: resid = count; ! 1338: copy = (vm_map_copy_t) data; ! 1339: uaddr = copy->offset; ! 1340: ! 1341: /* Allocate a kernel buffer. */ ! 1342: size = round_page (uaddr + count) - trunc_page (uaddr); ! 1343: if (size > MAX_COPY) ! 1344: size = MAX_COPY; ! 1345: addr = vm_map_min (device_io_map); ! 1346: err = vm_map_enter (device_io_map, &addr, size, 0, TRUE, ! 1347: NULL, 0, FALSE, VM_PROT_READ|VM_PROT_WRITE, ! 1348: VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); ! 1349: if (err) ! 1350: { ! 1351: vm_map_copy_discard (copy); ! 1352: goto out; ! 1353: } ! 1354: ! 1355: /* Determine size of I/O this time around. */ ! 1356: len = size - (uaddr & PAGE_MASK); ! 1357: if (len > resid) ! 1358: len = resid; ! 1359: ! 1360: while (1) ! 1361: { ! 1362: /* Map user pages. */ ! 1363: for (i = 0; i < copy->cpy_npages; i++) ! 1364: pmap_enter (vm_map_pmap (device_io_map), ! 1365: addr + (i << PAGE_SHIFT), ! 1366: copy->cpy_page_list[i]->phys_addr, ! 1367: VM_PROT_READ|VM_PROT_WRITE, TRUE); ! 1368: ! 1369: /* Do the write. */ ! 1370: amt = (*bd->ds->fops->write) (&td.inode, &td.file, ! 1371: (char *) addr + (uaddr & PAGE_MASK), len); ! 1372: ! 1373: /* Unmap pages and deallocate copy. */ ! 1374: pmap_remove (vm_map_pmap (device_io_map), ! 1375: addr, addr + (copy->cpy_npages << PAGE_SHIFT)); ! 1376: vm_map_copy_discard (copy); ! 1377: ! 1378: /* Check result of write. */ ! 1379: if (amt > 0) ! 1380: { ! 1381: resid -= amt; ! 1382: if (resid == 0) ! 1383: break; ! 1384: uaddr += amt; ! 1385: } ! 1386: else ! 1387: { ! 1388: if (amt < 0) ! 1389: err = linux_to_mach_error (amt); ! 1390: break; ! 1391: } ! 1392: ! 1393: /* Determine size of I/O this time around and copy in pages. */ ! 1394: len = round_page (uaddr + resid) - trunc_page (uaddr); ! 1395: if (len > MAX_COPY) ! 1396: len = MAX_COPY; ! 1397: len -= uaddr & PAGE_MASK; ! 1398: if (len > resid) ! 1399: len = resid; ! 1400: err = vm_map_copyin_page_list (current_map (), uaddr, len, ! 1401: FALSE, FALSE, ©, FALSE); ! 1402: if (err) ! 1403: break; ! 1404: } ! 1405: ! 1406: /* Delete kernel buffer. */ ! 1407: vm_map_remove (device_io_map, addr, addr + size); ! 1408: ! 1409: out: ! 1410: if (--bd->iocount == 0 && bd->want) ! 1411: { ! 1412: bd->want = 0; ! 1413: thread_wakeup ((event_t) bd); ! 1414: } ! 1415: if (IP_VALID (reply_port)) ! 1416: ds_device_write_reply (reply_port, reply_port_type, err, count - resid); ! 1417: return MIG_NO_REPLY; ! 1418: } ! 1419: ! 1420: static io_return_t ! 1421: device_read (void *d, ipc_port_t reply_port, ! 1422: mach_msg_type_name_t reply_port_type, dev_mode_t mode, ! 1423: recnum_t bn, int count, io_buf_ptr_t *data, ! 1424: unsigned *bytes_read) ! 1425: { ! 1426: boolean_t dirty; ! 1427: int resid, amt; ! 1428: io_return_t err = 0; ! 1429: queue_head_t pages; ! 1430: vm_map_copy_t copy; ! 1431: vm_offset_t addr, offset, alloc_offset, o; ! 1432: vm_object_t object; ! 1433: vm_page_t m; ! 1434: vm_size_t len, size; ! 1435: struct block_data *bd = d; ! 1436: DECL_DATA; ! 1437: ! 1438: INIT_DATA (); ! 1439: ! 1440: *data = 0; ! 1441: *bytes_read = 0; ! 1442: ! 1443: if (! bd->ds->fops->read) ! 1444: return D_INVALID_OPERATION; ! 1445: count = check_limit (bd, &td.file.f_pos, bn, count); ! 1446: if (count < 0) ! 1447: return D_INVALID_SIZE; ! 1448: if (count == 0) ! 1449: return 0; ! 1450: ! 1451: /* Allocate an object to hold the data. */ ! 1452: size = round_page (count); ! 1453: object = vm_object_allocate (size); ! 1454: if (! object) ! 1455: { ! 1456: err = D_NO_MEMORY; ! 1457: goto out; ! 1458: } ! 1459: alloc_offset = offset = 0; ! 1460: resid = count; ! 1461: ! 1462: /* Allocate a kernel buffer. */ ! 1463: addr = vm_map_min (device_io_map); ! 1464: if (size > MAX_COPY) ! 1465: size = MAX_COPY; ! 1466: err = vm_map_enter (device_io_map, &addr, size, 0, TRUE, NULL, ! 1467: 0, FALSE, VM_PROT_READ|VM_PROT_WRITE, ! 1468: VM_PROT_READ|VM_PROT_WRITE, VM_INHERIT_NONE); ! 1469: if (err) ! 1470: goto out; ! 1471: ! 1472: queue_init (&pages); ! 1473: ! 1474: while (resid) ! 1475: { ! 1476: /* Determine size of I/O this time around. */ ! 1477: len = round_page (offset + resid) - trunc_page (offset); ! 1478: if (len > MAX_COPY) ! 1479: len = MAX_COPY; ! 1480: ! 1481: /* Map any pages left from previous operation. */ ! 1482: o = trunc_page (offset); ! 1483: queue_iterate (&pages, m, vm_page_t, pageq) ! 1484: { ! 1485: pmap_enter (vm_map_pmap (device_io_map), ! 1486: addr + o - trunc_page (offset), ! 1487: m->phys_addr, VM_PROT_READ|VM_PROT_WRITE, TRUE); ! 1488: o += PAGE_SIZE; ! 1489: } ! 1490: assert (o == alloc_offset); ! 1491: ! 1492: /* Allocate and map pages. */ ! 1493: while (alloc_offset < trunc_page (offset) + len) ! 1494: { ! 1495: while ((m = vm_page_grab ()) == 0) ! 1496: VM_PAGE_WAIT (0); ! 1497: assert (! m->active && ! m->inactive); ! 1498: m->busy = TRUE; ! 1499: queue_enter (&pages, m, vm_page_t, pageq); ! 1500: pmap_enter (vm_map_pmap (device_io_map), ! 1501: addr + alloc_offset - trunc_page (offset), ! 1502: m->phys_addr, VM_PROT_READ|VM_PROT_WRITE, TRUE); ! 1503: alloc_offset += PAGE_SIZE; ! 1504: } ! 1505: ! 1506: /* Do the read. */ ! 1507: amt = len - (offset & PAGE_MASK); ! 1508: if (amt > resid) ! 1509: amt = resid; ! 1510: amt = (*bd->ds->fops->read) (&td.inode, &td.file, ! 1511: (char *) addr + (offset & PAGE_MASK), amt); ! 1512: ! 1513: /* Compute number of pages to insert in object. */ ! 1514: o = trunc_page (offset); ! 1515: if (amt > 0) ! 1516: { ! 1517: dirty = TRUE; ! 1518: resid -= amt; ! 1519: if (resid == 0) ! 1520: { ! 1521: /* Zero any unused space. */ ! 1522: if (offset + amt < o + len) ! 1523: memset ((void *) (addr + offset - o + amt), ! 1524: 0, o + len - offset - amt); ! 1525: offset = o + len; ! 1526: } ! 1527: else ! 1528: offset += amt; ! 1529: } ! 1530: else ! 1531: { ! 1532: dirty = FALSE; ! 1533: offset = o + len; ! 1534: } ! 1535: ! 1536: /* Unmap pages and add them to the object. */ ! 1537: pmap_remove (vm_map_pmap (device_io_map), addr, addr + len); ! 1538: vm_object_lock (object); ! 1539: while (o < trunc_page (offset)) ! 1540: { ! 1541: m = (vm_page_t) queue_first (&pages); ! 1542: assert (! queue_end (&pages, (queue_entry_t) m)); ! 1543: queue_remove (&pages, m, vm_page_t, pageq); ! 1544: assert (m->busy); ! 1545: vm_page_lock_queues (); ! 1546: if (dirty) ! 1547: { ! 1548: PAGE_WAKEUP_DONE (m); ! 1549: m->dirty = TRUE; ! 1550: vm_page_insert (m, object, o); ! 1551: } ! 1552: else ! 1553: vm_page_free (m); ! 1554: vm_page_unlock_queues (); ! 1555: o += PAGE_SIZE; ! 1556: } ! 1557: vm_object_unlock (object); ! 1558: if (amt <= 0) ! 1559: { ! 1560: if (amt < 0) ! 1561: err = linux_to_mach_error (amt); ! 1562: break; ! 1563: } ! 1564: } ! 1565: ! 1566: /* Delete kernel buffer. */ ! 1567: vm_map_remove (device_io_map, addr, addr + size); ! 1568: ! 1569: assert (queue_empty (&pages)); ! 1570: ! 1571: out: ! 1572: if (! err) ! 1573: err = vm_map_copyin_object (object, 0, round_page (count), ©); ! 1574: if (! err) ! 1575: { ! 1576: *data = (io_buf_ptr_t) copy; ! 1577: *bytes_read = count - resid; ! 1578: } ! 1579: else ! 1580: vm_object_deallocate (object); ! 1581: if (--bd->iocount == 0 && bd->want) ! 1582: { ! 1583: bd->want = 0; ! 1584: thread_wakeup ((event_t) bd); ! 1585: } ! 1586: return err; ! 1587: } ! 1588: ! 1589: static io_return_t ! 1590: device_get_status (void *d, dev_flavor_t flavor, dev_status_t status, ! 1591: mach_msg_type_number_t *status_count) ! 1592: { ! 1593: struct block_data *bd = d; ! 1594: ! 1595: switch (flavor) ! 1596: { ! 1597: case DEV_GET_SIZE: ! 1598: if (disk_major (MAJOR (bd->dev))) ! 1599: { ! 1600: assert (bd->ds->gd); ! 1601: ! 1602: if (bd->part >= 0) ! 1603: { ! 1604: struct disklabel *lp; ! 1605: ! 1606: assert (bd->ds->labels); ! 1607: lp = bd->ds->labels[MINOR (bd->dev)]; ! 1608: assert (lp); ! 1609: (status[DEV_GET_SIZE_DEVICE_SIZE] ! 1610: = lp->d_partitions[bd->part].p_size << 9); ! 1611: } ! 1612: else ! 1613: (status[DEV_GET_SIZE_DEVICE_SIZE] ! 1614: = bd->ds->gd->part[MINOR (bd->dev)].nr_sects << 9); ! 1615: } ! 1616: else ! 1617: { ! 1618: assert (blk_size[MAJOR (bd->dev)]); ! 1619: (status[DEV_GET_SIZE_DEVICE_SIZE] ! 1620: = (blk_size[MAJOR (bd->dev)][MINOR (bd->dev)] ! 1621: << BLOCK_SIZE_BITS)); ! 1622: } ! 1623: /* It would be nice to return the block size as reported by ! 1624: the driver, but a lot of user level code assumes the sector ! 1625: size to be 512. */ ! 1626: status[DEV_GET_SIZE_RECORD_SIZE] = 512; ! 1627: /* Always return DEV_GET_SIZE_COUNT. This is what all native ! 1628: Mach drivers do, and makes it possible to detect the absence ! 1629: of the call by setting it to a different value on input. MiG ! 1630: makes sure that we will never return more integers than the ! 1631: user asked for. */ ! 1632: *status_count = DEV_GET_SIZE_COUNT; ! 1633: break; ! 1634: ! 1635: case V_GETPARMS: ! 1636: if (*status_count < (sizeof (struct disk_parms) / sizeof (int))) ! 1637: return D_INVALID_OPERATION; ! 1638: else ! 1639: { ! 1640: struct disk_parms *dp = status; ! 1641: struct hd_geometry hg; ! 1642: DECL_DATA; ! 1643: ! 1644: INIT_DATA(); ! 1645: ! 1646: if ((*bd->ds->fops->ioctl) (&td.inode, &td.file, ! 1647: HDIO_GETGEO, &hg)) ! 1648: return D_INVALID_OPERATION; ! 1649: ! 1650: dp->dp_type = DPT_WINI; /* XXX: It may be a floppy... */ ! 1651: dp->dp_heads = hg.heads; ! 1652: dp->dp_cyls = hg.cylinders; ! 1653: dp->dp_sectors = hg.sectors; ! 1654: dp->dp_dosheads = hg.heads; ! 1655: dp->dp_doscyls = hg.cylinders; ! 1656: dp->dp_dossectors = hg.sectors; ! 1657: dp->dp_secsiz = 512; /* XXX */ ! 1658: dp->dp_ptag = 0; ! 1659: dp->dp_pflag = 0; ! 1660: ! 1661: /* XXX */ ! 1662: dp->dp_pstartsec = -1; ! 1663: dp->dp_pnumsec = -1; ! 1664: ! 1665: *status_count = sizeof (struct disk_parms) / sizeof (int); ! 1666: } ! 1667: ! 1668: break; ! 1669: ! 1670: default: ! 1671: return D_INVALID_OPERATION; ! 1672: } ! 1673: ! 1674: return D_SUCCESS; ! 1675: } ! 1676: ! 1677: struct device_emulation_ops linux_block_emulation_ops = ! 1678: { ! 1679: NULL, ! 1680: NULL, ! 1681: dev_to_port, ! 1682: device_open, ! 1683: device_close, ! 1684: device_write, ! 1685: NULL, ! 1686: device_read, ! 1687: NULL, ! 1688: NULL, ! 1689: device_get_status, ! 1690: NULL, ! 1691: NULL, ! 1692: NULL, ! 1693: NULL, ! 1694: NULL ! 1695: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.