2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
32 #define DIRENT_UNUSED 0xe5
34 #define DENT_IS_NULL(dent) (((unsigned char*)(dent))[0] == 0)
35 #define DENT_IS_UNUSED(dent) (((unsigned char*)(dent))[0] == DIRENT_UNUSED)
38 #define ATTR_HIDDEN 0x02
39 #define ATTR_SYSTEM 0x04
40 #define ATTR_VOLID 0x08
42 #define ATTR_ARCHIVE 0x20
46 enum { FAT12, FAT16, FAT32, EXFAT };
47 static const char *typestr[] = { "fat12", "fat16", "fat32", "exfat" };
62 uint32_t first_data_sect;
63 uint32_t num_data_sect;
64 uint32_t num_clusters;
68 struct fat_dir *rootdir;
69 unsigned int clust_mask;
75 unsigned char fmtid[8];
78 uint16_t reserved_sect;
87 uint32_t num_sectors32;
88 } __attribute__((packed));
97 } __attribute__((packed));
104 uint16_t fsinfo_sect;
105 uint16_t backup_boot_sect;
113 } __attribute__((packed));
119 uint8_t ctime_tenths;
120 uint16_t ctime_halfsec;
123 uint16_t first_cluster_high; /* 0 for FAT12 and FAT16 */
126 uint16_t first_cluster_low;
128 } __attribute__((packed));
139 } __attribute__((packed));
145 struct fat_dirent *ent;
148 struct fs_dirent *fsent;
156 struct fat_dirent ent;
159 int32_t cur_clust; /* cluster number corresponding to cur_pos */
166 static void destroy(struct filesys *fs);
168 static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags);
169 static void close(struct fs_node *node);
170 static long fsize(struct fs_node *node);
171 static int seek(struct fs_node *node, int offs, int whence);
172 static long tell(struct fs_node *node);
173 static int read(struct fs_node *node, void *buf, int sz);
174 static int write(struct fs_node *node, void *buf, int sz);
175 static int rewinddir(struct fs_node *node);
176 static struct fs_dirent *readdir(struct fs_node *node);
177 static int rename(struct fs_node *node, const char *name);
178 static int remove(struct fs_node *node);
180 static struct fat_dir *load_dir(struct fatfs *fs, struct fat_dirent *dent);
181 static void parse_dir_entries(struct fat_dir *dir);
182 static void free_dir(struct fat_dir *dir);
184 static struct fat_file *init_file(struct fatfs *fatfs, struct fat_dirent *dent);
185 static void free_file(struct fat_file *file);
187 static int read_sectors(int dev, uint64_t sidx, int count, void *sect);
188 static int read_cluster(struct fatfs *fatfs, uint32_t addr, void *clust);
189 static int dent_filename(struct fat_dirent *dent, struct fat_dirent *prev, char *buf);
190 static struct fs_dirent *find_entry(struct fat_dir *dir, const char *name);
192 static uint32_t read_fat(struct fatfs *fatfs, uint32_t addr);
193 static int32_t next_cluster(struct fatfs *fatfs, int32_t addr);
194 static int32_t find_cluster(struct fatfs *fatfs, int count, int32_t clust);
196 /* static void dbg_printdir(struct fat_dirent *dir, int max_entries); */
197 static void clean_trailws(char *s);
199 static struct fs_operations fs_fat_ops = {
212 static unsigned char sectbuf[512];
213 static int max_sect_once;
215 struct filesys *fsfat_create(int dev, uint64_t start, uint64_t size)
221 struct fat_dir *rootdir;
223 struct bparam_ext16 *bpb16;
224 struct bparam_ext32 *bpb32;
226 max_sect_once = ((unsigned char*)0xa0000 - low_mem_buffer) / 512;
227 /* some BIOS implementations have a maximum limit of 127 sectors */
228 if(max_sect_once > 127) max_sect_once = 127;
230 if(read_sectors(dev, start, 1, sectbuf) == -1) {
233 bpb = (struct bparam*)sectbuf;
234 bpb16 = (struct bparam_ext16*)(sectbuf + sizeof *bpb);
235 bpb32 = (struct bparam_ext32*)(sectbuf + sizeof *bpb);
237 if(bpb->jmp[0] != 0xeb || bpb->jmp[2] != 0x90) {
240 if(bpb->sect_bytes != 512) {
244 if(!(fatfs = malloc(sizeof *fatfs))) {
245 panic("FAT: create failed to allocate memory for the fat filesystem data\n");
247 memset(fatfs, 0, sizeof *fatfs);
248 fatfs->dev = dev < 0 ? boot_drive_number : dev;
249 fatfs->start_sect = start;
250 fatfs->size = bpb->num_sectors ? bpb->num_sectors : bpb->num_sectors32;
251 fatfs->cluster_size = bpb->cluster_size;
252 fatfs->fat_size = bpb->fat_size ? bpb->fat_size : bpb32->fat_size;
253 fatfs->fat_sect = bpb->reserved_sect;
254 fatfs->root_sect = fatfs->fat_sect + fatfs->fat_size * bpb->num_fats;
255 fatfs->root_size = (bpb->num_dirent * sizeof(struct fat_dirent) + 511) / 512;
256 fatfs->first_data_sect = bpb->reserved_sect + bpb->num_fats * fatfs->fat_size + fatfs->root_size;
257 fatfs->num_data_sect = fatfs->size - (bpb->reserved_sect + bpb->num_fats * fatfs->fat_size + fatfs->root_size);
258 fatfs->num_clusters = fatfs->num_data_sect / fatfs->cluster_size;
260 if(fatfs->num_clusters < 4085) {
262 } else if(fatfs->num_clusters < 65525) {
264 } else if(fatfs->num_clusters < 268435445) {
270 switch(fatfs->type) {
272 memcpy(fatfs->label, bpb16->label, sizeof bpb16->label);
277 fatfs->root_sect = bpb32->root_clust / fatfs->cluster_size;
278 fatfs->root_size = 0;
279 memcpy(fatfs->label, bpb32->label, sizeof bpb32->label);
286 endp = fatfs->label + sizeof fatfs->label - 2;
287 while(endp >= fatfs->label && isspace(*endp)) {
292 if(!(fatfs->fat = malloc(fatfs->fat_size * 512))) {
293 panic("FAT: failed to allocate memory for the FAT (%lu bytes)\n", (unsigned long)fatfs->fat_size * 512);
297 while(num_read < fatfs->fat_size) {
298 int count = fatfs->fat_size - num_read;
299 if(count > max_sect_once) count = max_sect_once;
301 read_sectors(dev, fatfs->start_sect + fatfs->fat_sect + num_read, count, ptr);
306 /* open root directory */
307 if(fatfs->type == FAT32) {
308 struct fat_dirent ent;
310 ent.first_cluster_low = bpb32->root_clust;
311 ent.first_cluster_high = bpb32->root_clust >> 16;
312 if(!(rootdir = load_dir(fatfs, &ent))) {
313 panic("FAT: failed to load FAT32 root directory\n");
317 if(!(rootdir = malloc(sizeof *rootdir))) {
318 panic("FAT: failed to allocate root directory structure\n");
320 rootdir->fatfs = fatfs;
322 rootdir->max_nent = fatfs->root_size * 512 / sizeof(struct fat_dirent);
323 if(!(rootdir->ent = malloc(fatfs->root_size * 512))) {
324 panic("FAT: failed to allocate memory for the root directory\n");
326 ptr = (char*)rootdir->ent;
329 while(num_read < fatfs->root_size) {
330 int count = fatfs->root_size - num_read;
331 if(count > max_sect_once) count = max_sect_once;
333 read_sectors(dev, fatfs->start_sect + fatfs->root_sect + num_read, count, ptr);
338 parse_dir_entries(rootdir);
341 fatfs->rootdir = rootdir;
343 /* assume cluster_size is a power of two */
344 fatfs->clust_mask = (fatfs->cluster_size * 512) - 1;
345 fatfs->clust_shift = 0;
346 while((1 << fatfs->clust_shift) < (fatfs->cluster_size * 512)) {
347 fatfs->clust_shift++;
350 /* fill generic fs structure */
351 if(!(fs = malloc(sizeof *fs))) {
352 panic("FAT: create failed to allocate memory for the filesystem structure\n");
354 fs->type = FSTYPE_FAT;
355 fs->name = fatfs->label;
356 fs->fsop = &fs_fat_ops;
360 printf("opened %s filesystem dev: %x, start: %lld\n", typestr[fatfs->type], fatfs->dev, start);
361 if(fatfs->label[0]) {
362 printf(" volume label: %s\n", fatfs->label);
368 static void destroy(struct filesys *fs)
370 struct fatfs *fatfs = fs->data;
375 static struct fs_node *open(struct filesys *fs, const char *path, unsigned int flags)
378 struct fatfs *fatfs = fs->data;
379 struct fat_dir *dir, *newdir;
380 struct fs_dirent *dent;
381 struct fat_dirent *fatdent;
382 struct fs_node *node;
385 dir = fatfs->rootdir;
386 path = fs_path_skipsep((char*)path);
388 if(cwdnode->fs->type != FSTYPE_FAT) {
396 /* we have more path components, yet the last one wasn't a dir */
401 path = fs_path_next((char*)path, name, sizeof name);
403 if(name[0] == '.' && name[1] == 0) {
407 if(!(dent = find_entry(dir, name))) {
411 fatdent = dent->data;
413 if((fatdent->first_cluster_low | fatdent->first_cluster_high) == 0) {
414 if(fatdent->attr == ATTR_DIR) {
415 /* ".." entries back to the root directory seem to have a 0
416 * cluster address as a special case
418 newdir = fatfs->rootdir;
420 return 0; /* but we can't have 0-address files (right?) */
423 newdir = fatdent->attr == ATTR_DIR ? load_dir(fatfs, fatdent) : 0;
425 if(dir != fatfs->rootdir && dir != cwdnode->data) {
432 if(!(node = malloc(sizeof *node))) {
433 panic("FAT: open failed to allocate fs_node structure\n");
437 if(dir == fatfs->rootdir) {
438 if(!(newdir = malloc(sizeof *newdir))) {
439 panic("FAT: failed to allocate directory structure\n");
445 node->type = FSNODE_DIR;
450 node->type = FSNODE_FILE;
451 if(!(node->data = init_file(fatfs, fatdent))) {
452 panic("FAT: failed to allocate file entry structure\n");
459 static void close(struct fs_node *node)
463 free_file(node->data);
467 free_dir(node->data);
471 panic("FAT: close node is not a file nor a dir\n");
477 static long fsize(struct fs_node *node)
479 struct fat_file *file;
481 if(node->type != FSNODE_FILE) {
485 return file->ent.size_bytes;
488 static int seek(struct fs_node *node, int offs, int whence)
491 struct fat_file *file;
493 unsigned int cur_clust_idx, new_clust_idx;
495 if(node->type != FSNODE_FILE) {
499 fatfs = node->fs->data;
508 new_pos = file->cur_pos + offs;
512 new_pos = file->ent.size_bytes + offs;
519 if(new_pos < 0) new_pos = 0;
521 cur_clust_idx = file->cur_pos >> fatfs->clust_shift;
522 new_clust_idx = new_pos >> fatfs->clust_shift;
523 /* if the new position does not fall in the same cluster as the previous one
524 * re-calculate cur_clust
526 if(new_clust_idx != cur_clust_idx) {
527 if(new_clust_idx < cur_clust_idx) {
528 file->cur_clust = find_cluster(fatfs, new_clust_idx, file->first_clust);
530 file->cur_clust = find_cluster(fatfs, new_clust_idx - cur_clust_idx, file->cur_clust);
534 file->cur_pos = new_pos;
538 static long tell(struct fs_node *node)
540 struct fat_file *file;
542 if(!node || node->type != FSNODE_FILE) {
547 return file->cur_pos;
550 static int read(struct fs_node *node, void *buf, int sz)
553 struct fat_file *file;
556 int offs, len, buf_left, rd_left;
557 unsigned int cur_clust_idx, new_clust_idx;
559 if(!node || !buf || sz < 0 || node->type != FSNODE_FILE) {
563 fatfs = node->fs->data;
566 if(file->cur_clust < 0) {
570 cur_clust_idx = file->cur_pos >> fatfs->clust_shift;
572 while(num_read < sz) {
573 if(!file->buf_valid) {
574 read_cluster(fatfs, file->cur_clust, file->clustbuf);
578 offs = file->cur_pos & fatfs->clust_mask;
579 buf_left = fatfs->cluster_size * 512 - offs;
580 rd_left = sz - num_read;
581 len = buf_left < rd_left ? buf_left : rd_left;
583 if(file->cur_pos + len > file->ent.size_bytes) {
584 len = file->ent.size_bytes - file->cur_pos;
587 memcpy(bufptr, file->clustbuf + offs, len);
591 file->cur_pos += len;
592 if(file->cur_pos >= file->ent.size_bytes) {
593 file->cur_clust = -1;
595 break; /* reached EOF */
598 new_clust_idx = file->cur_pos >> fatfs->clust_shift;
599 if(new_clust_idx != cur_clust_idx) {
601 if((file->cur_clust = next_cluster(fatfs, file->cur_clust)) < 0) {
602 break; /* reached EOF */
604 cur_clust_idx = new_clust_idx;
610 static int write(struct fs_node *node, void *buf, int sz)
612 return -1; /* TODO */
615 static int rewinddir(struct fs_node *node)
619 if(node->type != FSNODE_DIR) {
628 static struct fs_dirent *readdir(struct fs_node *node)
632 if(node->type != FSNODE_DIR) {
637 if(dir->cur_ent >= dir->fsent_size) {
641 return dir->fsent + dir->cur_ent++;
644 static int rename(struct fs_node *node, const char *name)
646 return -1; /* TODO */
649 static int remove(struct fs_node *node)
652 return -1; /* TODO */
655 static struct fat_dir *load_dir(struct fatfs *fs, struct fat_dirent *dent)
662 if(dent->attr != ATTR_DIR) return 0;
664 addr = dent->first_cluster_low;
665 if(fs->type >= FAT32) {
666 addr |= (uint32_t)dent->first_cluster_high << 16;
671 bufsz += fs->cluster_size * 512;
672 if(!(buf = realloc(buf, bufsz))) {
673 panic("FAT: failed to allocate cluster buffer (%d bytes)\n", bufsz);
676 if(read_cluster(fs, addr, buf + prevsz) == -1) {
677 printf("load_dir: failed to read cluster: %lu\n", (unsigned long)addr);
681 } while((addr = next_cluster(fs, addr)) >= 0);
683 if(!(dir = malloc(sizeof *dir))) {
684 panic("FAT: failed to allocate directory structure\n");
687 dir->ent = (struct fat_dirent*)buf;
688 dir->max_nent = bufsz / sizeof *dir->ent;
692 parse_dir_entries(dir);
696 static void parse_dir_entries(struct fat_dir *dir)
699 struct fat_dirent *dent, *prev_dent;
700 struct fs_dirent *eptr;
701 char entname[MAX_NAME];
703 /* create an fs_dirent array with one element for each actual entry
704 * (disregarding volume labels, and LFN entries).
706 if(!(dir->fsent = malloc(dir->max_nent * sizeof *dir->fsent))) {
707 panic("FAT: failed to allocate dirent array\n");
711 prev_dent = dent - 1;
713 for(i=0; i<dir->max_nent; i++) {
714 if(DENT_IS_NULL(dent)) break;
716 if(!DENT_IS_UNUSED(dent) && dent->attr != ATTR_VOLID && dent->attr != ATTR_LFN) {
717 if(dent_filename(dent, prev_dent, entname) > 0) {
718 if(!(eptr->name = malloc(strlen(entname) + 1))) {
719 panic("FAT: failed to allocate dirent name\n");
721 strcpy(eptr->name, entname);
723 eptr->type = dent->attr == ATTR_DIR ? FSNODE_DIR : FSNODE_FILE;
724 eptr->fsize = dent->size_bytes;
728 if(dent->attr != ATTR_LFN) {
733 dir->fsent_size = eptr - dir->fsent;
737 static void free_dir(struct fat_dir *dir)
740 struct fat_dir *root = dir->fatfs->rootdir;
743 if(--dir->ref > 0) return;
745 if(dir->ent != root->ent) {
748 for(i=0; i<dir->fsent_size; i++) {
749 free(dir->fsent[i].name);
758 static struct fat_file *init_file(struct fatfs *fatfs, struct fat_dirent *dent)
760 struct fat_file *file;
762 if(!(file = calloc(1, sizeof *file))) {
763 panic("FAT: failed to allocate file structure\n");
765 if(!(file->clustbuf = malloc(fatfs->cluster_size * 512))) {
766 panic("FAT: failed to allocate file cluster buffer\n");
769 file->first_clust = dent->first_cluster_low | ((int32_t)dent->first_cluster_high << 16);
770 file->cur_clust = file->first_clust;
774 static void free_file(struct fat_file *file)
777 free(file->clustbuf);
782 static int read_sectors(int dev, uint64_t sidx, int count, void *sect)
784 if(dev == -1 || dev == boot_drive_number) {
785 if(bdev_read_range(sidx, count, sect) == -1) {
791 printf("BUG: reading sectors from drives other than the boot drive not implemented yet\n");
795 static int read_cluster(struct fatfs *fatfs, uint32_t addr, void *clust)
798 uint64_t saddr = (uint64_t)(addr - 2) * fatfs->cluster_size + fatfs->first_data_sect + fatfs->start_sect;
800 if(read_sectors(fatfs->dev, saddr, fatfs->cluster_size, ptr) == -1) {
806 static int dent_filename(struct fat_dirent *dent, struct fat_dirent *prev, char *buf)
810 struct fat_lfnent *lfn = (struct fat_lfnent*)(dent - 1);
812 if(lfn > (struct fat_lfnent*)prev && lfn->attr == ATTR_LFN) {
813 /* found a long filename entry, use that */
815 uint16_t ustr[14], *uptr = ustr;
816 memcpy(uptr, lfn->part1, sizeof lfn->part1);
817 uptr += sizeof lfn->part1 / sizeof *lfn->part1;
818 memcpy(uptr, lfn->part2, sizeof lfn->part2);
819 uptr += sizeof lfn->part2 / sizeof *lfn->part2;
820 memcpy(uptr, lfn->part3, sizeof lfn->part3);
825 *ptr++ = *(char*)uptr++;
830 if(uptr < ustr + 13 || (lfn->seq & 0xf0) == 0x40) break;
832 } while(lfn > (struct fat_lfnent*)prev && lfn->attr == ATTR_LFN);
835 /* regular 8.3 filename */
836 memcpy(buf, dent->name, 8);
839 if(!buf[0]) return 0;
841 if(dent->name[8] && dent->name[8] != ' ') {
842 ptr = buf + strlen(buf);
844 memcpy(ptr, dent->name + 8, 3);
854 static struct fs_dirent *find_entry(struct fat_dir *dir, const char *name)
857 struct fs_dirent *dent = dir->fsent;
859 for(i=0; i<dir->fsent_size; i++) {
860 if(strcasecmp(dent->name, name) == 0) {
868 static uint32_t read_fat(struct fatfs *fatfs, uint32_t addr)
870 uint32_t res = 0xffffffff;
872 switch(fatfs->type) {
875 uint32_t idx = addr + addr / 2;
876 res = ((uint16_t*)fatfs->fat)[idx];
879 res >>= 4; /* odd entries end up on the high 12 bits */
881 res &= 0xfff; /* even entries end up on the low 12 bits */
887 res = ((uint16_t*)fatfs->fat)[addr];
892 res = ((uint32_t*)fatfs->fat)[addr];
902 static int32_t next_cluster(struct fatfs *fatfs, int32_t addr)
904 uint32_t fatval = read_fat(fatfs, addr);
906 if(fatval == 0) return -1;
908 switch(fatfs->type) {
910 if(fatval >= 0xff8) return -1;
914 if(fatval >= 0xfff8) return -1;
918 case EXFAT: /* XXX ? */
919 if(fatval >= 0xffffff8) return -1;
929 static int32_t find_cluster(struct fatfs *fatfs, int count, int32_t clust)
931 while(count-- > 0 && (clust = next_cluster(fatfs, clust)) >= 0);
936 static void dbg_printdir(struct fat_dirent *dir, int max_entries)
939 struct fat_dirent *prev = dir - 1;
940 struct fat_dirent *end = max_entries > 0 ? dir + max_entries : 0;
942 while(!DENT_IS_NULL(dir) && (!end || dir < end)) {
943 if(!DENT_IS_UNUSED(dir) && dir->attr != ATTR_VOLID && dir->attr != ATTR_LFN) {
944 if(dent_filename(dir, prev, name) > 0) {
945 printf("%s%c\n", name, dir->attr == ATTR_DIR ? '/' : ' ');
948 if(dir->attr != ATTR_LFN) {
956 static void clean_trailws(char *s)
960 if(!s || !*s) return;
962 p = s + strlen(s) - 1;
963 while(p >= s && isspace(*p)) p--;