4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Basic file system reading code for standalone I/O system.
29 * Simulates a primitive UNIX I/O system (read(), write(), open(), etc).
30 * Does not support writes.
35 * This was used by installgrub for creating bootable floppy.
36 * The special part is diskread_callback/fileread_callback for gathering
40 #include <sys/param.h>
41 #include <sys/sysmacros.h>
42 #include <sys/vnode.h>
43 #include <sys/fs/pc_label.h>
44 #include <sys/bootvfs.h>
45 #include <sys/filep.h>
49 #include "../common/util.h"
50 #elif defined(_KERNEL)
51 #include <sys/sunddi.h>
59 #define dprintf if (bootrd_debug) printf
60 #elif defined(_KERNEL)
61 #define printf kobj_printf
62 #define dprintf if (bootrd_debug) kobj_printf
65 extern void kobj_printf(char *, ...);
67 #define dprintf if (bootrd_debug) printf
70 #define FI_STARTCLUST(fp) (*(ushort_t *)(fp)->fi_buf)
71 #define FI_LENGTH(fp) (*(long *)((fp)->fi_buf + 4))
73 extern int bootrd_debug
;
74 extern void *bkmem_alloc(size_t);
75 extern void bkmem_free(void *, size_t);
78 * NOTE: The fileread_callback is set by the calling program
79 * during a file read. diskread_callback is set to fileread_callback
80 * only if reading a file block. It needs to be NULL while reading
83 extern int (*diskread_callback
)(int, int);
84 extern int (*fileread_callback
)(int, int);
89 static int lookuppn(char *, _dir_entry_p
);
90 static fileid_t
*find_fp(int);
91 static void *readblock(int, int);
92 static int fat_map(int, int);
93 static int cluster_valid(long, int);
94 static int fat_ctodb(int, int);
96 static int bpcfs_mountroot(char *str
);
97 static int bpcfs_unmountroot(void);
98 static int bpcfs_open(char *str
, int flags
);
99 static int bpcfs_close(int fd
);
100 static void bpcfs_closeall(void);
101 static ssize_t
bpcfs_read(int fdesc
, char *buf
, size_t count
);
102 static off_t
bpcfs_lseek(int fdesc
, off_t addr
, int whence
);
104 static fileid_t
*head
;
105 static _fat_controller_p pcfsp
;
107 /* cache the cluster */
108 static int nsec_cache
;
109 static int nsec_start
;
110 static char *cluster_cache
;
114 bpcfs_mountroot(char *str
)
118 return (0); /* already mounted */
120 pcfsp
= bkmem_alloc(sizeof (_fat_controller_t
));
121 head
= (fileid_t
*)bkmem_alloc(sizeof (fileid_t
));
122 head
->fi_back
= head
->fi_forw
= head
;
123 head
->fi_filedes
= 0;
126 /* read of first floppy sector */
127 head
->fi_blocknum
= 0;
128 head
->fi_count
= SECSIZ
;
129 head
->fi_memp
= (caddr_t
)pcfsp
->f_sector
;
130 if (diskread(head
)) {
131 printf("failed to read first sector\n");
132 bkmem_free(pcfsp
, sizeof (*pcfsp
));
137 if (pcfsp
->f_bpb
.bs_spc
== 0) {
138 printf("invalid bios paramet block\n");
143 (pcfsp
->f_bpb
.bs_num_fats
* ltohs(pcfsp
->f_bpb
.bs_spf
)) +
144 ltohs(pcfsp
->f_bpb
.bs_resv_sectors
);
146 ltohs(pcfsp
->f_bpb
.bs_num_root_entries
) *
147 sizeof (_dir_entry_t
) / SECSIZ
;
149 pcfsp
->f_dclust
= CLUSTER_ROOTDIR
;
150 pcfsp
->f_filesec
= pcfsp
->f_rootsec
+ pcfsp
->f_rootlen
;
151 pcfsp
->f_nxtfree
= CLUSTER_FIRST
;
153 /* figure out the number of clusters in this partition */
154 ncluster
= (((ulong_t
)ltohs(pcfsp
->f_bpb
.bs_siv
) ?
155 (ulong_t
)ltohs(pcfsp
->f_bpb
.bs_siv
) :
156 (ulong_t
)ltohi(pcfsp
->f_bpb
.bs_siv
)) -
157 pcfsp
->f_filesec
) / (ulong_t
)pcfsp
->f_bpb
.bs_spc
;
158 pcfsp
->f_16bit
= ncluster
>= CLUSTER_MAX_12
;
159 pcfsp
->f_ncluster
= ncluster
;
161 /* cache the cluster */
163 nsec_cache
= (((ncluster
<< 1) + 511) >> 9);
165 nsec_cache
= (ncluster
+ ((ncluster
+ 1) >> 1) + 511) >> 9;
166 cluster_cache
= bkmem_alloc(nsec_cache
* SECSIZ
);
167 if (cluster_cache
== NULL
) {
168 printf("bpcfs_mountroot: out of memory\n");
169 bkmem_free(pcfsp
, sizeof (*pcfsp
));
174 head
->fi_blocknum
= nsec_start
=
175 ltohs(pcfsp
->f_bpb
.bs_resv_sectors
) + pcfsp
->f_adjust
;
176 head
->fi_count
= nsec_cache
* SECSIZ
;
177 head
->fi_memp
= cluster_cache
;
178 if (diskread(head
)) {
179 printf("bpcfs_mountroot: failed to read cluster\n");
180 bkmem_free(pcfsp
, sizeof (*pcfsp
));
184 dprintf("read cluster sectors %d starting at %d\n",
185 nsec_cache
, nsec_start
);
190 bpcfs_unmountroot(void)
195 (void) bpcfs_closeall();
205 bpcfs_open(char *str
, int flags
)
207 static int filedes
= 1;
212 dprintf("open %s\n", str
);
213 filep
= (fileid_t
*)bkmem_alloc(sizeof (fileid_t
));
214 filep
->fi_back
= head
->fi_back
;
215 filep
->fi_forw
= head
;
216 head
->fi_back
->fi_forw
= filep
;
217 head
->fi_back
= filep
;
218 filep
->fi_filedes
= filedes
++;
220 filep
->fi_path
= (char *)bkmem_alloc(strlen(str
) + 1);
221 (void) strcpy(filep
->fi_path
, str
);
223 if (lookuppn(str
, &d
)) {
224 (void) bpcfs_close(filep
->fi_filedes
);
228 filep
->fi_offset
= 0;
229 FI_STARTCLUST(filep
) = d
.d_cluster
;
230 FI_LENGTH(filep
) = d
.d_size
;
231 dprintf("file %s size = %ld\n", str
, d
.d_size
);
232 return (filep
->fi_filedes
);
240 dprintf("close %d\n", fd
);
241 if (!(filep
= find_fp(fd
)))
244 if (filep
->fi_taken
== 0 || filep
== head
) {
245 printf("File descripter %d no allocated!\n", fd
);
249 /* unlink and deallocate node */
250 filep
->fi_forw
->fi_back
= filep
->fi_back
;
251 filep
->fi_back
->fi_forw
= filep
->fi_forw
;
252 bkmem_free(filep
->fi_path
, strlen(filep
->fi_path
) + 1);
253 bkmem_free((char *)filep
, sizeof (fileid_t
));
254 dprintf("close done\n");
263 while ((filep
= head
->fi_forw
) != head
)
264 if (filep
->fi_taken
&& bpcfs_close(filep
->fi_filedes
))
265 printf("Filesystem may be inconsistent.\n");
267 bkmem_free(pcfsp
, sizeof (*pcfsp
));
268 bkmem_free(head
, sizeof (fileid_t
));
274 bpcfs_read(int fd
, caddr_t b
, size_t c
)
277 uint_t count
= 0, xfer
, i
;
283 dprintf("bpcfs_read: fd = %d, buf = %p, size = %d\n",
287 printf("invalid file descriptor %d\n", fd
);
291 spc
= pcfsp
->f_bpb
.bs_spc
;
293 blk
= FI_STARTCLUST(fp
);
294 rd
= blk
== CLUSTER_ROOTDIR
? 1 : 0;
296 spc
= pcfsp
->f_bpb
.bs_spc
;
298 blk
= FI_STARTCLUST(fp
);
299 rd
= (blk
== CLUSTER_ROOTDIR
) ? 1 : 0;
301 if ((c
= MIN(FI_LENGTH(fp
) - off
, c
)) == 0)
304 while (off
>= pcfsp
->f_bpb
.bs_spc
* SECSIZ
) {
305 blk
= fat_map(blk
, rd
);
306 off
-= pcfsp
->f_bpb
.bs_spc
* SECSIZ
;
308 if (!cluster_valid(blk
, rd
)) {
309 printf("bpcfs_read: invalid cluster: %ld, %d\n",
316 sector
= fat_ctodb(blk
, rd
);
317 diskread_callback
= fileread_callback
;
318 for (i
= ((off
/ SECSIZ
) % pcfsp
->f_bpb
.bs_spc
); i
< spc
; i
++) {
319 xfer
= MIN(SECSIZ
- (off
% SECSIZ
), c
- count
);
321 break; /* last sector done */
323 block
= (char *)readblock(sector
+ i
, 1);
327 dprintf("bpcfs_read: read %d bytes\n", xfer
);
328 if (diskread_callback
== NULL
)
329 (void) bcopy(&block
[off
% SECSIZ
], b
, xfer
);
335 diskread_callback
= NULL
;
337 blk
= fat_map(blk
, rd
);
338 if (!cluster_valid(blk
, rd
)) {
339 printf("bpcfs_read: invalid cluster: %ld, %d\n",
346 fp
->fi_offset
+= count
;
351 * This version of seek() only performs absolute seeks (whence == 0).
354 bpcfs_lseek(int fd
, off_t addr
, int whence
)
358 dprintf("lseek %d, off = %lx\n", fd
, addr
);
359 if (!(filep
= find_fp(fd
)))
364 filep
->fi_offset
+= addr
;
367 filep
->fi_offset
= addr
;
371 printf("lseek(): invalid whence value %d\n", whence
);
375 filep
->fi_blocknum
= addr
/ DEV_BSIZE
;
383 fileid_t
*filep
= head
;
386 while ((filep
= filep
->fi_forw
) != head
)
387 if (fd
== filep
->fi_filedes
)
388 return (filep
->fi_taken
? filep
: 0);
395 cluster_valid(long c
, int rd
)
397 return ((rd
&& (c
== 0)) ? 1 : (c
>= CLUSTER_RES_16_0
? 0 : c
));
401 fat_ctodb(int blk
, int r
)
405 s
= r
? blk
+ pcfsp
->f_rootsec
+ pcfsp
->f_adjust
:
406 ((blk
- 2) * pcfsp
->f_bpb
.bs_spc
) +
407 pcfsp
->f_filesec
+ pcfsp
->f_adjust
;
413 fat_map(int blk
, int rootdir
)
415 ulong_t sectn
, fat_index
;
419 return (blk
> pcfsp
->f_rootlen
? CLUSTER_EOF
: blk
+ 1);
422 /* ---- Find out what sector this cluster is in ---- */
423 fat_index
= (pcfsp
->f_16bit
) ? ((ulong_t
)blk
<< 1) :
424 ((ulong_t
)blk
+ ((uint_t
)blk
>> 1));
426 sectn
= (fat_index
/ SECSIZ
) + ltohs(pcfsp
->f_bpb
.bs_resv_sectors
)
430 * Read two sectors so that if our fat_index points at the last byte
431 * byte we'll have the data needed. This is only a problem for fat12
434 if (!(fp
= (uchar_t
*)readblock(sectn
, 2))) {
435 printf("fat_map: bad cluster\n");
436 return (CLUSTER_BAD_16
);
439 fp
+= (fat_index
% SECSIZ
);
442 blk
= fp
[0] | (fp
[1] << 8);
445 blk
= ((fp
[0] >> 4) & 0xf) | (fp
[1] << 4);
447 blk
= ((fp
[1] & 0xf) << 8) | fp
[0];
450 * This makes compares easier because we can just compare
451 * against one value instead of two.
453 if (blk
>= CLUSTER_RES_12_0
)
454 blk
|= CLUSTER_RES_16_0
;
460 namecmp(char *pn
, char *dn
, int cs
)
462 dprintf("namecmp %s, %s, len = %d\n", pn
, dn
, cs
);
464 /* starting char must match */
467 if (toupper(*pn
++) != toupper(*dn
++))
471 dprintf("namecmp: cs = %d\n", cs
);
472 /* remainder should be either ~# or all spaces */
473 if (cs
> 0 && *dn
== '~')
484 dircmp(char *name
, char *d_name
, char *d_ext
)
489 sep
= (char *)strchr(name
, '.');
497 if (namecmp(name
, d_name
, NAMESIZ
) || namecmp(ext
, d_ext
, EXTSIZ
))
507 lookup(char *n
, _dir_entry_p dp
, ulong_t dir_blk
)
509 int spc
= pcfsp
->f_bpb
.bs_spc
;
510 int rd
= (dir_blk
== CLUSTER_ROOTDIR
? 1 : 0);
514 dprintf("lookup: name = %s\n", n
);
516 while (cluster_valid(dir_blk
, rd
)) {
517 sector
= fat_ctodb(dir_blk
, rd
);
518 dxp
= readblock(sector
, 1); /* read one sector */
521 for (j
= 0; j
< DIRENTS
* spc
; j
++, dxp
++) {
522 dprintf("lookup: dir entry %s.%s;\n",
523 dxp
->d_name
, dxp
->d_ext
);
524 if (dxp
->d_name
[0] == 0)
526 if ((uchar_t
)dxp
->d_name
[0] != 0xE5 &&
527 (dxp
->d_attr
& (DE_LABEL
|DE_HIDDEN
)) == 0 &&
528 dircmp(n
, dxp
->d_name
, dxp
->d_ext
) == 0) {
529 dprintf("lookup: match found\n");
530 (void) bcopy(dxp
, dp
, sizeof (*dp
));
535 dir_blk
= fat_map(dir_blk
, rd
);
542 lookuppn(char *n
, _dir_entry_p dp
)
545 char name
[8 + 1 + 3 + 1]; /* <8>.<3>'\0' */
549 dprintf("lookuppn: path = %s\n", n
);
550 dir_blk
= pcfsp
->f_dclust
;
551 if ((*n
== '\\') || (*n
== '/')) {
552 dir_blk
= CLUSTER_ROOTDIR
;
553 while ((*n
== '\\') || (*n
== '/'))
556 (void) bzero(dp
, sizeof (*dp
));
557 dp
->d_cluster
= CLUSTER_ROOTDIR
;
558 dp
->d_attr
= DE_DIRECTORY
;
563 ep
= &name
[0] + sizeof (name
);
565 (void) bzero(name
, sizeof (name
));
567 while (*n
&& (*n
!= '\\') && (*n
!= '/'))
571 dprintf("return, name %s is too long\n", name
);
572 return (-1); /* name is too long */
574 while ((*n
== '\\') || (*n
== '/'))
576 if (lookup(name
, &dd
, dir_blk
) == 0) {
577 dprintf("return, name %s not found\n", name
);
580 dprintf("dd = %x:%x:%x attr = %x\n",
581 *(int *)&dd
, *(((int *)&dd
) + 1),
582 *(((int *)&dd
) + 2), dd
.d_attr
);
583 if (*n
&& ((dd
.d_attr
& DE_DIRECTORY
) == 0)) {
584 dprintf("return, not a directory\n");
588 dir_blk
= dd
.d_cluster
;
590 (void) bcopy(&dd
, dp
, sizeof (dd
));
595 readblock(int sector
, int nsec
)
597 if (sector
>= nsec_start
&& sector
+ nsec
<= nsec_start
+ nsec_cache
)
598 return (cluster_cache
+ (sector
- nsec_start
) * SECSIZ
);
600 /* read disk sectors */
601 head
->fi_blocknum
= sector
;
602 head
->fi_count
= nsec
* SECSIZ
;
603 head
->fi_memp
= head
->fi_buf
;
604 if (diskread(head
)) {
605 printf("failed to %d sectors at %d\n", nsec
, sector
);
609 return (head
->fi_buf
);
612 struct boot_fs_ops bpcfs_ops
= {