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.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * Basic file system reading code for standalone I/O system.
31 * Simulates a primitive UNIX I/O system (read(), write(), open(), etc).
32 * Does not support writes.
37 * This is currently used by installgrub for creating bootable floppy.
38 * The special part is diskread_callback/fileread_callback for gathering
42 #include <sys/param.h>
43 #include <sys/sysmacros.h>
44 #include <sys/vnode.h>
45 #include <sys/fs/pc_label.h>
46 #include <sys/bootvfs.h>
47 #include <sys/filep.h>
51 #include "../common/util.h"
52 #elif defined(_KERNEL)
53 #include <sys/sunddi.h>
61 #define dprintf if (bootrd_debug) printf
62 #elif defined(_KERNEL)
63 #define printf kobj_printf
64 #define dprintf if (bootrd_debug) kobj_printf
67 extern void kobj_printf(char *, ...);
69 #define dprintf if (bootrd_debug) printf
72 #define FI_STARTCLUST(fp) (*(ushort_t *)(fp)->fi_buf)
73 #define FI_LENGTH(fp) (*(long *)((fp)->fi_buf + 4))
75 extern int bootrd_debug
;
76 extern void *bkmem_alloc(size_t);
77 extern void bkmem_free(void *, size_t);
80 * NOTE: The fileread_callback is set by the calling program
81 * during a file read. diskread_callback is set to fileread_callback
82 * only if reading a file block. It needs to be NULL while reading
85 extern int (*diskread_callback
)(int, int);
86 extern int (*fileread_callback
)(int, int);
91 static int lookuppn(char *, _dir_entry_p
);
92 static fileid_t
*find_fp(int);
93 static void *readblock(int, int);
94 static int fat_map(int, int);
95 static int cluster_valid(long, int);
96 static int fat_ctodb(int, int);
98 static int bpcfs_mountroot(char *str
);
99 static int bpcfs_unmountroot(void);
100 static int bpcfs_open(char *str
, int flags
);
101 static int bpcfs_close(int fd
);
102 static void bpcfs_closeall(void);
103 static ssize_t
bpcfs_read(int fdesc
, char *buf
, size_t count
);
104 static off_t
bpcfs_lseek(int fdesc
, off_t addr
, int whence
);
106 static fileid_t
*head
;
107 static _fat_controller_p pcfsp
;
109 /* cache the cluster */
110 static int nsec_cache
;
111 static int nsec_start
;
112 static char *cluster_cache
;
116 bpcfs_mountroot(char *str
)
120 return (0); /* already mounted */
122 pcfsp
= bkmem_alloc(sizeof (_fat_controller_t
));
123 head
= (fileid_t
*)bkmem_alloc(sizeof (fileid_t
));
124 head
->fi_back
= head
->fi_forw
= head
;
125 head
->fi_filedes
= 0;
128 /* read of first floppy sector */
129 head
->fi_blocknum
= 0;
130 head
->fi_count
= SECSIZ
;
131 head
->fi_memp
= (caddr_t
)pcfsp
->f_sector
;
132 if (diskread(head
)) {
133 printf("failed to read first sector\n");
134 bkmem_free(pcfsp
, sizeof (*pcfsp
));
139 if (pcfsp
->f_bpb
.bs_spc
== 0) {
140 printf("invalid bios paramet block\n");
145 (pcfsp
->f_bpb
.bs_num_fats
* ltohs(pcfsp
->f_bpb
.bs_spf
)) +
146 ltohs(pcfsp
->f_bpb
.bs_resv_sectors
);
148 ltohs(pcfsp
->f_bpb
.bs_num_root_entries
) *
149 sizeof (_dir_entry_t
) / SECSIZ
;
151 pcfsp
->f_dclust
= CLUSTER_ROOTDIR
;
152 pcfsp
->f_filesec
= pcfsp
->f_rootsec
+ pcfsp
->f_rootlen
;
153 pcfsp
->f_nxtfree
= CLUSTER_FIRST
;
155 /* figure out the number of clusters in this partition */
156 ncluster
= (((ulong_t
)ltohs(pcfsp
->f_bpb
.bs_siv
) ?
157 (ulong_t
)ltohs(pcfsp
->f_bpb
.bs_siv
) :
158 (ulong_t
)ltohi(pcfsp
->f_bpb
.bs_siv
)) -
159 pcfsp
->f_filesec
) / (ulong_t
)pcfsp
->f_bpb
.bs_spc
;
160 pcfsp
->f_16bit
= ncluster
>= CLUSTER_MAX_12
;
161 pcfsp
->f_ncluster
= ncluster
;
163 /* cache the cluster */
165 nsec_cache
= (((ncluster
<< 1) + 511) >> 9);
167 nsec_cache
= (ncluster
+ ((ncluster
+ 1) >> 1) + 511) >> 9;
168 cluster_cache
= bkmem_alloc(nsec_cache
* SECSIZ
);
169 if (cluster_cache
== NULL
) {
170 printf("bpcfs_mountroot: out of memory\n");
171 bkmem_free(pcfsp
, sizeof (*pcfsp
));
176 head
->fi_blocknum
= nsec_start
=
177 ltohs(pcfsp
->f_bpb
.bs_resv_sectors
) + pcfsp
->f_adjust
;
178 head
->fi_count
= nsec_cache
* SECSIZ
;
179 head
->fi_memp
= cluster_cache
;
180 if (diskread(head
)) {
181 printf("bpcfs_mountroot: failed to read cluster\n");
182 bkmem_free(pcfsp
, sizeof (*pcfsp
));
186 dprintf("read cluster sectors %d starting at %d\n",
187 nsec_cache
, nsec_start
);
192 bpcfs_unmountroot(void)
197 (void) bpcfs_closeall();
207 bpcfs_open(char *str
, int flags
)
209 static int filedes
= 1;
214 dprintf("open %s\n", str
);
215 filep
= (fileid_t
*)bkmem_alloc(sizeof (fileid_t
));
216 filep
->fi_back
= head
->fi_back
;
217 filep
->fi_forw
= head
;
218 head
->fi_back
->fi_forw
= filep
;
219 head
->fi_back
= filep
;
220 filep
->fi_filedes
= filedes
++;
222 filep
->fi_path
= (char *)bkmem_alloc(strlen(str
) + 1);
223 (void) strcpy(filep
->fi_path
, str
);
225 if (lookuppn(str
, &d
)) {
226 (void) bpcfs_close(filep
->fi_filedes
);
230 filep
->fi_offset
= 0;
231 FI_STARTCLUST(filep
) = d
.d_cluster
;
232 FI_LENGTH(filep
) = d
.d_size
;
233 dprintf("file %s size = %ld\n", str
, d
.d_size
);
234 return (filep
->fi_filedes
);
242 dprintf("close %d\n", fd
);
243 if (!(filep
= find_fp(fd
)))
246 if (filep
->fi_taken
== 0 || filep
== head
) {
247 printf("File descripter %d no allocated!\n", fd
);
251 /* unlink and deallocate node */
252 filep
->fi_forw
->fi_back
= filep
->fi_back
;
253 filep
->fi_back
->fi_forw
= filep
->fi_forw
;
254 bkmem_free(filep
->fi_path
, strlen(filep
->fi_path
) + 1);
255 bkmem_free((char *)filep
, sizeof (fileid_t
));
256 dprintf("close done\n");
265 while ((filep
= head
->fi_forw
) != head
)
266 if (filep
->fi_taken
&& bpcfs_close(filep
->fi_filedes
))
267 printf("Filesystem may be inconsistent.\n");
269 bkmem_free(pcfsp
, sizeof (*pcfsp
));
270 bkmem_free(head
, sizeof (fileid_t
));
276 bpcfs_read(int fd
, caddr_t b
, size_t c
)
279 uint_t count
= 0, xfer
, i
;
285 dprintf("bpcfs_read: fd = %d, buf = %p, size = %d\n",
289 printf("invalid file descriptor %d\n", fd
);
293 spc
= pcfsp
->f_bpb
.bs_spc
;
295 blk
= FI_STARTCLUST(fp
);
296 rd
= blk
== CLUSTER_ROOTDIR
? 1 : 0;
298 spc
= pcfsp
->f_bpb
.bs_spc
;
300 blk
= FI_STARTCLUST(fp
);
301 rd
= (blk
== CLUSTER_ROOTDIR
) ? 1 : 0;
303 if ((c
= MIN(FI_LENGTH(fp
) - off
, c
)) == 0)
306 while (off
>= pcfsp
->f_bpb
.bs_spc
* SECSIZ
) {
307 blk
= fat_map(blk
, rd
);
308 off
-= pcfsp
->f_bpb
.bs_spc
* SECSIZ
;
310 if (!cluster_valid(blk
, rd
)) {
311 printf("bpcfs_read: invalid cluster: %ld, %d\n",
318 sector
= fat_ctodb(blk
, rd
);
319 diskread_callback
= fileread_callback
;
320 for (i
= ((off
/ SECSIZ
) % pcfsp
->f_bpb
.bs_spc
); i
< spc
; i
++) {
321 xfer
= MIN(SECSIZ
- (off
% SECSIZ
), c
- count
);
323 break; /* last sector done */
325 block
= (char *)readblock(sector
+ i
, 1);
329 dprintf("bpcfs_read: read %d bytes\n", xfer
);
330 if (diskread_callback
== NULL
)
331 (void) bcopy(&block
[off
% SECSIZ
], b
, xfer
);
337 diskread_callback
= NULL
;
339 blk
= fat_map(blk
, rd
);
340 if (!cluster_valid(blk
, rd
)) {
341 printf("bpcfs_read: invalid cluster: %ld, %d\n",
348 fp
->fi_offset
+= count
;
353 * This version of seek() only performs absolute seeks (whence == 0).
356 bpcfs_lseek(int fd
, off_t addr
, int whence
)
360 dprintf("lseek %d, off = %lx\n", fd
, addr
);
361 if (!(filep
= find_fp(fd
)))
366 filep
->fi_offset
+= addr
;
369 filep
->fi_offset
= addr
;
373 printf("lseek(): invalid whence value %d\n", whence
);
377 filep
->fi_blocknum
= addr
/ DEV_BSIZE
;
385 fileid_t
*filep
= head
;
388 while ((filep
= filep
->fi_forw
) != head
)
389 if (fd
== filep
->fi_filedes
)
390 return (filep
->fi_taken
? filep
: 0);
397 cluster_valid(long c
, int rd
)
399 return ((rd
&& (c
== 0)) ? 1 : (c
>= CLUSTER_RES_16_0
? 0 : c
));
403 fat_ctodb(int blk
, int r
)
407 s
= r
? blk
+ pcfsp
->f_rootsec
+ pcfsp
->f_adjust
:
408 ((blk
- 2) * pcfsp
->f_bpb
.bs_spc
) +
409 pcfsp
->f_filesec
+ pcfsp
->f_adjust
;
415 fat_map(int blk
, int rootdir
)
417 ulong_t sectn
, fat_index
;
421 return (blk
> pcfsp
->f_rootlen
? CLUSTER_EOF
: blk
+ 1);
424 /* ---- Find out what sector this cluster is in ---- */
425 fat_index
= (pcfsp
->f_16bit
) ? ((ulong_t
)blk
<< 1) :
426 ((ulong_t
)blk
+ ((uint_t
)blk
>> 1));
428 sectn
= (fat_index
/ SECSIZ
) + ltohs(pcfsp
->f_bpb
.bs_resv_sectors
)
432 * Read two sectors so that if our fat_index points at the last byte
433 * byte we'll have the data needed. This is only a problem for fat12
436 if (!(fp
= (uchar_t
*)readblock(sectn
, 2))) {
437 printf("fat_map: bad cluster\n");
438 return (CLUSTER_BAD_16
);
441 fp
+= (fat_index
% SECSIZ
);
444 blk
= fp
[0] | (fp
[1] << 8);
447 blk
= ((fp
[0] >> 4) & 0xf) | (fp
[1] << 4);
449 blk
= ((fp
[1] & 0xf) << 8) | fp
[0];
452 * This makes compares easier because we can just compare
453 * against one value instead of two.
455 if (blk
>= CLUSTER_RES_12_0
)
456 blk
|= CLUSTER_RES_16_0
;
462 namecmp(char *pn
, char *dn
, int cs
)
464 dprintf("namecmp %s, %s, len = %d\n", pn
, dn
, cs
);
466 /* starting char must match */
469 if (toupper(*pn
++) != toupper(*dn
++))
473 dprintf("namecmp: cs = %d\n", cs
);
474 /* remainder should be either ~# or all spaces */
475 if (cs
> 0 && *dn
== '~')
486 dircmp(char *name
, char *d_name
, char *d_ext
)
491 sep
= (char *)strchr(name
, '.');
499 if (namecmp(name
, d_name
, NAMESIZ
) || namecmp(ext
, d_ext
, EXTSIZ
))
509 lookup(char *n
, _dir_entry_p dp
, ulong_t dir_blk
)
511 int spc
= pcfsp
->f_bpb
.bs_spc
;
512 int rd
= (dir_blk
== CLUSTER_ROOTDIR
? 1 : 0);
516 dprintf("lookup: name = %s\n", n
);
518 while (cluster_valid(dir_blk
, rd
)) {
519 sector
= fat_ctodb(dir_blk
, rd
);
520 dxp
= readblock(sector
, 1); /* read one sector */
523 for (j
= 0; j
< DIRENTS
* spc
; j
++, dxp
++) {
524 dprintf("lookup: dir entry %s.%s;\n",
525 dxp
->d_name
, dxp
->d_ext
);
526 if (dxp
->d_name
[0] == 0)
528 if ((uchar_t
)dxp
->d_name
[0] != 0xE5 &&
529 (dxp
->d_attr
& (DE_LABEL
|DE_HIDDEN
)) == 0 &&
530 dircmp(n
, dxp
->d_name
, dxp
->d_ext
) == 0) {
531 dprintf("lookup: match found\n");
532 (void) bcopy(dxp
, dp
, sizeof (*dp
));
537 dir_blk
= fat_map(dir_blk
, rd
);
544 lookuppn(char *n
, _dir_entry_p dp
)
547 char name
[8 + 1 + 3 + 1]; /* <8>.<3>'\0' */
551 dprintf("lookuppn: path = %s\n", n
);
552 dir_blk
= pcfsp
->f_dclust
;
553 if ((*n
== '\\') || (*n
== '/')) {
554 dir_blk
= CLUSTER_ROOTDIR
;
555 while ((*n
== '\\') || (*n
== '/'))
558 (void) bzero(dp
, sizeof (*dp
));
559 dp
->d_cluster
= CLUSTER_ROOTDIR
;
560 dp
->d_attr
= DE_DIRECTORY
;
565 ep
= &name
[0] + sizeof (name
);
567 (void) bzero(name
, sizeof (name
));
569 while (*n
&& (*n
!= '\\') && (*n
!= '/'))
573 dprintf("return, name %s is too long\n", name
);
574 return (-1); /* name is too long */
576 while ((*n
== '\\') || (*n
== '/'))
578 if (lookup(name
, &dd
, dir_blk
) == 0) {
579 dprintf("return, name %s not found\n", name
);
582 dprintf("dd = %x:%x:%x attr = %x\n",
583 *(int *)&dd
, *(((int *)&dd
) + 1),
584 *(((int *)&dd
) + 2), dd
.d_attr
);
585 if (*n
&& ((dd
.d_attr
& DE_DIRECTORY
) == 0)) {
586 dprintf("return, not a directory\n");
590 dir_blk
= dd
.d_cluster
;
592 (void) bcopy(&dd
, dp
, sizeof (dd
));
597 readblock(int sector
, int nsec
)
599 if (sector
>= nsec_start
&& sector
+ nsec
<= nsec_start
+ nsec_cache
)
600 return (cluster_cache
+ (sector
- nsec_start
) * SECSIZ
);
602 /* read disk sectors */
603 head
->fi_blocknum
= sector
;
604 head
->fi_count
= nsec
* SECSIZ
;
605 head
->fi_memp
= head
->fi_buf
;
606 if (diskread(head
)) {
607 printf("failed to %d sectors at %d\n", nsec
, sector
);
611 return (head
->fi_buf
);
614 struct boot_fs_ops bpcfs_ops
= {