1 /*        $NetBSD: sdcd.c,v 1.19 2024/01/07 07:58:34 isaki Exp $      */
2 
3 /*
4  * Copyright (c) 2001 MINOURA Makoto.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/bitops.h>
30 #include <sys/disklabel.h>
31 #include <lib/libkern/libkern.h>
32 #include <lib/libsa/stand.h>
33 
34 #include "libx68k.h"
35 #include "sdcdvar.h"
36 #include "iocs.h"
37 
38 
39 static int current_id = -1;
40 static int current_blkbytes;
41 static int current_blkshift;
42 static int current_devsize, current_npart;
43 static struct boot_partinfo partitions[MAXPARTITIONS];
44 
45 static uint human2blk(uint);
46 static uint human2bsd(uint);
47 static uint bsd2blk(uint);
48 static int readdisklabel(int);
49 static int check_unit(int);
50 
51 #ifdef DEBUG
52 #define DPRINTF(x)  printf x
53 #else
54 #define DPRINTF(x)
55 #endif
56 
57 /*
58  * Convert the number of sectors on Human68k
59  * into the number of blocks on the current device.
60  */
61 static uint
human2blk(uint n)62 human2blk(uint n)
63 {
64           uint blk_per_sect;
65 
66           /* Human68k uses 1024 byte/sector. */
67           blk_per_sect = 4 >> current_blkshift;
68           if (blk_per_sect == 0)
69                     blk_per_sect = 1;
70           return blk_per_sect * n;
71 }
72 
73 /*
74  * Convert the number of sectors on Human68k
75  * into the number of DEV_BSIZE sectors.
76  */
77 static uint
human2bsd(uint n)78 human2bsd(uint n)
79 {
80 
81           return n * (1024 / DEV_BSIZE);
82 }
83 
84 /*
85  * Convert the number of DEV_BSIZE sectors
86  * into the number of blocks on the current device.
87  */
88 static uint
bsd2blk(uint n)89 bsd2blk(uint n)
90 {
91 
92           return ((DEV_BSIZE / 256) * n) >> current_blkshift;
93 }
94 
95 static int
check_unit(int id)96 check_unit(int id)
97 {
98 #define BUFFER_SIZE 8192
99           int error;
100           void *buffer = alloca(BUFFER_SIZE);
101 
102           if (current_id == id)
103                     return 0;
104 
105           current_id = -1;
106 
107           error = IOCS_S_TESTUNIT(id);
108           if (error < 0) {                        /* not ready */
109                     error = ENXIO;
110                     goto out;
111           }
112 
113           {
114                     struct iocs_inquiry *inqdata = buffer;
115 
116                     error = IOCS_S_INQUIRY(sizeof(*inqdata), id, inqdata);
117                     if (error < 0) {              /* WHY??? */
118                               error = ENXIO;
119                               goto out;
120                     }
121                     if ((inqdata->unit != 0) &&   /* direct */
122                         (inqdata->unit != 5) &&   /* cdrom */
123                         (inqdata->unit != 7)) {   /* optical */
124                               error = EUNIT;
125                               goto out;
126                     }
127           }
128 
129           {
130                     struct iocs_readcap *rcdata = buffer;
131 
132                     error = IOCS_S_READCAP(id, rcdata);
133                     if (error < 0) {              /* WHY??? */
134                               error = EUNIT;
135                               goto out;
136                     }
137                     current_blkbytes = rcdata->size;
138                     current_blkshift = fls32(current_blkbytes) - 9;
139                     current_devsize = rcdata->block;
140           }
141 
142           {
143                     error = IOCS_S_READ(0, 1, id, current_blkshift, buffer);
144                     if (error < 0) {
145                               error =  EIO;
146                               goto out;
147                     }
148                     if (strncmp((char *)buffer, "X68SCSI1", 8) != 0) {
149                               error = EUNLAB;
150                               goto out;
151                     }
152           }
153 
154  out:
155           return error;
156 }
157 
158 static int
readdisklabel(int id)159 readdisklabel(int id)
160 {
161           int error, i;
162           char *buffer;
163           struct disklabel *label;
164           struct dos_partition *parttbl;
165 
166           if (current_id == id)
167                     return 0;
168           current_id = -1;
169 
170           error = check_unit(id);
171           if (error)
172                     return error;
173           if (current_blkbytes > 2048) {
174                     printf("FATAL: Unsupported block size %d.\n",
175                         current_blkbytes);
176                     return ERDLAB;
177           }
178 
179           /* Try BSD disklabel first */
180           buffer = alloca(2048);
181           error = IOCS_S_READ(LABELSECTOR, 1, id, current_blkshift, buffer);
182           if (error < 0)
183                     return EIO;
184           label = (void *)(buffer + LABELOFFSET);
185           if (label->d_magic == DISKMAGIC &&
186               label->d_magic2 == DISKMAGIC) {
187                     for (i = 0; i < label->d_npartitions; i++) {
188                               partitions[i].start = label->d_partitions[i].p_offset;
189                               partitions[i].size  = label->d_partitions[i].p_size;
190                     }
191                     current_npart = label->d_npartitions;
192 
193                     goto done;
194           }
195 
196           /* Try Human68K-style partition table */
197           error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer);
198           if (error < 0)
199                     return EIO;
200           parttbl = (void *)(buffer + DOSBBSECTOR);
201           if (strncmp(buffer, "X68K", 4) != 0)
202                     return EUNLAB;
203           parttbl++;
204           for (current_npart = 0, i = 0;
205                current_npart < MAXPARTITIONS && i < 15 && parttbl[i].dp_size;
206                i++) {
207                     partitions[current_npart].start
208                               = human2bsd(parttbl[i].dp_start);
209                     partitions[current_npart].size
210                               = human2bsd(parttbl[i].dp_size);
211                     if (++current_npart == RAW_PART) {
212                               partitions[current_npart].start = 0;
213                               partitions[current_npart].size = -1; /* XXX */
214                               current_npart++;
215                     }
216           }
217 done:
218 #ifdef DEBUG
219           for (i = 0; i < current_npart; i++) {
220                     printf ("%d: starts %d, size %d\n", i,
221                               partitions[i].start,
222                               partitions[i].size);
223           }
224 #endif
225           current_id = id;
226 
227           return 0;
228 }
229 
230 int
sd_getbsdpartition(int id,int humanpart)231 sd_getbsdpartition(int id, int humanpart)
232 {
233           int error, i;
234           char *buffer;
235           struct dos_partition *parttbl;
236           unsigned parttop;
237 
238           if (humanpart < 2)
239                     humanpart++;
240 
241           error = readdisklabel(id);
242           if (error) {
243                     printf("Reading disklabel: %s\n", strerror(error));
244                     return -1;
245           }
246           buffer = alloca(2048);
247           error = IOCS_S_READ(human2blk(2), 1, id, current_blkshift, buffer);
248           if (error < 0) {
249                     printf("Reading partition table: %s\n", strerror(error));
250                     return -1;
251           }
252           parttbl = (void *)(buffer + DOSBBSECTOR);
253           if (strncmp(buffer, "X68K", 4) != 0)
254                     return 0;
255           parttop = human2bsd(parttbl[humanpart].dp_start);
256 
257           for (i = 0; i < current_npart; i++) {
258                     if (partitions[i].start == parttop)
259                               return i;
260           }
261 
262           printf("Could not determine the boot partition.\n");
263 
264           return -1;
265 }
266 
267 struct sdcd_softc {
268           int                           sc_part;
269           struct boot_partinfo          sc_partinfo;
270 };
271 
272 /* sdopen(struct open_file *f, int id, int part) */
273 int
sdopen(struct open_file * f,...)274 sdopen(struct open_file *f, ...)
275 {
276           int error;
277           struct sdcd_softc *sc;
278           int id, part;
279           va_list ap;
280 
281           va_start(ap, f);
282           id   = va_arg(ap, int);
283           part = va_arg(ap, int);
284           va_end(ap);
285 
286           if (id < 0 || id > 7)
287                     return ENXIO;
288           if (current_id != id) {
289                     error = readdisklabel(id);
290                     if (error)
291                               return error;
292           }
293           if (part >= current_npart)
294                     return ENXIO;
295 
296           sc = alloc(sizeof(struct sdcd_softc));
297           sc->sc_part = part;
298           sc->sc_partinfo = partitions[part];
299           f->f_devdata = sc;
300           return 0;
301 }
302 
303 int
sdcdclose(struct open_file * f)304 sdcdclose(struct open_file *f)
305 {
306 
307           dealloc(f->f_devdata, sizeof(struct sdcd_softc));
308           return 0;
309 }
310 
311 int
sdcdstrategy(void * arg,int rw,daddr_t dblk,size_t size,void * buf,size_t * rsize)312 sdcdstrategy(void *arg, int rw, daddr_t dblk, size_t size,
313            void *buf, size_t *rsize)
314 {
315           struct sdcd_softc *sc = arg;
316           uint32_t  start = sc->sc_partinfo.start + dblk;
317           size_t              nblks;
318           int                 error;
319 
320           if (size == 0) {
321                     if (rsize)
322                               *rsize = 0;
323                     return 0;
324           }
325           start = bsd2blk(start);
326           nblks = howmany(size, current_blkbytes);
327 
328           if (start < 0x200000 && nblks < 256) {
329                     if (rw & F_WRITE)
330                               error = IOCS_S_WRITE(start, nblks, current_id,
331                                                    current_blkshift, buf);
332                     else
333                               error = IOCS_S_READ(start, nblks, current_id,
334                                                   current_blkshift, buf);
335           } else {
336                     if (rw & F_WRITE)
337                               error = IOCS_S_WRITEEXT(start, nblks, current_id,
338                                                       current_blkshift, buf);
339                     else
340                               error = IOCS_S_READEXT(start, nblks, current_id,
341                                                      current_blkshift, buf);
342           }
343           if (error < 0)
344                     return EIO;
345 
346           if (rsize)
347                     *rsize = size;
348           return 0;
349 }
350 
351 /* cdopen(struct open_file *f, int id, int part) */
352 int
cdopen(struct open_file * f,...)353 cdopen(struct open_file *f, ...)
354 {
355           int error;
356           struct sdcd_softc *sc;
357           int id, part;
358           va_list ap;
359 
360           va_start(ap, f);
361           id   = va_arg(ap, int);
362           part = va_arg(ap, int);
363           va_end(ap);
364 
365           if (id < 0 || id > 7)
366                     return ENXIO;
367           if (part != 0 && part != 2)
368                     return ENXIO;
369           if (current_id != id) {
370                     error = check_unit(id);
371                     if (error)
372                               return error;
373           }
374 
375           sc = alloc(sizeof(struct sdcd_softc));
376           current_npart = 3;
377           sc->sc_part = 0;
378           sc->sc_partinfo.start = 0;
379           sc->sc_partinfo.size = current_devsize;
380           f->f_devdata = sc;
381           current_id = id;
382 
383           return 0;
384 }
385