Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / boot / alpha / libalpha / srmdisk.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/alpha/libalpha/srmdisk.c,v 1.8.2.2 2001/08/01 20:52:03 mjacob Exp $
28  */
29
30 /*
31  * SRM 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 #include <machine/prom.h>
47
48 #include "bootstrap.h"
49 #include "libalpha.h"
50
51 #define SRMDISK_SECSIZE 512
52
53 #define BUFSIZE         (1 * SRMDISK_SECSIZE)
54 #define MAXBDDEV        MAXDEV
55
56 #ifdef DISK_DEBUG
57 # define D(x)   x
58 #else
59 # define D(x)
60 #endif
61
62 static int      bd_init(void);
63 static int      bd_strategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize);
64 static int      bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize);
65 static int      bd_open(struct open_file *f, ...);
66 static int      bd_close(struct open_file *f);
67 static void     bd_print(int verbose);
68
69 struct open_disk {
70     int                 od_fd;
71     int                 od_unit;                /* our unit number */
72     int                 od_boff;                /* block offset from beginning of SRM disk */
73     int                 od_flags;
74 #define BD_FLOPPY       (1<<2)
75     u_char              od_buf[BUFSIZE];        /* transfer buffer (do we want/need this?) */
76 };
77
78 struct devsw srmdisk = {
79     "disk", 
80     DEVT_DISK, 
81     bd_init,
82     bd_strategy, 
83     bd_open, 
84     bd_close, 
85     noioctl,
86     bd_print
87 };
88
89 /*
90  * List of SRM devices, translation from disk unit number to
91  * SRM unit number.
92  */
93 static struct 
94 {
95     char        bd_name[64];
96     int         bd_unit;                /* SRM unit number */
97     int         bd_namelen;
98     int         bd_flags;
99     int         bd_fd;
100     int         bd_opencount;
101 } bdinfo [MAXBDDEV];
102 static int nbdinfo = 0;
103
104 /*    
105  * Quiz SRM for disk devices, save a little info about them.
106  */
107 static int
108 bd_init(void) 
109 {
110     prom_return_t ret;
111     char devname[64];
112
113     bdinfo[0].bd_unit = 0;      /* XXX */
114     bdinfo[0].bd_flags = 0;     /* XXX */
115     ret.bits = prom_getenv(PROM_E_BOOTED_DEV,
116                            bdinfo[0].bd_name, sizeof(bdinfo[0].bd_name));
117     bdinfo[0].bd_namelen = ret.u.retval;
118     bdinfo[0].bd_fd = -1;
119     bdinfo[0].bd_opencount = 0;
120     nbdinfo++;
121
122     return (0);
123 }
124
125 /*
126  * Print information about disks
127  */
128 static void
129 bd_print(int verbose)
130 {
131     int         i;
132     char        line[80];
133     
134     for (i = 0; i < nbdinfo; i++) {
135         sprintf(line, "    disk%d:   SRM drive %s", i, bdinfo[i].bd_name);
136         pager_output(line);
137         /* XXX more detail? */
138         pager_output("\n");
139     }
140 }
141
142 /*
143  * Attempt to open the disk described by (dev) for use by (f).
144  *
145  * Note that the philosophy here is "give them exactly what
146  * they ask for".  This is necessary because being too "smart"
147  * about what the user might want leads to complications.
148  * (eg. given no slice or partition value, with a disk that is
149  *  sliced - are they after the first BSD slice, or the DOS
150  *  slice before it?)
151  */
152 static int 
153 bd_open(struct open_file *f, ...)
154 {
155     va_list                     args;
156     struct alpha_devdesc        *dev;
157     struct dos_partition        *dptr;
158     struct open_disk            *od;
159     struct disklabel            *lp;
160     int                         sector, slice, i;
161     int                         error;
162     int                         unit, fd;
163     prom_return_t               ret;
164
165     va_start(args, f);
166     dev = va_arg(args, struct alpha_devdesc*);
167     va_end(args);
168
169     unit = dev->d_kind.srmdisk.unit;
170     if (unit >= nbdinfo) {
171         D(printf("attempt to open nonexistent disk\n"));
172         return(ENXIO);
173     }
174     
175     /* Call the prom to open the disk. */
176     if (bdinfo[unit].bd_fd < 0) {
177         ret.bits = prom_open(bdinfo[unit].bd_name, bdinfo[unit].bd_namelen);
178         if (ret.u.status == 2)
179             return (ENXIO);
180         if (ret.u.status == 3)
181             return (EIO);
182         bdinfo[unit].bd_fd = fd = ret.u.retval;
183     } else {
184         fd = bdinfo[unit].bd_fd;
185     }
186     bdinfo[unit].bd_opencount++;
187
188     od = (struct open_disk *) malloc(sizeof(struct open_disk));
189     if (!od) {
190         D(printf("srmdiskopen: no memory\n"));
191         return (ENOMEM);
192     }
193
194     /* Look up SRM unit number, intialise open_disk structure */
195     od->od_fd = fd;
196     od->od_unit = dev->d_kind.srmdisk.unit;
197     od->od_flags = bdinfo[od->od_unit].bd_flags;
198     od->od_boff = 0;
199     error = 0;
200
201 #if 0
202     /* Get geometry for this open (removable device may have changed) */
203     if (set_geometry(&od->od_ll)) {
204         D(printf("bd_open: can't get geometry\n"));
205         error = ENXIO;
206         goto out;
207     }
208 #endif
209
210     /*
211      * Following calculations attempt to determine the correct value
212      * for d->od_boff by looking for the slice and partition specified,
213      * or searching for reasonable defaults.
214      */
215
216 #if 0
217     /*
218      * Find the slice in the DOS slice table.
219      */
220     if (readsects(&od->od_ll, 0, 1, od->od_buf, 0)) {
221         D(printf("bd_open: error reading MBR\n"));
222         error = EIO;
223         goto out;
224     }
225
226     /* 
227      * Check the slice table magic.
228      */
229     if ((od->od_buf[0x1fe] != 0xff) || (od->od_buf[0x1ff] != 0xaa)) {
230         /* If a slice number was explicitly supplied, this is an error */
231         if (dev->d_kind.srmdisk.slice > 0) {
232             D(printf("bd_open: no slice table/MBR (no magic)\n"));
233             error = ENOENT;
234             goto out;
235         }
236         sector = 0;
237         goto unsliced;          /* may be a floppy */
238     }
239     dptr = (struct dos_partition *) & od->od_buf[DOSPARTOFF];
240
241     /* 
242      * XXX No support here for 'extended' slices
243      */
244     if (dev->d_kind.srmdisk.slice <= 0) {
245         /*
246          * Search for the first FreeBSD slice; this also works on "unsliced"
247          * disks, as they contain a "historically bogus" MBR.
248          */
249         for (i = 0; i < NDOSPART; i++, dptr++)
250             if (dptr->dp_typ == DOSPTYP_386BSD) {
251                 sector = dptr->dp_start;
252                 break;
253             }
254         /* Did we find something? */
255         if (sector == -1) {
256             error = ENOENT;
257             goto out;
258         }
259     } else {
260         /*
261          * Accept the supplied slice number unequivocally (we may be looking
262          * for a DOS partition) if we can handle it.
263          */
264         if ((dev->d_kind.srmdisk.slice > NDOSPART) || (dev->d_kind.srmdisk.slice < 1)) {
265             error = ENOENT;
266             goto out;
267         }
268         dptr += (dev->d_kind.srmdisk.slice - 1);
269         sector = dptr->dp_start;
270     }
271  unsliced:
272
273 #else
274     sector = 0;
275 #endif
276     /* 
277      * Now we have the slice, look for the partition in the disklabel if we have
278      * a partition to start with.
279      */
280     if (dev->d_kind.srmdisk.partition < 0) {
281         od->od_boff = sector;           /* no partition, must be after the slice */
282     } else {
283         if (bd_strategy(od, F_READ, sector + LABELSECTOR, 512, od->od_buf, 0)) {
284             D(printf("bd_open: error reading disklabel\n"));
285             error = EIO;
286             goto out;
287         }
288         lp = (struct disklabel *) (od->od_buf + LABELOFFSET);
289         if (lp->d_magic != DISKMAGIC) {
290             D(printf("bd_open: no disklabel\n"));
291 #if 0
292             error = ENOENT;
293             goto out;
294 #endif
295         } else if (dev->d_kind.srmdisk.partition >= lp->d_npartitions) {
296
297             /*
298              * The partition supplied is out of bounds; this is fatal.
299              */
300             D(printf("partition '%c' exceeds partitions in table (a-'%c')\n",
301                      'a' + dev->d_kind.srmdisk.partition, 'a' + lp->d_npartitions));
302             error = EPART;
303             goto out;
304
305         } else {
306
307             /*
308              * Complain if the partition type is wrong and it shouldn't be, but
309              * regardless accept this partition.
310              */
311             D(if ((lp->d_partitions[dev->d_kind.srmdisk.partition].p_fstype == FS_UNUSED) &&
312                   !(od->od_flags & BD_FLOPPY))      /* Floppies often have bogus fstype */
313               printf("bd_open: warning, partition marked as unused\n"););
314
315             od->od_boff = lp->d_partitions[dev->d_kind.srmdisk.partition].p_offset;
316         }
317     }
318     /*
319      * Save our context
320      */
321     f->f_devdata = od;
322
323  out:
324     if (error)
325         free(od);
326     return(error);
327 }
328
329 static int 
330 bd_close(struct open_file *f)
331 {
332     struct open_disk    *od = f->f_devdata;
333
334     bdinfo[od->od_unit].bd_opencount--;
335     if (bdinfo[od->od_unit].bd_opencount == 0) {
336         (void)prom_close(od->od_fd);
337         bdinfo[od->od_unit].bd_fd = -1;
338     }
339
340     free(od);
341     f->f_devdata = NULL;
342     return(0);
343 }
344
345 static int 
346 bd_strategy(void *devdata, int rw, daddr_t dblk, size_t size, void *buf, size_t *rsize)
347 {
348     struct bcache_devdata       bcd;
349     struct open_disk    *od = (struct open_disk *)devdata;
350     
351     bcd.dv_strategy = bd_realstrategy;
352     bcd.dv_devdata = devdata;
353     return(bcache_strategy(&bcd, od->od_unit, rw, dblk + od->od_boff, size, buf, rsize));
354 }
355
356 static int 
357 bd_realstrategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize)
358 {
359     prom_return_t       ret;
360     struct open_disk    *od = (struct open_disk *)devdata;
361
362     if (size % SRMDISK_SECSIZE)
363         panic("bd_strategy: I/O not block multiple");
364
365     if (flag != F_READ)
366         return(EROFS);
367
368     if (rsize)
369         *rsize = 0;
370
371     ret.bits = prom_read(od->od_fd, size, buf, dblk);
372     if (ret.u.status) {
373         D(printf("read error\n"));
374         return (EIO);
375     }
376
377     if (rsize)
378         *rsize = size;
379     return (0);
380 }
381