git: e7c0cb720898 - main - loader: dosfs should be able to work with different sector sizes

From: Toomas Soome <tsoome_at_FreeBSD.org>
Date: Mon, 03 Mar 2025 21:54:49 UTC
The branch main has been updated by tsoome:

URL: https://cgit.FreeBSD.org/src/commit/?id=e7c0cb720898fd36487c4e6b0aff52435084e6bf

commit e7c0cb720898fd36487c4e6b0aff52435084e6bf
Author:     Toomas Soome <tsoome@FreeBSD.org>
AuthorDate: 2025-03-03 21:54:23 +0000
Commit:     Toomas Soome <tsoome@FreeBSD.org>
CommitDate: 2025-03-03 21:54:23 +0000

    loader: dosfs should be able to work with different sector sizes
    
    The current dosfs is only able to use 512B sector sizes, add
    support for 4kn disks (incidentally this should also add support
    for 1024B and 2048B sectors, have not tested those).
    
    Reviewed by:    imp
    Differential Revision:  https://reviews.freebsd.org/D49078
---
 stand/libsa/dosfs.c | 194 ++++++++++++++++++++++++++++++++++------------------
 stand/libsa/dosfs.h |   3 +
 2 files changed, 131 insertions(+), 66 deletions(-)

diff --git a/stand/libsa/dosfs.c b/stand/libsa/dosfs.c
index 123350263d09..aca198cdf6fa 100644
--- a/stand/libsa/dosfs.c
+++ b/stand/libsa/dosfs.c
@@ -32,6 +32,7 @@
  */
 
 #include <sys/types.h>
+#include <sys/disk.h>
 #include <string.h>
 #include <stddef.h>
 
@@ -71,10 +72,6 @@ struct fs_ops dosfs_fsops = {
 	.fo_unmount = dos_unmount
 };
 
-#define SECSIZ  512             /* sector size */
-#define SSHIFT    9             /* SECSIZ shift */
-#define DEPSEC   16             /* directory entries per sector */
-#define DSHIFT    4             /* DEPSEC shift */
 #define LOCLUS    2             /* lowest cluster number */
 #define FATBLKSZ  0x20000       /* size of block in the FAT cache buffer */
 
@@ -92,14 +89,43 @@ typedef struct {
 	u_char heads[2];            /* drive heads */
 	u_char hidsec[4];           /* hidden sectors */
 	u_char lsecs[4];            /* huge sectors */
-	u_char lspf[4];             /* huge sectors per FAT */
-	u_char xflg[2];             /* flags */
-	u_char vers[2];             /* filesystem version */
-	u_char rdcl[4];             /* root directory start cluster */
-	u_char infs[2];             /* filesystem info sector */
-	u_char bkbs[2];             /* backup boot sector */
+	union {
+		struct {
+			u_char drvnum;		/* Int 13 drive number */
+			u_char rsvd1;		/* Reserved */
+			u_char bootsig;		/* Boot signature (0x29) */
+			u_char volid[4];	/* Volume serial number */
+			u_char vollab[11];	/* Volume label */
+			u_char fstype[8];	/* Informational */
+		} f12_f16;
+		struct {
+			u_char lspf[4];		/* huge sectors per FAT */
+			u_char xflg[2];		/* flags */
+			u_char vers[2];		/* filesystem version */
+			u_char rdcl[4];		/* root directory cluster */
+			u_char infs[2];		/* filesystem info sector */
+			u_char bkbs[2];		/* backup boot sector */
+			u_char reserved[12];	/* Reserved */
+			u_char drvnum;		/* Int 13 drive number */
+			u_char rsvd1;		/* Reserved */
+			u_char bootsig;		/* Boot signature (0x29) */
+			u_char volid[4];	/* Volume serial number */
+			u_char vollab[11];	/* Volume label */
+			u_char fstype[8];	/* Informational */
+		} f32;
+	} fstype;
 } DOS_BPB;
 
+typedef struct {
+	u_char fsi_leadsig[4];		/* Value 0x41615252 */
+	u_char fsi_reserved1[480];
+	u_char fsi_structsig[4];	/* Value 0x61417272 */
+	u_char fsi_free_count[4];	/* Last known free cluster count */
+	u_char fsi_next_free[4];	/* First free cluster */
+	u_char fsi_reserved2[12];
+	u_char fsi_trailsig[4];		/* Value 0xAA550000 */
+} DOS_FSINFO;
+
 /* Initial portion of DOS boot sector */
 typedef struct {
 	u_char jmp[3];              /* usually 80x86 'jmp' opcode */
@@ -117,16 +143,18 @@ static DOS_DE dot[2] = {
 };
 
 /* The usual conversion macros to avoid multiplication and division */
-#define bytsec(n)      ((n) >> SSHIFT)
-#define secbyt(s)      ((s) << SSHIFT)
-#define entsec(e)      ((e) >> DSHIFT)
-#define bytblk(fs, n)  ((n) >> (fs)->bshift)
-#define blkbyt(fs, b)  ((b) << (fs)->bshift)
-#define secblk(fs, s)  ((s) >> ((fs)->bshift - SSHIFT))
-#define blksec(fs, b)  ((b) << ((fs)->bshift - SSHIFT))
+#define bytsec(fs, n)	((n) >> (fs)->sshift)
+#define secbyt(fs, s)	((s) << (fs)->sshift)
+#define depsec(fs)	(1 << (fs)->dshift)
+#define entsec(fs, e)	((e) >> (fs)->dshift)
+#define bytblk(fs, n)	((n) >> (fs)->bshift)
+#define blkbyt(fs, b)	((b) << (fs)->bshift)
+#define secblk(fs, s)	((s) >> ((fs)->bshift - (fs)->sshift))
+#define blksec(fs, b)	((b) << ((fs)->bshift - (fs)->sshift))
 
 /* Convert cluster number to offset within filesystem */
-#define blkoff(fs, b) (secbyt((fs)->lsndta) + blkbyt(fs, (b) - LOCLUS))
+#define blkoff(fs, b)	(secbyt(fs, (fs)->lsndta) + \
+			blkbyt(fs, (b) - LOCLUS))
 
 /* Convert cluster number to logical sector number */
 #define blklsn(fs, b)  ((fs)->lsndta + blksec(fs, (b) - LOCLUS))
@@ -154,17 +182,17 @@ static int fatcnt(DOS_FS *, u_int);
 static int fatget(DOS_FS *, u_int *);
 static int fatend(u_int, u_int);
 static int ioread(DOS_FS *, uint64_t, void *, size_t);
-static int ioget(struct open_file *, daddr_t, void *, size_t);
+static int ioget(DOS_FS *, daddr_t, void *, size_t);
 
 static int
-dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
+dos_read_fatblk(DOS_FS *fs, u_int blknum)
 {
 	int err;
 	size_t io_size;
 	daddr_t offset_in_fat, max_offset_in_fat;
 
 	offset_in_fat = ((daddr_t)blknum) * FATBLKSZ;
-	max_offset_in_fat = secbyt(fs->spf);
+	max_offset_in_fat = secbyt(fs, (daddr_t)fs->spf);
 	io_size = FATBLKSZ;
 	if (offset_in_fat > max_offset_in_fat)
 		offset_in_fat = max_offset_in_fat;
@@ -172,7 +200,7 @@ dos_read_fatblk(DOS_FS *fs, struct open_file *fd, u_int blknum)
 		io_size = ((size_t)(max_offset_in_fat - offset_in_fat));
 
 	if (io_size != 0) {
-		err = ioget(fd, fs->lsnfat + bytsec(offset_in_fat),
+		err = ioget(fs, fs->lsnfat + bytsec(fs, offset_in_fat),
 		    fs->fatbuf, io_size);
 		if (err != 0) {
 			fs->fatbuf_blknum = ((u_int)(-1));
@@ -193,23 +221,34 @@ static int
 dos_mount_impl(DOS_FS *fs, struct open_file *fd)
 {
 	int err;
+	unsigned secsz;
 	u_char *buf;
 
 	fs->fd = fd;
 
-	if ((buf = malloc(secbyt(1))) == NULL)
+	err = ioctl(fd->f_id, DIOCGSECTORSIZE, &secsz);
+	if (err != 0) {
+		return (err);
+	}
+
+	buf = malloc(secsz);
+	if (buf == NULL)
 		return (errno);
-	if ((err = ioget(fs->fd, 0, buf, secbyt(1))) ||
+
+	if ((err = ioget(fs, 0, buf, secsz)) ||
 	    (err = parsebs(fs, (DOS_BS *)buf))) {
 		free(buf);
 		return (err);
 	}
-	free(buf);
+	fs->secbuf = buf;
 
-	if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL)
+	if ((fs->fatbuf = malloc(FATBLKSZ)) == NULL) {
+		free(buf);
 		return (errno);
-	err = dos_read_fatblk(fs, fd, 0);
+	}
+	err = dos_read_fatblk(fs, 0);
 	if (err != 0) {
+		free(buf);
 		free(fs->fatbuf);
 		return (err);
 	}
@@ -292,6 +331,7 @@ dos_unmount_impl(DOS_FS *fs)
 {
 	if (fs->links)
 		return (EBUSY);
+	free(fs->secbuf);
 	free(fs->fatbuf);
 	free(fs);
 	return (0);
@@ -305,7 +345,7 @@ dos_open(const char *path, struct open_file *fd)
 {
 	DOS_DE *de;
 	DOS_FILE *f;
-	DOS_FS *fs;
+	DOS_FS *fs = NULL;
 	dos_mnt_t *mnt;
 	const char *dev;
 	u_int size, clus;
@@ -408,7 +448,7 @@ dos_read(struct open_file *fd, void *buf, size_t nbyte, size_t *resid)
 		if (c != 0)
 			off += blkoff(f->fs, (uint64_t)c);
 		else
-			off += secbyt(f->fs->lsndir);
+			off += secbyt(f->fs, f->fs->lsndir);
 		err = ioread(f->fs, off, buf, n);
 		if (err != 0)
 			goto out;
@@ -588,36 +628,54 @@ dos_readdir(struct open_file *fd, struct dirent *d)
 static int
 parsebs(DOS_FS *fs, DOS_BS *bs)
 {
-	u_int sc;
+	u_int sc, RootDirSectors;
 
-	if ((bs->jmp[0] != 0x69 &&
-	    bs->jmp[0] != 0xe9 &&
-	    (bs->jmp[0] != 0xeb || bs->jmp[2] != 0x90)) ||
-	    bs->bpb.media < 0xf0)
+	if (bs->bpb.media < 0xf0)
 		return (EINVAL);
-	if (cv2(bs->bpb.secsiz) != SECSIZ)
+
+	/* Check supported sector sizes */
+	switch (cv2(bs->bpb.secsiz)) {
+	case 512:
+	case 1024:
+	case 2048:
+	case 4096:
+		fs->sshift = ffs(cv2(bs->bpb.secsiz)) - 1;
+		break;
+
+	default:
 		return (EINVAL);
+	}
+
 	if (!(fs->spc = bs->bpb.spc) || fs->spc & (fs->spc - 1))
 		return (EINVAL);
-	fs->bsize = secbyt(fs->spc);
+	fs->bsize = secbyt(fs, fs->spc);
 	fs->bshift = ffs(fs->bsize) - 1;
-	if ((fs->spf = cv2(bs->bpb.spf))) {
+	fs->dshift = ffs(secbyt(fs, 1) / sizeof (DOS_DE)) - 1;
+	fs->dirents = cv2(bs->bpb.dirents);
+	fs->spf = cv2(bs->bpb.spf);
+	fs->lsnfat = cv2(bs->bpb.ressec);
+
+	if (fs->spf != 0) {
 		if (bs->bpb.fats != 2)
 			return (EINVAL);
-		if (!(fs->dirents = cv2(bs->bpb.dirents)))
+		if (fs->dirents == 0)
 			return (EINVAL);
 	} else {
-		if (!(fs->spf = cv4(bs->bpb.lspf)))
+		fs->spf = cv4(bs->bpb.fstype.f32.lspf);
+		if (fs->spf == 0)
 			return (EINVAL);
-		if (!bs->bpb.fats || bs->bpb.fats > 16)
+		if (bs->bpb.fats == 0 || bs->bpb.fats > 16)
 			return (EINVAL);
-		if ((fs->rdcl = cv4(bs->bpb.rdcl)) < LOCLUS)
+		fs->rdcl = cv4(bs->bpb.fstype.f32.rdcl);
+		if (fs->rdcl < LOCLUS)
 			return (EINVAL);
 	}
-	if (!(fs->lsnfat = cv2(bs->bpb.ressec)))
-		return (EINVAL);
+
+	RootDirSectors = ((fs->dirents * sizeof (DOS_DE)) +
+	    (secbyt(fs, 1) - 1)) / secbyt(fs, 1);
+
 	fs->lsndir = fs->lsnfat + fs->spf * bs->bpb.fats;
-	fs->lsndta = fs->lsndir + entsec(fs->dirents);
+	fs->lsndta = fs->lsndir + RootDirSectors;
 	if (!(sc = cv2(bs->bpb.secs)) && !(sc = cv4(bs->bpb.lsecs)))
 		return (EINVAL);
 	if (fs->lsndta > sc)
@@ -625,7 +683,7 @@ parsebs(DOS_FS *fs, DOS_BS *bs)
 	if ((fs->xclus = secblk(fs, sc - fs->lsndta) + 1) < LOCLUS)
 		return (EINVAL);
 	fs->fatsz = fs->dirents ? fs->xclus < 0xff6 ? 12 : 16 : 32;
-	sc = (secbyt(fs->spf) << 1) / (fs->fatsz >> 2) - 1;
+	sc = (secbyt(fs, fs->spf) << 1) / (fs->fatsz >> 2) - 1;
 	if (fs->xclus > sc)
 		fs->xclus = sc;
 	return (0);
@@ -672,7 +730,7 @@ namede(DOS_FS *fs, const char *path, DOS_DE **dep)
 static int
 lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
 {
-	static DOS_DIR dir[DEPSEC];
+	DOS_DIR *dir;
 	u_char lfn[261];
 	u_char sfn[13];
 	u_int nsec, lsec, xdn, chk, sec, ent, x;
@@ -686,9 +744,10 @@ lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
 		}
 	if (!clus && fs->fatsz == 32)
 		clus = fs->rdcl;
-	nsec = !clus ? entsec(fs->dirents) : fs->spc;
+	nsec = !clus ? entsec(fs, fs->dirents) : fs->spc;
 	lsec = 0;
 	xdn = chk = 0;
+	dir = (DOS_DIR *)fs->secbuf;
 	for (;;) {
 		if (!clus && !lsec)
 			lsec = fs->lsndir;
@@ -697,9 +756,10 @@ lookup(DOS_FS *fs, u_int clus, const char *name, DOS_DE **dep)
 		else
 			return (EINVAL);
 		for (sec = 0; sec < nsec; sec++) {
-			if ((err = ioget(fs->fd, lsec + sec, dir, secbyt(1))))
+			if ((err = ioget(fs, lsec + sec, dir,
+			    secbyt(fs, 1))))
 				return (err);
-			for (ent = 0; ent < DEPSEC; ent++) {
+			for (ent = 0; ent < depsec(fs); ent++) {
 				if (!*dir[ent].de.name)
 					return (ENOENT);
 				if (*dir[ent].de.name != 0xe5) {
@@ -866,14 +926,14 @@ fatget(DOS_FS *fs, u_int *c)
 	/* ensure that current 128K FAT block is cached */
 	offset = fatoff(fs->fatsz, val_in);
 	nbyte = fs->fatsz != 32 ? 2 : 4;
-	if (offset + nbyte > secbyt(fs->spf))
+	if (offset + nbyte > secbyt(fs, fs->spf))
 		return (EINVAL);
 	blknum = offset / FATBLKSZ;
 	offset %= FATBLKSZ;
 	if (offset + nbyte > FATBLKSZ)
 		return (EINVAL);
 	if (blknum != fs->fatbuf_blknum) {
-		err = dos_read_fatblk(fs, fs->fd, blknum);
+		err = dos_read_fatblk(fs, blknum);
 		if (err != 0)
 			return (err);
 	}
@@ -918,53 +978,55 @@ static int
 ioread(DOS_FS *fs, uint64_t offset, void *buf, size_t nbyte)
 {
 	char *s;
-	size_t n;
+	size_t n, secsiz;
 	int err;
 	uint64_t off;
-	u_char local_buf[SECSIZ];
 
+	secsiz = secbyt(fs, 1);
 	s = buf;
-	if ((off = offset & (SECSIZ - 1))) {
+	if ((off = offset & (secsiz - 1))) {
 		offset -= off;
-		if ((n = SECSIZ - off) > nbyte)
+		if ((n = secsiz - off) > nbyte)
 			n = nbyte;
-		err = ioget(fs->fd, bytsec(offset), local_buf,
-		    sizeof(local_buf));
+		err = ioget(fs, bytsec(fs, offset), fs->secbuf, secsiz);
 		if (err != 0)
 			return (err);
-		memcpy(s, local_buf + off, n);
-		offset += SECSIZ;
+		memcpy(s, fs->secbuf + off, n);
+		offset += secsiz;
 		s += n;
 		nbyte -= n;
 	}
-	n = nbyte & (SECSIZ - 1);
+	n = nbyte & (secsiz - 1);
 	if (nbyte -= n) {
-		if ((err = ioget(fs->fd, bytsec(offset), s, nbyte)))
+		if ((err = ioget(fs, bytsec(fs, offset), s, nbyte)))
 			return (err);
 		offset += nbyte;
 		s += nbyte;
 	}
 	if (n != 0) {
-		err = ioget(fs->fd, bytsec(offset), local_buf,
-		    sizeof(local_buf));
+		err = ioget(fs, bytsec(fs, offset), fs->secbuf, secsiz);
 		if (err != 0)
 			return (err);
-		memcpy(s, local_buf, n);
+		memcpy(s, fs->secbuf, n);
 	}
 	return (0);
 }
 
 /*
- * Sector-based I/O primitive
+ * Sector-based I/O primitive. Note, since strategy functions are operating
+ * in terms of 512B sectors, we need to do necessary conversion here.
  */
 static int
-ioget(struct open_file *fd, daddr_t lsec, void *buf, size_t size)
+ioget(DOS_FS *fs, daddr_t lsec, void *buf, size_t size)
 {
 	size_t rsize;
 	int rv;
+	struct open_file *fd = fs->fd;
 
 	/* Make sure we get full read or error. */
 	rsize = 0;
+	/* convert native sector number to 512B sector number. */
+	lsec = secbyt(fs, lsec) >> 9;
 	rv = (fd->f_dev->dv_strategy)(fd->f_devdata, F_READ, lsec,
 	    size, buf, &rsize);
 	if ((rv == 0) && (size != rsize))
diff --git a/stand/libsa/dosfs.h b/stand/libsa/dosfs.h
index 44fe8eac2595..9d844001a057 100644
--- a/stand/libsa/dosfs.h
+++ b/stand/libsa/dosfs.h
@@ -94,12 +94,15 @@ typedef union {
 
 typedef struct {
     struct open_file *fd;       /* file descriptor */
+    u_char *secbuf;		/* sector cache */
     u_char *fatbuf;             /* FAT cache buffer */
     u_int fatbuf_blknum;        /* number of 128K block in FAT cache buffer */
     u_int links;                /* active links to structure */
+    u_int sshift;		/* sector shift */
     u_int spc;                  /* sectors per cluster */
     u_int bsize;                /* cluster size in bytes */
     u_int bshift;               /* cluster conversion shift */
+    u_int dshift;		/* directory entries shift */
     u_int dirents;              /* root directory entries */
     u_int spf;                  /* sectors per fat */
     u_int rdcl;                 /* root directory start cluster */