Fixed some Task stuff, added DOSFS filesystem
This commit is contained in:
parent
6ebee28032
commit
9216b3359a
6
Makefile
6
Makefile
@ -1,11 +1,11 @@
|
|||||||
objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o
|
objects = entry.o kernel.o task.o handler.o interrupt.o v86.o print.o tss.o dosfs/dosfs.o
|
||||||
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding -march=pentium-m -fno-stack-protector -nostdlib -c
|
CFLAGS = -target "i686-elf" -m32 -mgeneral-regs-only -ffreestanding -march=pentium-m -fno-stack-protector -nostdlib -c
|
||||||
|
|
||||||
%.o: %.nasm
|
%.o: %.nasm
|
||||||
nasm -f elf32 -o $@ $<
|
nasm -f elf32 -o $@ $<
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
clang $(CFLAGS) -O2 $<
|
clang $(CFLAGS) -O2 -o $@ $<
|
||||||
|
|
||||||
all: $(objects)
|
all: $(objects)
|
||||||
nasm boot.nasm -o boot.bin
|
nasm boot.nasm -o boot.bin
|
||||||
@ -17,3 +17,5 @@ virtdisk:
|
|||||||
dd bs=1M count=32 if=/dev/zero of=virtdisk.bin
|
dd bs=1M count=32 if=/dev/zero of=virtdisk.bin
|
||||||
echo -n -e '\x55\xaa' | dd bs=1 seek=510 conv=notrunc of=virtdisk.bin
|
echo -n -e '\x55\xaa' | dd bs=1 seek=510 conv=notrunc of=virtdisk.bin
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm $(objects)
|
||||||
|
@ -26,6 +26,6 @@ string: db 'DISK ERROR'
|
|||||||
|
|
||||||
addr_packet:
|
addr_packet:
|
||||||
db 0x10, 0x00 ; size, reserved
|
db 0x10, 0x00 ; size, reserved
|
||||||
dw 0x20 ; blocks
|
dw 0x39 ; blocks
|
||||||
dd 0x8000 ; transfer buffer
|
dd 0x8000 ; transfer buffer
|
||||||
dq 1 ; start block
|
dq 1 ; start block
|
||||||
|
1273
dosfs/dosfs.c
Executable file
1273
dosfs/dosfs.c
Executable file
File diff suppressed because it is too large
Load Diff
374
dosfs/dosfs.h
Executable file
374
dosfs/dosfs.h
Executable file
@ -0,0 +1,374 @@
|
|||||||
|
/*
|
||||||
|
DOSFS Embedded FAT-Compatible Filesystem
|
||||||
|
(C) 2005 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _DOSFS_H
|
||||||
|
#define _DOSFS_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// User-supplied functions
|
||||||
|
uint32_t DFS_ReadSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||||
|
uint32_t DFS_WriteSector(uint8_t unit, uint8_t *buffer, uint32_t sector, uint32_t count);
|
||||||
|
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Configurable items
|
||||||
|
#define MAX_PATH 64 // Maximum path length (increasing this will
|
||||||
|
// GREATLY increase stack requirements!)
|
||||||
|
#define DIR_SEPARATOR '/' // character separating directory components
|
||||||
|
|
||||||
|
// End of configurable items
|
||||||
|
//===================================================================
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// 32-bit error codes
|
||||||
|
#define DFS_OK 0 // no error
|
||||||
|
#define DFS_EOF 1 // end of file (not an error)
|
||||||
|
#define DFS_WRITEPROT 2 // volume is write protected
|
||||||
|
#define DFS_NOTFOUND 3 // path or file not found
|
||||||
|
#define DFS_PATHLEN 4 // path too long
|
||||||
|
#define DFS_ALLOCNEW 5 // must allocate new directory cluster
|
||||||
|
#define DFS_ERRMISC 0xffffffff // generic error
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// File access modes
|
||||||
|
#define DFS_READ 1 // read-only
|
||||||
|
#define DFS_WRITE 2 // write-only
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Miscellaneous constants
|
||||||
|
#define SECTOR_SIZE 512 // sector size in bytes
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// Internal subformat identifiers
|
||||||
|
#define FAT12 0
|
||||||
|
#define FAT16 1
|
||||||
|
#define FAT32 2
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
// DOS attribute bits
|
||||||
|
#define ATTR_READ_ONLY 0x01
|
||||||
|
#define ATTR_HIDDEN 0x02
|
||||||
|
#define ATTR_SYSTEM 0x04
|
||||||
|
#define ATTR_VOLUME_ID 0x08
|
||||||
|
#define ATTR_DIRECTORY 0x10
|
||||||
|
#define ATTR_ARCHIVE 0x20
|
||||||
|
#define ATTR_LONG_NAME (ATTR_READ_ONLY | ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME_ID)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Directory entry structure
|
||||||
|
note: if name[0] == 0xe5, this is a free dir entry
|
||||||
|
if name[0] == 0x00, this is a free entry and all subsequent entries are free
|
||||||
|
if name[0] == 0x05, the first character of the name is 0xe5 [a kanji nicety]
|
||||||
|
|
||||||
|
Date format: bit 0-4 = day of month (1-31)
|
||||||
|
bit 5-8 = month, 1=Jan..12=Dec
|
||||||
|
bit 9-15 = count of years since 1980 (0-127)
|
||||||
|
Time format: bit 0-4 = 2-second count, (0-29)
|
||||||
|
bit 5-10 = minutes (0-59)
|
||||||
|
bit 11-15= hours (0-23)
|
||||||
|
*/
|
||||||
|
typedef struct _tagDIRENT {
|
||||||
|
uint8_t name[11]; // filename
|
||||||
|
uint8_t attr; // attributes (see ATTR_* constant definitions)
|
||||||
|
uint8_t reserved; // reserved, must be 0
|
||||||
|
uint8_t crttimetenth; // create time, 10ths of a second (0-199 are valid)
|
||||||
|
uint8_t crttime_l; // creation time low byte
|
||||||
|
uint8_t crttime_h; // creation time high byte
|
||||||
|
uint8_t crtdate_l; // creation date low byte
|
||||||
|
uint8_t crtdate_h; // creation date high byte
|
||||||
|
uint8_t lstaccdate_l; // last access date low byte
|
||||||
|
uint8_t lstaccdate_h; // last access date high byte
|
||||||
|
uint8_t startclus_h_l; // high word of first cluster, low byte (FAT32)
|
||||||
|
uint8_t startclus_h_h; // high word of first cluster, high byte (FAT32)
|
||||||
|
uint8_t wrttime_l; // last write time low byte
|
||||||
|
uint8_t wrttime_h; // last write time high byte
|
||||||
|
uint8_t wrtdate_l; // last write date low byte
|
||||||
|
uint8_t wrtdate_h; // last write date high byte
|
||||||
|
uint8_t startclus_l_l; // low word of first cluster, low byte
|
||||||
|
uint8_t startclus_l_h; // low word of first cluster, high byte
|
||||||
|
uint8_t filesize_0; // file size, low byte
|
||||||
|
uint8_t filesize_1; //
|
||||||
|
uint8_t filesize_2; //
|
||||||
|
uint8_t filesize_3; // file size, high byte
|
||||||
|
} DIRENT, *PDIRENT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Partition table entry structure
|
||||||
|
*/
|
||||||
|
typedef struct _tagPTINFO {
|
||||||
|
uint8_t active; // 0x80 if partition active
|
||||||
|
uint8_t start_h; // starting head
|
||||||
|
uint8_t start_cs_l; // starting cylinder and sector (low byte)
|
||||||
|
uint8_t start_cs_h; // starting cylinder and sector (high byte)
|
||||||
|
uint8_t type; // type ID byte
|
||||||
|
uint8_t end_h; // ending head
|
||||||
|
uint8_t end_cs_l; // ending cylinder and sector (low byte)
|
||||||
|
uint8_t end_cs_h; // ending cylinder and sector (high byte)
|
||||||
|
uint8_t start_0; // starting sector# (low byte)
|
||||||
|
uint8_t start_1; //
|
||||||
|
uint8_t start_2; //
|
||||||
|
uint8_t start_3; // starting sector# (high byte)
|
||||||
|
uint8_t size_0; // size of partition (low byte)
|
||||||
|
uint8_t size_1; //
|
||||||
|
uint8_t size_2; //
|
||||||
|
uint8_t size_3; // size of partition (high byte)
|
||||||
|
} PTINFO, *PPTINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Master Boot Record structure
|
||||||
|
*/
|
||||||
|
typedef struct _tagMBR {
|
||||||
|
uint8_t bootcode[0x1be]; // boot sector
|
||||||
|
PTINFO ptable[4]; // four partition table structures
|
||||||
|
uint8_t sig_55; // 0x55 signature byte
|
||||||
|
uint8_t sig_aa; // 0xaa signature byte
|
||||||
|
} MBR, *PMBR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
BIOS Parameter Block structure (FAT12/16)
|
||||||
|
*/
|
||||||
|
typedef struct _tagBPB {
|
||||||
|
uint8_t bytepersec_l; // bytes per sector low byte (0x00)
|
||||||
|
uint8_t bytepersec_h; // bytes per sector high byte (0x02)
|
||||||
|
uint8_t secperclus; // sectors per cluster (1,2,4,8,16,32,64,128 are valid)
|
||||||
|
uint8_t reserved_l; // reserved sectors low byte
|
||||||
|
uint8_t reserved_h; // reserved sectors high byte
|
||||||
|
uint8_t numfats; // number of FAT copies (2)
|
||||||
|
uint8_t rootentries_l; // number of root dir entries low byte (0x00 normally)
|
||||||
|
uint8_t rootentries_h; // number of root dir entries high byte (0x02 normally)
|
||||||
|
uint8_t sectors_s_l; // small num sectors low byte
|
||||||
|
uint8_t sectors_s_h; // small num sectors high byte
|
||||||
|
uint8_t mediatype; // media descriptor byte
|
||||||
|
uint8_t secperfat_l; // sectors per FAT low byte
|
||||||
|
uint8_t secperfat_h; // sectors per FAT high byte
|
||||||
|
uint8_t secpertrk_l; // sectors per track low byte
|
||||||
|
uint8_t secpertrk_h; // sectors per track high byte
|
||||||
|
uint8_t heads_l; // heads low byte
|
||||||
|
uint8_t heads_h; // heads high byte
|
||||||
|
uint8_t hidden_0; // hidden sectors low byte
|
||||||
|
uint8_t hidden_1; // (note - this is the number of MEDIA sectors before
|
||||||
|
uint8_t hidden_2; // first sector of VOLUME - we rely on the MBR instead)
|
||||||
|
uint8_t hidden_3; // hidden sectors high byte
|
||||||
|
uint8_t sectors_l_0; // large num sectors low byte
|
||||||
|
uint8_t sectors_l_1; //
|
||||||
|
uint8_t sectors_l_2; //
|
||||||
|
uint8_t sectors_l_3; // large num sectors high byte
|
||||||
|
} BPB, *PBPB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extended BIOS Parameter Block structure (FAT12/16)
|
||||||
|
*/
|
||||||
|
typedef struct _tagEBPB {
|
||||||
|
uint8_t unit; // int 13h drive#
|
||||||
|
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||||
|
uint8_t signature; // 0x28 or 0x29
|
||||||
|
uint8_t serial_0; // serial#
|
||||||
|
uint8_t serial_1; // serial#
|
||||||
|
uint8_t serial_2; // serial#
|
||||||
|
uint8_t serial_3; // serial#
|
||||||
|
uint8_t label[11]; // volume label
|
||||||
|
uint8_t system[8]; // filesystem ID
|
||||||
|
} EBPB, *PEBPB;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extended BIOS Parameter Block structure (FAT32)
|
||||||
|
*/
|
||||||
|
typedef struct _tagEBPB32 {
|
||||||
|
uint8_t fatsize_0; // big FAT size in sectors low byte
|
||||||
|
uint8_t fatsize_1; //
|
||||||
|
uint8_t fatsize_2; //
|
||||||
|
uint8_t fatsize_3; // big FAT size in sectors high byte
|
||||||
|
uint8_t extflags_l; // extended flags low byte
|
||||||
|
uint8_t extflags_h; // extended flags high byte
|
||||||
|
uint8_t fsver_l; // filesystem version (0x00) low byte
|
||||||
|
uint8_t fsver_h; // filesystem version (0x00) high byte
|
||||||
|
uint8_t root_0; // cluster of root dir, low byte
|
||||||
|
uint8_t root_1; //
|
||||||
|
uint8_t root_2; //
|
||||||
|
uint8_t root_3; // cluster of root dir, high byte
|
||||||
|
uint8_t fsinfo_l; // sector pointer to FSINFO within reserved area, low byte (2)
|
||||||
|
uint8_t fsinfo_h; // sector pointer to FSINFO within reserved area, high byte (0)
|
||||||
|
uint8_t bkboot_l; // sector pointer to backup boot sector within reserved area, low byte (6)
|
||||||
|
uint8_t bkboot_h; // sector pointer to backup boot sector within reserved area, high byte (0)
|
||||||
|
uint8_t reserved[12]; // reserved, should be 0
|
||||||
|
|
||||||
|
uint8_t unit; // int 13h drive#
|
||||||
|
uint8_t head; // archaic, used by Windows NT-class OSes for flags
|
||||||
|
uint8_t signature; // 0x28 or 0x29
|
||||||
|
uint8_t serial_0; // serial#
|
||||||
|
uint8_t serial_1; // serial#
|
||||||
|
uint8_t serial_2; // serial#
|
||||||
|
uint8_t serial_3; // serial#
|
||||||
|
uint8_t label[11]; // volume label
|
||||||
|
uint8_t system[8]; // filesystem ID
|
||||||
|
} EBPB32, *PEBPB32;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Logical Boot Record structure (volume boot sector)
|
||||||
|
*/
|
||||||
|
typedef struct _tagLBR {
|
||||||
|
uint8_t jump[3]; // JMP instruction
|
||||||
|
uint8_t oemid[8]; // OEM ID, space-padded
|
||||||
|
BPB bpb; // BIOS Parameter Block
|
||||||
|
union {
|
||||||
|
EBPB ebpb; // FAT12/16 Extended BIOS Parameter Block
|
||||||
|
EBPB32 ebpb32; // FAT32 Extended BIOS Parameter Block
|
||||||
|
} ebpb;
|
||||||
|
uint8_t code[420]; // boot sector code
|
||||||
|
uint8_t sig_55; // 0x55 signature byte
|
||||||
|
uint8_t sig_aa; // 0xaa signature byte
|
||||||
|
} LBR, *PLBR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Volume information structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagVOLINFO {
|
||||||
|
uint8_t unit; // unit on which this volume resides
|
||||||
|
uint8_t filesystem; // formatted filesystem
|
||||||
|
|
||||||
|
// These two fields aren't very useful, so support for them has been commented out to
|
||||||
|
// save memory. (Note that the "system" tag is not actually used by DOS to determine
|
||||||
|
// filesystem type - that decision is made entirely on the basis of how many clusters
|
||||||
|
// the drive contains. DOSFS works the same way).
|
||||||
|
// See tag: OEMID in dosfs.c
|
||||||
|
// uint8_t oemid[9]; // OEM ID ASCIIZ
|
||||||
|
// uint8_t system[9]; // system ID ASCIIZ
|
||||||
|
uint8_t label[12]; // volume label ASCIIZ
|
||||||
|
uint32_t startsector; // starting sector of filesystem
|
||||||
|
uint8_t secperclus; // sectors per cluster
|
||||||
|
uint16_t reservedsecs; // reserved sectors
|
||||||
|
uint32_t numsecs; // number of sectors in volume
|
||||||
|
uint32_t secperfat; // sectors per FAT
|
||||||
|
uint16_t rootentries; // number of root dir entries
|
||||||
|
|
||||||
|
uint32_t numclusters; // number of clusters on drive
|
||||||
|
|
||||||
|
// The fields below are PHYSICAL SECTOR NUMBERS.
|
||||||
|
uint32_t fat1; // starting sector# of FAT copy 1
|
||||||
|
uint32_t rootdir; // starting sector# of root directory (FAT12/FAT16) or cluster (FAT32)
|
||||||
|
uint32_t dataarea; // starting sector# of data area (cluster #2)
|
||||||
|
} VOLINFO, *PVOLINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Flags in DIRINFO.flags
|
||||||
|
*/
|
||||||
|
#define DFS_DI_BLANKENT 0x01 // Searching for blank entry
|
||||||
|
|
||||||
|
/*
|
||||||
|
Directory search structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagDIRINFO {
|
||||||
|
uint32_t currentcluster; // current cluster in dir
|
||||||
|
uint8_t currentsector; // current sector in cluster
|
||||||
|
uint8_t currententry; // current dir entry in sector
|
||||||
|
uint8_t *scratch; // ptr to user-supplied scratch buffer (one sector)
|
||||||
|
uint8_t flags; // internal DOSFS flags
|
||||||
|
} DIRINFO, *PDIRINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
File handle structure (Internal to DOSFS)
|
||||||
|
*/
|
||||||
|
typedef struct _tagFILEINFO {
|
||||||
|
PVOLINFO volinfo; // VOLINFO used to open this file
|
||||||
|
uint32_t dirsector; // physical sector containing dir entry of this file
|
||||||
|
uint8_t diroffset; // # of this entry within the dir sector
|
||||||
|
uint8_t mode; // mode in which this file was opened
|
||||||
|
uint32_t firstcluster; // first cluster of file
|
||||||
|
uint32_t filelen; // byte length of file
|
||||||
|
|
||||||
|
uint32_t cluster; // current cluster
|
||||||
|
uint32_t pointer; // current (BYTE) pointer
|
||||||
|
} FILEINFO, *PFILEINFO;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get starting sector# of specified partition on drive #unit
|
||||||
|
NOTE: This code ASSUMES an MBR on the disk.
|
||||||
|
scratchsector should point to a SECTOR_SIZE scratch area
|
||||||
|
Returns 0xffffffff for any error.
|
||||||
|
If pactive is non-NULL, this function also returns the partition active flag.
|
||||||
|
If pptype is non-NULL, this function also returns the partition type.
|
||||||
|
If psize is non-NULL, this function also returns the partition size.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetPtnStart(uint8_t unit, uint8_t *scratchsector, uint8_t pnum, uint8_t *pactive, uint8_t *pptype, uint32_t *psize);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Retrieve volume info from BPB and store it in a VOLINFO structure
|
||||||
|
You must provide the unit and starting sector of the filesystem, and
|
||||||
|
a pointer to a sector buffer for scratch
|
||||||
|
Attempts to read BPB and glean information about the FS from that.
|
||||||
|
Returns 0 OK, nonzero for any error.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetVolInfo(uint8_t unit, uint8_t *scratchsector, uint32_t startsector, PVOLINFO volinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Open a directory for enumeration by DFS_GetNextDirEnt
|
||||||
|
You must supply a populated VOLINFO (see DFS_GetVolInfo)
|
||||||
|
The empty string or a string containing only the directory separator are
|
||||||
|
considered to be the root directory.
|
||||||
|
Returns 0 OK, nonzero for any error.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_OpenDir(PVOLINFO volinfo, uint8_t *dirname, PDIRINFO dirinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Get next entry in opened directory structure. Copies fields into the dirent
|
||||||
|
structure, updates dirinfo. Note that it is the _caller's_ responsibility to
|
||||||
|
handle the '.' and '..' entries.
|
||||||
|
A deleted file will be returned as a NULL entry (first char of filename=0)
|
||||||
|
by this code. Filenames beginning with 0x05 will be translated to 0xE5
|
||||||
|
automatically. Long file name entries will be returned as NULL.
|
||||||
|
returns DFS_EOF if there are no more entries, DFS_OK if this entry is valid,
|
||||||
|
or DFS_ERRMISC for a media error
|
||||||
|
*/
|
||||||
|
uint32_t DFS_GetNext(PVOLINFO volinfo, PDIRINFO dirinfo, PDIRENT dirent);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Open a file for reading or writing. You supply populated VOLINFO, a path to the file,
|
||||||
|
mode (DFS_READ or DFS_WRITE) and an empty fileinfo structure. You also need to
|
||||||
|
provide a pointer to a sector-sized scratch buffer.
|
||||||
|
Returns various DFS_* error states. If the result is DFS_OK, fileinfo can be used
|
||||||
|
to access the file from this point on.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_OpenFile(PVOLINFO volinfo, uint8_t *path, uint8_t mode, uint8_t *scratch, PFILEINFO fileinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Read an open file
|
||||||
|
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||||
|
pointer to a SECTOR_SIZE scratch buffer.
|
||||||
|
Note that returning DFS_EOF is not an error condition. This function updates the
|
||||||
|
successcount field with the number of bytes actually read.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_ReadFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Write an open file
|
||||||
|
You must supply a prepopulated FILEINFO as provided by DFS_OpenFile, and a
|
||||||
|
pointer to a SECTOR_SIZE scratch buffer.
|
||||||
|
This function updates the successcount field with the number of bytes actually written.
|
||||||
|
*/
|
||||||
|
uint32_t DFS_WriteFile(PFILEINFO fileinfo, uint8_t *scratch, uint8_t *buffer, uint32_t *successcount, uint32_t len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Seek file pointer to a given position
|
||||||
|
This function does not return status - refer to the fileinfo->pointer value
|
||||||
|
to see where the pointer wound up.
|
||||||
|
Requires a SECTOR_SIZE scratch buffer
|
||||||
|
*/
|
||||||
|
void DFS_Seek(PFILEINFO fileinfo, uint32_t offset, uint8_t *scratch);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Delete a file
|
||||||
|
scratch must point to a sector-sized buffer
|
||||||
|
*/
|
||||||
|
uint32_t DFS_UnlinkFile(PVOLINFO volinfo, uint8_t *path, uint8_t *scratch);
|
||||||
|
|
||||||
|
// If we are building a host-emulation version, include host support
|
||||||
|
#ifdef HOSTVER
|
||||||
|
#include "hostemu.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _DOSFS_H
|
366
dosfs/readme.txt
Executable file
366
dosfs/readme.txt
Executable file
@ -0,0 +1,366 @@
|
|||||||
|
README.TXT (C) Copyright 2006
|
||||||
|
DOSFS Level 1 Version 1.02 Lewin A.R.W. Edwards (sysadm@zws.com)
|
||||||
|
=====================================================================
|
||||||
|
|
||||||
|
Abstract
|
||||||
|
========
|
||||||
|
DOSFS is a FAT-compatible filesystem intended for fairly low-end
|
||||||
|
embedded applications. It is not the leanest possible implementation
|
||||||
|
(the leanest FAT implementations operate in << 512 bytes of RAM, with
|
||||||
|
heavy restrictions). This code strikes a good balance between size
|
||||||
|
and functionality, with an emphasis on RAM footprint.
|
||||||
|
|
||||||
|
Intended target systems would be in the ballpark of 1K RAM, 4K ROM
|
||||||
|
or more.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* Supports FAT12, FAT16 and FAT32 volumes
|
||||||
|
* Supports storage devices up to 2048Gbytes in size (LBA32)
|
||||||
|
* Supports devices with or without MBRs (hard disks vs. floppy disks
|
||||||
|
or ZIP drives formatted as "big floppies")
|
||||||
|
* Supports multiple partitions on disks with MBRs
|
||||||
|
* Supports subdirectories
|
||||||
|
* Can be operated with a single global 512-byte sector buffer
|
||||||
|
* Fully reentrant code (assuming the underlying physical device driver
|
||||||
|
is reentrant and global sector buffers are not used). There are no
|
||||||
|
global variables in the filesystem
|
||||||
|
* Does not perform any memory allocation
|
||||||
|
* Partial support for random-access files
|
||||||
|
|
||||||
|
Applications:
|
||||||
|
* Firmware upgrades
|
||||||
|
* Failsafe IPL
|
||||||
|
* Media playback
|
||||||
|
* Data logging
|
||||||
|
* Configuration storage
|
||||||
|
|
||||||
|
There is no technical support for this free product; however, if you
|
||||||
|
have questions or suggestions, you are encouraged to email Lewin
|
||||||
|
Edwards at sysadm@zws.com. If you need custom additions to the code,
|
||||||
|
or if you have other projects for which you need engineering
|
||||||
|
assistance, please feel free to email or call (646) 549-3715.
|
||||||
|
|
||||||
|
License
|
||||||
|
=======
|
||||||
|
The license for DOSFS is very simple but verbose to state.
|
||||||
|
|
||||||
|
1. DOSFS is (C) Copyright 2006 by Lewin A.R.W. Edwards ("Author").
|
||||||
|
All rights not explicitly granted herein are reserved. The DOSFS
|
||||||
|
code is the permanent property of the Author and no transfer of
|
||||||
|
ownership is implied by this license.
|
||||||
|
|
||||||
|
2. DOSFS is an educational project, provided as-is. No guarantee of
|
||||||
|
performance or suitability for any application is stated or
|
||||||
|
implied. You use this product entirely at your own risk. Use of
|
||||||
|
this product in any manner automatically waives any right to seek
|
||||||
|
compensation or damages of any sort from the Author. Since the
|
||||||
|
products you might make are entirely out of the Author's control,
|
||||||
|
use of this product also constitutes an agreement by you to take
|
||||||
|
full responsibility for and indemnify the Author against any
|
||||||
|
action for any loss or damage (including economic loss of any
|
||||||
|
type, and specifically including patent litigation) that arises
|
||||||
|
from a product made by you that incorporates any portion of
|
||||||
|
the DOSFS code.
|
||||||
|
|
||||||
|
3. If you live under the jurisdiction of any legislation that would
|
||||||
|
prohibit or limit any condition in this license, you cannot be
|
||||||
|
licensed to use this product.
|
||||||
|
|
||||||
|
4. If you do not fall into the excluded category in point 3, you are
|
||||||
|
hereby licensed to use the DOSFS code in any application that you
|
||||||
|
see fit. You are not required to pay any fee or notify the Author
|
||||||
|
that you are using DOSFS. Any modifications made by you to the
|
||||||
|
DOSFS code are your property and you may distribute the modified
|
||||||
|
version in any manner that you wish. You are not required to
|
||||||
|
disclose sourcecode to such modifications, either to the Author or
|
||||||
|
to any third party. Any such disclosure made to the Author will
|
||||||
|
irrevocably become the property of the Author in the absence of a
|
||||||
|
formal agreement to the contrary, established prior to such
|
||||||
|
disclosure being made.
|
||||||
|
|
||||||
|
To summarize the intent of the above: DOSFS is free. You can do what
|
||||||
|
you want with it. Anything that happens as a result is entirely your
|
||||||
|
responsibility. You can't take ownership of my code and stop me from
|
||||||
|
doing whatever I want with it. If you do something nifty with DOSFS
|
||||||
|
and send me the sourcecode, I may include your changes in the next
|
||||||
|
distribution and it will be released to the world as free software.
|
||||||
|
If someone sues you because your DOSFS-containing product causes
|
||||||
|
any sort of legal, financial or other problem, it's your lawsuit,
|
||||||
|
not mine, and you'll exclude me from the proceedings.
|
||||||
|
|
||||||
|
User-Supplied Functions
|
||||||
|
=======================
|
||||||
|
You must provide functions to read sectors into memory and write
|
||||||
|
them back to the target media. The demo suite includes an emulation
|
||||||
|
module that reads/writes a disk image file (#define HOSTVER pulls
|
||||||
|
in hostemu.h which wraps the prototypes for these functions).
|
||||||
|
There are various tools for UNIX, DOS, Windows et al, to create
|
||||||
|
images from storage media; my preferred utility is dd.
|
||||||
|
|
||||||
|
The functions you must supply in your embedded app are:
|
||||||
|
|
||||||
|
DFS_ReadSector(unit,buffer,sector,count)
|
||||||
|
DFS_WriteSector(unit,buffer,sector,count)
|
||||||
|
|
||||||
|
These two functions read and write, respectively, "count" sectors of
|
||||||
|
size SECTOR_SIZE (512 bytes; see below) from/to physical sector
|
||||||
|
#"sector" of device "unit", to/from the scratch buffer "buffer". They
|
||||||
|
should return 0 for success or nonzero for failure. In the current
|
||||||
|
implementation of DOSFS, count will always be 1.
|
||||||
|
|
||||||
|
The "unit" argument is designed to permit implementation of multiple
|
||||||
|
storage devices, for example multiple media slots on a single device,
|
||||||
|
or to differentiate between master and slave devices on an ATAPI bus.
|
||||||
|
|
||||||
|
This code is designed for 512-byte sectors. Although the sector size
|
||||||
|
is a #define, you should not tinker with it because the vast majority
|
||||||
|
of FAT filesystems use 512-byte sectors, and the DOSFS code doesn't
|
||||||
|
support runtime determination of sector size. This will not affect the
|
||||||
|
vast majority of users.
|
||||||
|
|
||||||
|
Example Code
|
||||||
|
============
|
||||||
|
Refer to the tests in main.c to see how to call DOSFS functions.
|
||||||
|
(These tests are all commented out). Note that the only two files
|
||||||
|
you need to add to your project are dosfs.c and dosfs.h.
|
||||||
|
|
||||||
|
|
||||||
|
Mounting Volumes
|
||||||
|
================
|
||||||
|
--If the device has a partition table (practically all removable flash
|
||||||
|
media are formatted this way), call DFS_GetPtnStart to get the
|
||||||
|
starting sector# of the desired partition. You can optionally also
|
||||||
|
retrieve the active state, partition type byte and partition size
|
||||||
|
in this step. The reason this step is broken out separately is so
|
||||||
|
you can support devices that are formatted like a floppy disk, i.e.
|
||||||
|
the volume starts directly at physical sector 0 of the media.
|
||||||
|
|
||||||
|
--Call DFS_GetVolInfo to read filesystem info into a VOLINFO structure.
|
||||||
|
DFS_GetVolInfo needs to know the unit number and partition starting
|
||||||
|
sector (as returned by DFS_GetPtnStart, or 0 if this is a "floppy-
|
||||||
|
format" volume without an MBR).
|
||||||
|
|
||||||
|
From this point on, the VOLINFO structure is all you'll need - you can
|
||||||
|
forget the unit and partition start sector numbers.
|
||||||
|
|
||||||
|
Enumerating Directory Contents
|
||||||
|
==============================
|
||||||
|
--Call DFS_Opendir and supply a path, populated VOLINFO and a
|
||||||
|
DIRINFO structure to receive the results. Note - you must PREPOPULATE
|
||||||
|
the DIRINFO.scratch field with a pointer to a sector scratch buffer.
|
||||||
|
This buffer must remain unmolested while you have the directory open
|
||||||
|
for searching.
|
||||||
|
--Call DFS_GetNext to receive the DIRENT contents for the next directory
|
||||||
|
item. This function returns DFS_OK for no error, and DFS_EOF if there
|
||||||
|
are no more entries in the directory being searched.
|
||||||
|
Before using the DIRENT, check the first character of the name. If it
|
||||||
|
is NULL, then this is an unusable entry - call DFS_GetNext again to
|
||||||
|
keep searching. LFN directory entries are automatically tagged this way
|
||||||
|
so your application will not be pestered by them.
|
||||||
|
|
||||||
|
Note: A designed side-effect of this code is that when you locate the
|
||||||
|
file of interest, the DIRINFO.currentcluster, DIRINFO.currentsector
|
||||||
|
and DIRINFO.currententry-1 fields will identify the directory entry of
|
||||||
|
interest.
|
||||||
|
|
||||||
|
Reading a File
|
||||||
|
==============
|
||||||
|
--Call DFS_OpenFile with mode = DFS_READ and supply a path and the relevant
|
||||||
|
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used
|
||||||
|
to refer to the file.
|
||||||
|
--Optionally call DFS_Seek to set the file pointer. If you attempt to set
|
||||||
|
the file pointer past the end of file, the file will NOT be extended. Check
|
||||||
|
the FILEINFO.pointer value after DFS_Seek to verify that the pointer is
|
||||||
|
where you expect it to be.
|
||||||
|
--Observe that functionality similar to the "whence" parameter of fseek() can
|
||||||
|
be obtained by using simple arithmetic on the FILEINFO.pointer and
|
||||||
|
FILEINFO.filelen members.
|
||||||
|
--Call DFS_ReadFile with the FILEINFO you obtained from OpenFile, and a
|
||||||
|
pointer to a buffer plus the desired number of bytes to read, and a
|
||||||
|
pointer to a sector-sized scratch buffer. The reason a scratch sector is
|
||||||
|
required is because the underlying sector read function doesn't know
|
||||||
|
about partial reads.
|
||||||
|
--Note that a file opened for reading cannot be written. If you need r/w
|
||||||
|
access, open with mode = DFS_WRITE (see below).
|
||||||
|
|
||||||
|
Writing a file
|
||||||
|
==============
|
||||||
|
--Call DFS_OpenFile with mode = DFS_WRITE and supply a path and the relevant
|
||||||
|
VOLINFO structure. DFS_OpenFile will populate a FILEINFO that can be used to
|
||||||
|
refer to the file.
|
||||||
|
--Optionally call DFS_Seek to set the file pointer. Refer to the notes on
|
||||||
|
this topic in the section on reading files, above.
|
||||||
|
--Call DFS_WriteFile with the FILEINFO you obtained from OpenFile, and a
|
||||||
|
pointer to the source buffer, and a pointer to a sector-sized scratch
|
||||||
|
buffer.
|
||||||
|
--Note that a file open for writing can also be read.
|
||||||
|
--Files are created automatically if they do not exist. Subdirectories are
|
||||||
|
NOT automatically created.
|
||||||
|
--If you open an existing file for writing, the file pointer will start at
|
||||||
|
the beginning of the data; if you want to append, seek to the end before
|
||||||
|
writing new data.
|
||||||
|
--If you perform random-access writes to a file, the length will NOT change
|
||||||
|
unless you exceed the file's original length. There is currently no
|
||||||
|
function to truncate a file at the current pointer position.
|
||||||
|
--On-disk consistency is guaranteed when DFS_WriteFile exits, unless your
|
||||||
|
physical layer has a writeback cache in it.
|
||||||
|
|
||||||
|
Deleting a file
|
||||||
|
===============
|
||||||
|
--Call DFS_UnlinkFile
|
||||||
|
--WARNING: This call will delete a subdirectory (correctly) but will NOT
|
||||||
|
first recurse the directory to delete the contents - so you will end up
|
||||||
|
with lost clusters.
|
||||||
|
|
||||||
|
Notes
|
||||||
|
=====
|
||||||
|
Some platforms may require explicit pragmas or attributes to the structures
|
||||||
|
and unions. For example, arm-gcc will require __attribute__ ((__packed__))
|
||||||
|
otherwise it will try to be "smart" and place the uint8_t members on 4-byte
|
||||||
|
boundaries. There is no truly elegant compiler-independent method to get
|
||||||
|
around this sort of problem.
|
||||||
|
|
||||||
|
The code assumes either a von Neumann architecture, or a compiler that
|
||||||
|
is smart enough to understand where your pointers are aimed and emit
|
||||||
|
the right kind of memory read and write instructions. The implications
|
||||||
|
of this statement depend on your target processor and the compiler you
|
||||||
|
are using. Be very careful not to straddle bank boundaries on bank-
|
||||||
|
switched memory systems.
|
||||||
|
|
||||||
|
Physical 32-bit sector numbers are used throughout. Therefore, the
|
||||||
|
CHS geometry (if any) of the storage media is not known to DOSFS. Your
|
||||||
|
sector r/w functions may need to query the CHS geometry and perform
|
||||||
|
mapping.
|
||||||
|
|
||||||
|
File timestamps set by DOSFS are always 1:01:00am on Jan 1, 2006. If
|
||||||
|
your system has a concept of real time, you can enhance this.
|
||||||
|
|
||||||
|
FILEINFO structures contain a pointer to the corresponding VOLINFO
|
||||||
|
used to open the file, mainly in order to avoid mixups but also to
|
||||||
|
obviate the need for an extra parameter to every file read/write. DOSFS
|
||||||
|
assumes that the VOLINFO won't move around. If you need to move or
|
||||||
|
destroy VOLINFOs pertaining to open files, you'll have to fix up the
|
||||||
|
pointer in the FILEINFO structure yourself.
|
||||||
|
|
||||||
|
The subdirectory delimiter is a forward slash ( '/' ) by default. The
|
||||||
|
reason for this is to avoid the common programming error of forgetting
|
||||||
|
that backslash is an escape character in C strings; i.e. "\MYDIR\FILE"
|
||||||
|
is NOT what you want; "\\MYDIR\\FILE" is what you wanted to type. If you
|
||||||
|
are porting DOS code into an embedded environment, feel free to change
|
||||||
|
this #define.
|
||||||
|
|
||||||
|
DOSFS does not have a concept of "current directory". A current directory
|
||||||
|
is owned by a process, and a process is an operating system concept.
|
||||||
|
DOSFS is a filesystem library, not an operating system. Therefore, any
|
||||||
|
path you provide to a DOSFS call is assumed to be relative to the root of
|
||||||
|
the volume.
|
||||||
|
|
||||||
|
There is no call to close a file or directory that is open for reading or
|
||||||
|
writing. You can simply destroy or reuse the data structures allocated for
|
||||||
|
that operation; there is no internal state in DOSFS so no cleanup is
|
||||||
|
necessary. Similarly, there is no call to close a file that is open for
|
||||||
|
writing. (Observe that dosfs.c has no global variables. All state information
|
||||||
|
is stored in data structures provided by the caller).
|
||||||
|
|
||||||
|
MAX_PATH is defined as 64. MS-type DOS filesystems support 128 characters
|
||||||
|
or more in paths. You can increase this define, but it may GREATLY
|
||||||
|
increase memory requirements.
|
||||||
|
|
||||||
|
VFAT long filenames are not supported. There is a certain amount of
|
||||||
|
patent controversy about them, but more importantly they don't really
|
||||||
|
belong in the scope of a "minimalist embedded filesystem".
|
||||||
|
|
||||||
|
Improving Performance
|
||||||
|
=====================
|
||||||
|
Read performance is fairly good, but can be improved by implementing read
|
||||||
|
caching on the FAT (see below) and, depending on your hardware platform,
|
||||||
|
possibly by implementing multi-sector reads.
|
||||||
|
|
||||||
|
Write performance may benefit ENORMOUSLY from platform-specific
|
||||||
|
optimization, especially if you are working with a flash media type that
|
||||||
|
has a large erase block size. While it is not possible to offer detailed
|
||||||
|
platform-independent advice, my general advice is to implement writeback
|
||||||
|
caching on the FAT area. One method for doing this would be to have a
|
||||||
|
cache system that lives in the DFS_ReadSector/WriteSector functions (on
|
||||||
|
top of the physical sector r/w functions) and is initially switched off.
|
||||||
|
Once you have called DFS_GetVolInfo, you then extract the VOLINFO.fat1
|
||||||
|
and VOLINFO.rootdir parameters and pass them to your caching layer.
|
||||||
|
Sectors >= fat1 and < rootdir should be cached. The cache strategy is
|
||||||
|
determined by the physical storage medium underlying the filesystem.
|
||||||
|
|
||||||
|
CACHING HINT:
|
||||||
|
Observe that there will be numerous read-modify-write operations in the
|
||||||
|
region from VOLINFO.fat1 through VOLINFO.fat1+VOLINFO.secperfat-1, but
|
||||||
|
in the region from VOLINFO.fat1+VOLINFO.secperfat through VOLINFO.rootdir
|
||||||
|
there will ONLY be write operations.
|
||||||
|
|
||||||
|
Platform Compatibility
|
||||||
|
======================
|
||||||
|
DOSFS was derived from code originally written for ARM7TDMI but
|
||||||
|
designed to be portable. It has been tested on AVR (using avrgcc),
|
||||||
|
MSP430 (using Rowley's CrossWorks) and PPC603e (using gcc); the host
|
||||||
|
test suite has also been validated on x86 using gcc under both Cygwin
|
||||||
|
and 32-bit Fedora Core 4 Linux.
|
||||||
|
|
||||||
|
TODO list
|
||||||
|
=========
|
||||||
|
* Add function to create subdirectory
|
||||||
|
* Make DFS_UnlinkFile recognize non-empty subdirectories
|
||||||
|
* Support "fast write" files where the FAT is not updated, for
|
||||||
|
logging applications where latency is important.
|
||||||
|
|
||||||
|
Test cases for V1.02
|
||||||
|
====================
|
||||||
|
Version 1.02 has NOT been through full regression testing. However the
|
||||||
|
bugs fixed in this version are important, and people have been asking
|
||||||
|
about them.
|
||||||
|
|
||||||
|
Test cases for V1.01
|
||||||
|
====================
|
||||||
|
See below.
|
||||||
|
|
||||||
|
Test cases for V1.00
|
||||||
|
====================
|
||||||
|
These are the test cases that were used to validate the correct
|
||||||
|
functionality of the DOSFS suite. Each test was performed on FAT12,
|
||||||
|
FAT16 and FAT32 volumes. P=Pass, F=Fail.
|
||||||
|
|
||||||
|
Case F12 F16 F32
|
||||||
|
---------------------------------------------------------------------
|
||||||
|
Get volume information P P P
|
||||||
|
Open root directory P P P
|
||||||
|
List contents of root directory (fully populated) P P P
|
||||||
|
Open subdirectory P P P
|
||||||
|
List contents of subdirectory (<= 1 cluster) P P P
|
||||||
|
List contents of large subdirectory (> 1 cluster) P P P
|
||||||
|
Open 5-level nested subdirectory P P P
|
||||||
|
Open existing file for reading P P P
|
||||||
|
Open nonexistent file for reading P P P
|
||||||
|
Seek past EOF, file open for reading P P P
|
||||||
|
Seek to cluster boundary P P P
|
||||||
|
Seek past cluster boundary P P P
|
||||||
|
Seek backwards to nonzero offset, pointer > cluster size P P P
|
||||||
|
Block-read entire file >1 cluster in size, odd size P P P
|
||||||
|
Seek to odd location in file P P P
|
||||||
|
Perform <1 sector reads from random file locations P P P
|
||||||
|
Open nonexistent file for writing in root dir P P P
|
||||||
|
Open nonexistent file for writing in subdir P P P
|
||||||
|
Repeat prev. 2 tests on volume with 0 free clusters P P P
|
||||||
|
Seek past EOF, file open for writing P P P
|
||||||
|
Open existing file for writing in root dir P P P
|
||||||
|
Write random-length records to file, 20 clusters total P P P
|
||||||
|
MS-DOS 6.0 SCANDISK cross-check P P P
|
||||||
|
|
||||||
|
Revision History
|
||||||
|
================
|
||||||
|
Jan-06-2005 larwe Initial release (1.0)
|
||||||
|
Jan-29-2006 larwe Bugfix release (1.01)
|
||||||
|
- Fixed error in FAT12 FAT read on boundary of sector
|
||||||
|
- Improved compilability under avrgcc
|
||||||
|
Sep-16-2006 larwe Bugfix release (1.02)
|
||||||
|
- DFS_Seek would not correctly rewind to start of file
|
||||||
|
- DFS_Seek would not correctly seek to a position not on a cluster
|
||||||
|
boundary
|
||||||
|
- DFS_OpenFile fencepost error caused memory access at [start of
|
||||||
|
string-1] with a local variable
|
||||||
|
- DFS_OpenFile could not open a file in the root directory
|
99
dosfs/tmpstring.c
Normal file
99
dosfs/tmpstring.c
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *d = dest;
|
||||||
|
const unsigned char *s = src;
|
||||||
|
for (; n; n--) *d++ = *s++;
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *memset(void *dest, int c, size_t n)
|
||||||
|
{
|
||||||
|
unsigned char *s = dest;
|
||||||
|
size_t k;
|
||||||
|
|
||||||
|
/* Fill head and tail with minimal branching. Each
|
||||||
|
* conditional ensures that all the subsequently used
|
||||||
|
* offsets are well-defined and in the dest region. */
|
||||||
|
|
||||||
|
if (!n) return dest;
|
||||||
|
s[0] = c;
|
||||||
|
s[n-1] = c;
|
||||||
|
if (n <= 2) return dest;
|
||||||
|
s[1] = c;
|
||||||
|
s[2] = c;
|
||||||
|
s[n-2] = c;
|
||||||
|
s[n-3] = c;
|
||||||
|
if (n <= 6) return dest;
|
||||||
|
s[3] = c;
|
||||||
|
s[n-4] = c;
|
||||||
|
if (n <= 8) return dest;
|
||||||
|
|
||||||
|
/* Advance pointer to align it at a 4-byte boundary,
|
||||||
|
* and truncate n to a multiple of 4. The previous code
|
||||||
|
* already took care of any head/tail that get cut off
|
||||||
|
* by the alignment. */
|
||||||
|
|
||||||
|
k = -(uintptr_t)s & 3;
|
||||||
|
s += k;
|
||||||
|
n -= k;
|
||||||
|
n &= -4;
|
||||||
|
|
||||||
|
/* Pure C fallback with no aliasing violations. */
|
||||||
|
for (; n; n--, s++) *s = c;
|
||||||
|
|
||||||
|
return dest;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t strlen(const char *s)
|
||||||
|
{
|
||||||
|
const char *a = s;
|
||||||
|
for (; *s; s++);
|
||||||
|
return s-a;
|
||||||
|
}
|
||||||
|
|
||||||
|
int memcmp(const void *vl, const void *vr, size_t n)
|
||||||
|
{
|
||||||
|
const unsigned char *l=vl, *r=vr;
|
||||||
|
for (; n && *l == *r; n--, l++, r++);
|
||||||
|
return n ? *l-*r : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strncpy(char *restrict d, const char *restrict s, size_t n)
|
||||||
|
{
|
||||||
|
for (; n && (*d=*s); n--, s++, d++);
|
||||||
|
memset(d, 0, n);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *strcpy(char *restrict dest, const char *restrict src)
|
||||||
|
{
|
||||||
|
char *restrict d = dest;
|
||||||
|
const char *restrict s = src;
|
||||||
|
for (; (*d=*s); s++, d++);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
int strcmp(const char *l, const char *r)
|
||||||
|
{
|
||||||
|
for (; *l==*r && *l; l++, r++);
|
||||||
|
return *(unsigned char *)l - *(unsigned char *)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* STDLIB DIV FUNCTIONS */
|
||||||
|
typedef struct { int quot, rem; } div_t;
|
||||||
|
typedef struct { long quot, rem; } ldiv_t;
|
||||||
|
div_t div(int num, int den)
|
||||||
|
{
|
||||||
|
return (div_t){ num/den, num%den };
|
||||||
|
}
|
||||||
|
ldiv_t ldiv(long num, long den)
|
||||||
|
{
|
||||||
|
return (ldiv_t){ num/den, num%den };
|
||||||
|
}
|
||||||
|
|
@ -77,7 +77,7 @@ div bl ; Unhandled DIV0 exception
|
|||||||
global jmp_usermode_test
|
global jmp_usermode_test
|
||||||
jmp_usermode_test:
|
jmp_usermode_test:
|
||||||
pop eax ; return address
|
pop eax ; return address
|
||||||
mov ebp, esp ; return stack
|
mov ecx, esp ; return stack
|
||||||
call save_current_task
|
call save_current_task
|
||||||
mov esp, 0x500000 ; usermode stack
|
mov esp, 0x500000 ; usermode stack
|
||||||
mov eax, 0x20 | 3
|
mov eax, 0x20 | 3
|
||||||
|
@ -175,7 +175,7 @@ void gpf_handler_v86(struct interrupt_frame *frame, unsigned long error_code) {
|
|||||||
frame->eip = (uint16_t)(frame->eip + 1);
|
frame->eip = (uint16_t)(frame->eip + 1);
|
||||||
goto done;
|
goto done;
|
||||||
case 0xCD: // INT n
|
case 0xCD: // INT n
|
||||||
vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0';
|
//vga[0] = 'I'; vga[2]++; if (vga[2] < '0') vga[2] = '0';
|
||||||
switch (ip[1]) {
|
switch (ip[1]) {
|
||||||
case 0x30:
|
case 0x30:
|
||||||
return_prev_task();
|
return_prev_task();
|
||||||
|
@ -27,6 +27,7 @@ typedef uint32_t FARPTR;
|
|||||||
#define EFLAG_IF ((uint32_t)1 << 9)
|
#define EFLAG_IF ((uint32_t)1 << 9)
|
||||||
#define EFLAG_VM ((uint32_t)1 << 17)
|
#define EFLAG_VM ((uint32_t)1 << 17)
|
||||||
|
|
||||||
|
FARPTR i386LinearToFp(void *ptr);
|
||||||
|
|
||||||
|
|
||||||
__attribute__ ((interrupt))
|
__attribute__ ((interrupt))
|
||||||
|
122
kernel.c
122
kernel.c
@ -1,5 +1,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "dosfs/dosfs.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
#include "interrupt.h"
|
#include "interrupt.h"
|
||||||
|
|
||||||
@ -52,13 +53,21 @@ void print_cr4() {
|
|||||||
printDword(reg, 0xB8000 + (160*5) + 50 + 8*4 + 4);
|
printDword(reg, 0xB8000 + (160*5) + 50 + 8*4 + 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute((__no_caller_saved_registers__))
|
struct __attribute((__packed__)) Int13DiskPacket_t {
|
||||||
|
uint8_t size; // 0x10
|
||||||
|
uint8_t reserved; // 0x00
|
||||||
|
uint16_t blocks;
|
||||||
|
uint32_t transfer_buffer; // 0x2300:0000
|
||||||
|
uint64_t start_block;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern struct Int13DiskPacket_t v86disk_addr_packet;
|
||||||
|
|
||||||
extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
||||||
extern void v86Test();
|
extern void v86Test();
|
||||||
extern void v86GfxMode();
|
extern void v86GfxMode();
|
||||||
extern void v86TextMode();
|
extern void v86TextMode();
|
||||||
extern void v86DiskRead();
|
extern void v86DiskRead();
|
||||||
__attribute((__no_caller_saved_registers__))
|
|
||||||
extern char *jmp_usermode_test();
|
extern char *jmp_usermode_test();
|
||||||
__attribute((__no_caller_saved_registers__))
|
__attribute((__no_caller_saved_registers__))
|
||||||
extern void kbd_wait();
|
extern void kbd_wait();
|
||||||
@ -88,6 +97,92 @@ Protected Only (1MB+)
|
|||||||
400000 - 500000 Usermode Stack (1mB)
|
400000 - 500000 Usermode Stack (1mB)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void TestV86() {
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86Test);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
||||||
|
}
|
||||||
|
void TestGfx() {
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86GfxMode);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
||||||
|
char *vga = jmp_usermode_test();
|
||||||
|
for (int i = 0; i < 320; i++) {
|
||||||
|
vga[i] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TestDiskRead() {
|
||||||
|
FARPTR v86_entry = i386LinearToFp(v86TextMode);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
||||||
|
v86_entry = i386LinearToFp(v86DiskRead);
|
||||||
|
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
||||||
|
word *vga_text = (word *)0xb8000;
|
||||||
|
char *diskReadBuf = (char *)0x23000;
|
||||||
|
for (int i = 0; i < (80*25)/2; i++) {
|
||||||
|
printByte(diskReadBuf[i], &vga_text[i*2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TestFAT() {
|
||||||
|
word *vga_text = (word *)0xb8000;
|
||||||
|
uint8_t *diskReadBuf = (uint8_t *)0x23000;
|
||||||
|
for (int i = 0; i < 80*25; i++)
|
||||||
|
vga_text[i] = 0x0f00;
|
||||||
|
VOLINFO vi;
|
||||||
|
|
||||||
|
uint8_t pactive, ptype;
|
||||||
|
uint32_t pstart, psize;
|
||||||
|
pstart = DFS_GetPtnStart(0, diskReadBuf, 0, &pactive, &ptype, &psize);
|
||||||
|
vga_text = (word *)0xb8000;
|
||||||
|
vga_text += printStr("PartStart: ", vga_text);
|
||||||
|
vga_text += printDword(pstart, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("PartSize: ", vga_text);
|
||||||
|
vga_text += printDword(psize, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("PartActive: ", vga_text);
|
||||||
|
vga_text += printByte(pactive, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("PartType: ", vga_text);
|
||||||
|
vga_text += printByte(ptype, vga_text);
|
||||||
|
vga_text = (word *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
|
||||||
|
DFS_GetVolInfo(0, diskReadBuf, pstart, &vi);
|
||||||
|
vga_text += printStr("Label: ", vga_text);
|
||||||
|
vga_text += printStr((char*)vi.label, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("Sec/Clus: ", vga_text);
|
||||||
|
vga_text += printByte(vi.secperclus, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("ResrvSec: ", vga_text);
|
||||||
|
vga_text += printWord(vi.reservedsecs, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("NumSec: ", vga_text);
|
||||||
|
vga_text += printDword(vi.numsecs, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("Sec/FAT: ", vga_text);
|
||||||
|
vga_text += printDword(vi.secperfat, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("FAT1@: ", vga_text);
|
||||||
|
vga_text += printDword(vi.fat1, vga_text);
|
||||||
|
vga_text += 2;
|
||||||
|
vga_text += printStr("ROOT@: ", vga_text);
|
||||||
|
vga_text += printDword(vi.rootdir, vga_text);
|
||||||
|
vga_text = (word *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
|
||||||
|
vga_text += printStr("Files in root:", vga_text);
|
||||||
|
vga_text = (word *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
DIRINFO di;
|
||||||
|
DIRENT de;
|
||||||
|
di.scratch = 0x23000;
|
||||||
|
while (!DFS_GetNext(&vi, &di, &de)) {
|
||||||
|
if (de.name[0]) {
|
||||||
|
for (int i = 0; i < 11 && de.name[i]; i++) {
|
||||||
|
*(uint8_t *)vga_text = de.name[i];
|
||||||
|
vga_text++;
|
||||||
|
}
|
||||||
|
vga_text = (word *)((((((uintptr_t)vga_text)-0xb8000) - ((((uintptr_t)vga_text)-0xb8000) % 160)) + 160)+0xb8000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void start() {
|
void start() {
|
||||||
word *vga_text = (word *)0xb8000;
|
word *vga_text = (word *)0xb8000;
|
||||||
char h[] = "LuciaOS";
|
char h[] = "LuciaOS";
|
||||||
@ -122,25 +217,14 @@ void start() {
|
|||||||
print_cr0();
|
print_cr0();
|
||||||
print_cr3();
|
print_cr3();
|
||||||
print_cr4();
|
print_cr4();
|
||||||
FARPTR v86_entry = i386LinearToFp(v86Test);
|
|
||||||
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
|
||||||
v86_entry = i386LinearToFp(v86GfxMode);
|
|
||||||
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
|
||||||
char *vga = jmp_usermode_test();
|
|
||||||
//asm ("xchgw %bx, %bx");
|
//asm ("xchgw %bx, %bx");
|
||||||
|
|
||||||
for (int i = 0; i < 320; i++) {
|
TestV86(); // has int 3 wait in v86
|
||||||
vga[i] = i;
|
TestGfx();
|
||||||
}
|
kbd_wait();
|
||||||
|
TestDiskRead();
|
||||||
|
kbd_wait();
|
||||||
|
TestFAT();
|
||||||
kbd_wait();
|
kbd_wait();
|
||||||
v86_entry = i386LinearToFp(v86TextMode);
|
|
||||||
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
|
||||||
v86_entry = i386LinearToFp(v86DiskRead);
|
|
||||||
enter_v86(0x8000, 0xFF00, FP_SEG(v86_entry), FP_OFF(v86_entry));
|
|
||||||
vga_text = (word *)0xb8000;
|
|
||||||
char *bootloader = (char *)0x23000;
|
|
||||||
for (int i = 0; i < (80*25)/2; i++) {
|
|
||||||
printByte(bootloader[i], &vga_text[i*2]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
print.c
16
print.c
@ -3,15 +3,25 @@
|
|||||||
char nibbleToHex(uint8_t n) {
|
char nibbleToHex(uint8_t n) {
|
||||||
return n > 9 ? (n - 10) + 'A' : n + '0';
|
return n > 9 ? (n - 10) + 'A' : n + '0';
|
||||||
}
|
}
|
||||||
void printByte(uint8_t v, uint16_t *buff) {
|
uintptr_t printByte(uint8_t v, uint16_t *buff) {
|
||||||
*(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF);
|
*(char *)&buff[0] = nibbleToHex((v >> 4) & 0xF);
|
||||||
*(char *)&buff[1] = nibbleToHex(v & 0xF);
|
*(char *)&buff[1] = nibbleToHex(v & 0xF);
|
||||||
|
return 2;
|
||||||
}
|
}
|
||||||
void printWord(uint16_t v, uint16_t *buff) {
|
uintptr_t printWord(uint16_t v, uint16_t *buff) {
|
||||||
printByte(v >> 8, buff);
|
printByte(v >> 8, buff);
|
||||||
printByte(v, &buff[2]);
|
printByte(v, &buff[2]);
|
||||||
|
return 4;
|
||||||
}
|
}
|
||||||
void printDword(uint32_t v, uint16_t *buff) {
|
uintptr_t printDword(uint32_t v, uint16_t *buff) {
|
||||||
printWord(v >> 16, buff);
|
printWord(v >> 16, buff);
|
||||||
printWord(v, &buff[4]);
|
printWord(v, &buff[4]);
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t printStr(char *v, uint16_t *buff) {
|
||||||
|
char *s;
|
||||||
|
for (s = v;*s;s++,buff++)
|
||||||
|
*(char*)buff = *s;
|
||||||
|
return s - v;
|
||||||
}
|
}
|
||||||
|
7
print.h
7
print.h
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
void printByte(uint8_t v, uint16_t *buff);
|
uintptr_t printByte(uint8_t v, uint16_t *buff);
|
||||||
void printWord(uint16_t v, uint16_t *buff);
|
uintptr_t printWord(uint16_t v, uint16_t *buff);
|
||||||
void printDword(uint32_t v, uint16_t *buff);
|
uintptr_t printDword(uint32_t v, uint16_t *buff);
|
||||||
|
uintptr_t printStr(char *v, uint16_t *buff);
|
||||||
|
54
task.nasm
54
task.nasm
@ -1,12 +1,16 @@
|
|||||||
task_ptr: equ (0x310000-4)
|
task_ptr: equ (0x310000-4)
|
||||||
|
|
||||||
|
; return address in EAX
|
||||||
|
; return stack in ECX
|
||||||
|
; we can modify EAX, ECX, EDX
|
||||||
|
; i.e. save all others in task
|
||||||
global save_current_task
|
global save_current_task
|
||||||
save_current_task:
|
save_current_task:
|
||||||
push ebx
|
push edx
|
||||||
mov ebx, esp
|
mov edx, esp ; EDX holds our tmp stack, unsaved
|
||||||
mov esp, dword [task_ptr] ; load current task pointer
|
mov esp, dword [task_ptr] ; load current task pointer
|
||||||
push ss
|
push ss
|
||||||
push ebp ; return stack
|
push ecx ; return stack
|
||||||
pushfd
|
pushfd
|
||||||
push cs
|
push cs
|
||||||
push eax ; return address
|
push eax ; return address
|
||||||
@ -14,33 +18,41 @@ push ds ; other segs, pop
|
|||||||
push es ; before iret
|
push es ; before iret
|
||||||
push fs ; in exit handler
|
push fs ; in exit handler
|
||||||
push gs
|
push gs
|
||||||
|
push ebp ; saved
|
||||||
|
push ebx ; saved
|
||||||
|
push esi ; saved
|
||||||
|
push edi ; saved
|
||||||
mov dword [task_ptr], esp ; save new task pointer
|
mov dword [task_ptr], esp ; save new task pointer
|
||||||
mov esp, ebx
|
mov esp, edx
|
||||||
pop ebx
|
pop edx
|
||||||
ret
|
ret
|
||||||
|
|
||||||
global return_prev_task
|
global return_prev_task
|
||||||
return_prev_task:
|
return_prev_task:
|
||||||
mov edi, eax ; save for later
|
mov ecx, eax ; save return value for later
|
||||||
mov esi, dword [task_ptr] ; load current task pointer
|
mov edx, dword [task_ptr] ; load current task pointer
|
||||||
add dword [task_ptr], 36 ; adjust to last task pointer
|
add dword [task_ptr], 52 ; adjust to last task pointer
|
||||||
mov eax, [esi+0] ; gs
|
mov edi, [edx+0]
|
||||||
|
mov esi, [edx+4]
|
||||||
|
mov ebx, [edx+8]
|
||||||
|
mov ebp, [edx+12]
|
||||||
|
mov eax, [edx+0+16] ; gs
|
||||||
mov gs, ax
|
mov gs, ax
|
||||||
mov eax, [esi+4] ; fs
|
mov eax, [edx+4+16] ; fs
|
||||||
mov fs, ax
|
mov fs, ax
|
||||||
mov eax, [esi+8] ; es
|
mov eax, [edx+8+16] ; es
|
||||||
mov es, ax
|
mov es, ax
|
||||||
mov ebx, [esi+16] ; eip
|
|
||||||
mov ecx, [esi+20] ; cs
|
|
||||||
mov edx, [esi+24] ; eflags
|
|
||||||
; SS:ESP <- return stack
|
; SS:ESP <- return stack
|
||||||
mov esp, [esi+28] ; esp
|
mov esp, [edx+28+16] ; esp
|
||||||
mov eax, [esi+32] ; ss
|
mov eax, [edx+32+16] ; ss
|
||||||
mov ss, ax
|
mov ss, ax
|
||||||
mov eax, [esi+12] ; ds
|
mov eax, [edx+24+16] ; eflags
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+20+16] ; cs
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+16+16] ; eip
|
||||||
|
push eax
|
||||||
|
mov eax, [edx+12+16] ; ds
|
||||||
mov ds, ax
|
mov ds, ax
|
||||||
push edx ; eflags
|
mov eax, ecx ; restore return value
|
||||||
push ecx ; cs
|
|
||||||
push ebx ; eip
|
|
||||||
mov eax, edi ; restore return value
|
|
||||||
iret
|
iret
|
||||||
|
2
tss.c
2
tss.c
@ -42,7 +42,7 @@ void write_tss() {
|
|||||||
|
|
||||||
// not technically TSS but set up task pointer
|
// not technically TSS but set up task pointer
|
||||||
uint32_t *current_task_ptr = (uint32_t*)(0x310000-4);
|
uint32_t *current_task_ptr = (uint32_t*)(0x310000-4);
|
||||||
*current_task_ptr = 0x310000-40; // each task is 9 dwords, plus 1 for pointer
|
*current_task_ptr = 0x310000-(20*4); // each task is 12 dwords, plus 1 for pointer
|
||||||
/* TODO setup null recovery task at start */
|
/* TODO setup null recovery task at start */
|
||||||
}
|
}
|
||||||
extern void flushTSS();
|
extern void flushTSS();
|
||||||
|
10
v86.nasm
10
v86.nasm
@ -67,18 +67,20 @@ mov si, v86disk_addr_packet ; ds:si
|
|||||||
int 0x13
|
int 0x13
|
||||||
int 0x30
|
int 0x30
|
||||||
jmp $
|
jmp $
|
||||||
|
global v86disk_addr_packet
|
||||||
v86disk_addr_packet:
|
v86disk_addr_packet:
|
||||||
db 0x10, 0x00 ; size, reserved
|
db 0x10, 0x00 ; size, reserved
|
||||||
dw 0x20 ; blocks
|
dw 0x1 ; blocks
|
||||||
dd 0x23000000 ; transfer buffer 0x23000
|
dd 0x23000000 ; transfer buffer 0x23000
|
||||||
dq 0 ; start block
|
dq 0x1 ; start block
|
||||||
[BITS 32]
|
[BITS 32]
|
||||||
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
; extern void enter_v86(uint32_t ss, uint32_t esp, uint32_t cs, uint32_t eip);
|
||||||
global enter_v86
|
global enter_v86
|
||||||
enter_v86:
|
enter_v86:
|
||||||
pop eax
|
pop eax ; return address
|
||||||
mov ebp, esp ; save stack pointer
|
mov ecx, esp ; return stack
|
||||||
call save_current_task
|
call save_current_task
|
||||||
|
mov ebp, esp ; save stack pointer
|
||||||
push dword [ebp+0] ; ss
|
push dword [ebp+0] ; ss
|
||||||
push dword [ebp+4] ; esp
|
push dword [ebp+4] ; esp
|
||||||
pushfd ; eflags
|
pushfd ; eflags
|
||||||
|
Loading…
Reference in New Issue
Block a user