Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / boot / arc / lib / arcdisk.c
1 /*-
2  * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
3  * Copyright (c) 1998 Doug Rabson <dfr@freebsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/boot/arc/lib/arcdisk.c,v 1.3.2.1 2001/01/05 17:37:51 mjacob Exp $
28  */
29
30 /*
31  * ARC disk device handling.
32  * 
33  * Ideas and algorithms from:
34  *
35  * - NetBSD libi386/biosdisk.c
36  * - FreeBSD biosboot/disk.c
37  *
38  */
39
40 #include <stand.h>
41
42 #include <sys/disklabel.h>
43 #include <sys/diskslice.h>
44
45 #include <machine/stdarg.h>
46
47 #include "bootstrap.h"
48 #include "libarc.h"
49 #include "arctypes.h"
50 #include "arcfuncs.h"
51
52 #define ARCDISK_SECSIZE 512
53
54 #define BUFSIZE         (1 * ARCDISK_SECSIZE)
55 #define MAXBDDEV        MAXDEV
56
57 #ifdef DISK_DEBUG
58 # define D(x)   x
59 #else
60 # define D(x)
61 #endif
62
63 static int      bd_init(void);
64 static int      bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize);
65 static int      bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize);
66 static int      bd_open(struct open_file *f, ...);
67 static int      bd_close(struct open_file *f);
68 static void     bd_print(int verbose);
69
70 struct open_disk {
71     int                 od_fd;
72     int                 od_unit;                /* our unit number */
73     int                 od_boff;                /* block offset from beginning of ARC disk */
74     int                 od_flags;
75 #define BD_FLOPPY       (1<<2)
76     u_char              od_buf[BUFSIZE];        /* transfer buffer (do we want/need this?) */
77 };
78
79 struct devsw arcdisk = {
80     "disk", 
81     DEVT_DISK, 
82     bd_init,
83     bd_strategy, 
84     bd_open, 
85     bd_close, 
86     noioctl,
87     bd_print
88 };
89
90 /*
91  * List of ARC devices, translation from disk unit number to
92  * ARC unit number.
93  */
94 static struct 
95 {
96     char        bd_name[64];
97     int         bd_unit;                /* ARC unit number */
98     int         bd_namelen;
99     int         bd_flags;
100 } bdinfo [MAXBDDEV];
101 static int nbdinfo = 0;
102
103 /*    
104  * Quiz ARC for disk devices, save a little info about them.
105  */
106 static int
107 bd_init(void) 
108 {
109     nbdinfo++;
110
111     return (0);
112 }
113
114 /*
115  * Print information about disks
116  */
117 static void
118 bd_print(int verbose)
119 {
120     int         i;
121     char        line[80];
122     
123     for (i = 0; i < nbdinfo; i++) {
124         sprintf(line, "    disk%d:   ARC drive %s", i, bdinfo[i].bd_name);
125         pager_output(line);
126         /* XXX more detail? */
127         pager_output("\n");
128     }
129 }
130
131 /*
132  * Attempt to open the disk described by (dev) for use by (f).
133  *
134  * Note that the philosophy here is "give them exactly what
135  * they ask for".  This is necessary because being too "smart"
136  * about what the user might want leads to complications.
137  * (eg. given no slice or partition value, with a disk that is
138  *  sliced - are they after the first BSD slice, or the DOS
139  *  slice before it?)
140  */
141 static int 
142 bd_open(struct open_file *f, ...)
143 {
144     struct arc_devdesc          *dev;
145     struct dos_partition        *dptr;
146     struct open_disk            *od;
147     struct disklabel            *lp;
148     int                         sector, slice, i;
149     int                         error;
150     int                         unit;
151     u_int32_t                   fd;
152     va_list                     ap;
153
154     va_start(ap, f);
155     dev = va_arg(ap, struct arc_devdesc *);
156     va_end(ap);
157
158     unit = dev->d_kind.arcdisk.unit;
159     if (unit >= nbdinfo) {
160         D(printf("attempt to open nonexistent disk\n"));
161         return(ENXIO);
162     }
163     
164     if (Open("scsi(0)disk(0)rdisk(0)partition(0)",
165              OpenReadOnly, &fd) != ESUCCESS)
166       if (Open("scsi(0)disk(1)rdisk(0)partition(0)",
167                OpenReadOnly, &fd) != ESUCCESS)
168         if (Open("multi(0)disk(0)fdisk(0)partition(0)",
169                  OpenReadOnly, &fd) != ESUCCESS)
170         return(ENXIO);
171
172     od = (struct open_disk *) malloc(sizeof(struct open_disk));
173     if (!od) {
174         D(printf("arcdiskopen: no memory\n"));
175         return (ENOMEM);
176     }
177
178     /* Look up ARC unit number, intialise open_disk structure */
179     od->od_fd = fd;
180     od->od_unit = dev->d_kind.arcdisk.unit;
181     od->od_flags = bdinfo[od->od_unit].bd_flags;
182     od->od_boff = 0;
183     error = 0;
184
185 #if 0
186     /* Get geometry for this open (removable device may have changed) */
187     if (set_geometry(&od->od_ll)) {
188         D(printf("bd_open: can't get geometry\n"));
189         error = ENXIO;
190         goto out;
191     }
192 #endif
193
194     /*
195      * Following calculations attempt to determine the correct value
196      * for d->od_boff by looking for the slice and partition specified,
197      * or searching for reasonable defaults.
198      */
199
200 #if 0
201     /*
202      * Find the slice in the DOS slice table.
203      */
204     if (readsects(&od->od_ll, 0, 1, od->od_buf, 0)) {
205         D(printf("bd_open: error reading MBR\n"));
206         error = EIO;
207         goto out;
208     }
209
210     /* 
211      * Check the slice table magic.
212      */
213     if ((od->od_buf[0x1fe] != 0xff) || (od->od_buf[0x1ff] != 0xaa)) {
214         /* If a slice number was explicitly supplied, this is an error */
215         if (dev->d_kind.arcdisk.slice > 0) {
216             D(printf("bd_open: no slice table/MBR (no magic)\n"));
217             error = ENOENT;
218             goto out;
219         }
220         sector = 0;
221         goto unsliced;          /* may be a floppy */
222     }
223     dptr = (struct dos_partition *) & od->od_buf[DOSPARTOFF];
224
225     /* 
226      * XXX No support here for 'extended' slices
227      */
228     if (dev->d_kind.arcdisk.slice <= 0) {
229         /*
230          * Search for the first FreeBSD slice; this also works on "unsliced"
231          * disks, as they contain a "historically bogus" MBR.
232          */
233         for (i = 0; i < NDOSPART; i++, dptr++)
234             if (dptr->dp_typ == DOSPTYP_386BSD) {
235                 sector = dptr->dp_start;
236                 break;
237             }
238         /* Did we find something? */
239         if (sector == -1) {
240             error = ENOENT;
241             goto out;
242         }
243     } else {
244         /*
245          * Accept the supplied slice number unequivocally (we may be looking
246          * for a DOS partition) if we can handle it.
247          */
248         if ((dev->d_kind.arcdisk.slice > NDOSPART) || (dev->d_kind.arcdisk.slice < 1)) {
249             error = ENOENT;
250             goto out;
251         }
252         dptr += (dev->d_kind.arcdisk.slice - 1);
253         sector = dptr->dp_start;
254     }
255  unsliced:
256
257 #else
258     sector = 0;
259 #endif
260     /* 
261      * Now we have the slice, look for the partition in the disklabel if we have
262      * a partition to start with.
263      */
264     if (dev->d_kind.arcdisk.partition < 0) {
265         od->od_boff = sector;           /* no partition, must be after the slice */
266     } else {
267         if (bd_strategy(od, F_READ, sector + LABELSECTOR, 512, od->od_buf, 0)) {
268             D(printf("bd_open: error reading disklabel\n"));
269             error = EIO;
270             goto out;
271         }
272         lp = (struct disklabel *) (od->od_buf + LABELOFFSET);
273         if (lp->d_magic != DISKMAGIC) {
274             D(printf("bd_open: no disklabel\n"));
275             error = ENOENT;
276             goto out;
277
278         } else if (dev->d_kind.arcdisk.partition >= lp->d_npartitions) {
279
280             /*
281              * The partition supplied is out of bounds; this is fatal.
282              */
283             D(printf("partition '%c' exceeds partitions in table (a-'%c')\n",
284                      'a' + dev->d_kind.arcdisk.partition, 'a' + lp->d_npartitions));
285             error = EPART;
286             goto out;
287
288         } else {
289
290             /*
291              * Complain if the partition type is wrong and it shouldn't be, but
292              * regardless accept this partition.
293              */
294             D(if ((lp->d_partitions[dev->d_kind.arcdisk.partition].p_fstype == FS_UNUSED) &&
295                   !(od->od_flags & BD_FLOPPY))      /* Floppies often have bogus fstype */
296               printf("bd_open: warning, partition marked as unused\n"););
297
298             od->od_boff = lp->d_partitions[dev->d_kind.arcdisk.partition].p_offset;
299         }
300     }
301     /*
302      * Save our context
303      */
304     f->f_devdata = od;
305
306  out:
307     if (error)
308         free(od);
309     return(error);
310 }
311
312 static int 
313 bd_close(struct open_file *f)
314 {
315     struct open_disk    *od = f->f_devdata;
316
317     Close(od->od_fd);
318
319     free(od);
320     f->f_devdata = NULL;
321     return(0);
322 }
323
324 static int 
325 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, char *buf, size_t *rsize)
326 {
327     struct bcache_devdata       bcd;
328     struct arc_devdesc  *dev = (struct arc_devdesc *)devdata;
329     
330     bcd.dv_strategy = bd_realstrategy;
331     bcd.dv_devdata = devdata;
332     return(bcache_strategy(&bcd, dev->d_kind.arcdisk.unit, rw, dblk, size,
333                            buf, rsize));
334 }
335
336 static int 
337 bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, char *buf, size_t *rsize)
338 {
339     struct open_disk    *od = (struct open_disk *)devdata;
340     fpos_t              seek;
341     u_int32_t           count;
342
343     if (size % ARCDISK_SECSIZE)
344         panic("bd_strategy: I/O not block multiple");
345
346     if (flag != F_READ)
347         return(EROFS);
348
349     if (rsize)
350         *rsize = 0;
351
352     seek = 512 * (dblk + od->od_boff);
353     Seek(od->od_fd, &seek, SeekAbsolute);
354     if (Read(od->od_fd, buf, size, &count) != ESUCCESS) {
355         D(printf("read error\n"));
356         return (EIO);
357     }
358
359     if (rsize)
360         *rsize = count;
361     return (0);
362 }
363