Hi, I'm new in the world of OS programming.
I already have a very basic OS (real mode, monolithic), written in NASM and mostly in C.
My goal is to make a clone of the early versions of MS-DOS (.com executable files, FAT16, etc)
I want to make something simple, because (how I said) I am new at OS programming.
Well, my real question is, how I can read/write FAT16 hard drives, get metadata (ex. last time modified, etc)
I already read the OSDev wiki, which gives to you a good introduction, but not how implement it, and well, I have no idea how to do what I want (I can't find it in internet).
Sorry for my bad english.
(if you need my source code, just say it and I upload a zip file with all files.)
FAT16 in real mode
-
- Member
- Posts: 38
- Joined: Wed Jul 25, 2018 2:47 pm
- Location: Pizzaland, Southern Europe
- Contact:
Re: FAT16 in real mode
The fastest way to read data from disks in real mode is to use the BIOS: https://en.wikipedia.org/wiki/INT_13H
The idea is that you read the raw data from the disks you are interested in, as they will contain the FAT16 structures. From there, you should follow the FAT16 specs to learn how the various pieces of data (and metadata) are laid out on the disk.
The idea is that you read the raw data from the disks you are interested in, as they will contain the FAT16 structures. From there, you should follow the FAT16 specs to learn how the various pieces of data (and metadata) are laid out on the disk.
Re: FAT16 in real mode
First, you need to read data from the disk. This means reading sectors into memory. Second, depending what sector you've just read, interpret it accoringly. With FAT you'll have the following structures:konniskatt wrote:Well, my real question is, how I can read/write FAT16 hard drives, get metadata (ex. last time modified, etc)
I already read the OSDev wiki, which gives to you a good introduction, but not how implement it, and well, I have no idea how to do what I want
1. BPB (describes the whole file system, like cluster size, root directory position etc. This is always in the first sector of the volume)
2. directory entries (filenames and starting clusters, this stores the metadata you asked for)
3. FAT table (data is stored in a linked list, but the "next" pointers are gathered together, that's what this table is about)
There are plenty of examples. Try the keywords "fat16 boot sector asm" in google.(I can't find it in internet).
For example here's alexfru's FAT16 reader in assembly:
https://github.com/alexfru/BootProg/blo ... boot16.asm
If you prefer C, here's my example code, which can read FAT16/FAT32. It was written for RPi3, but the code is architecture independent, just replace "sd_readblock()" with the aforementioned INT13H call:
https://github.com/bztsrc/raspi3-tutori ... file/fat.c
Writing a file on a FAT file system is very similar, but you'll need a "find_free_cluster()" function, which is searching the FAT looking for a free entry. Then:
1. find a free cluster, that would be the "starting cluster"
2. write data to disk with INT13H at cluster (use sector_per_cluster and other fields in BPB to calculate LBA)
3. if you have no more data to write, go to step 7
4. find a free cluster
5. save that new cluster number into the FAT at position fat[cluster]
6. go to step 2
7. write EOF marker into the FAT at fat[cluster]
8. write directory entry with the file name, file size, "starting cluster" and other info
Cheers,
bzt
-
- Member
- Posts: 119
- Joined: Wed Dec 12, 2018 12:16 pm
Re: FAT16 in real mode
Is there a way to read hard disk without using assembler?, I wish to use C instead of assembler.
Or use inline assembler
Or use inline assembler
Re: FAT16 in real mode
Of course there is a way. Wrap x86 IO operations with C functions and write IDE(or AHCI) HDD driver.
-
- Member
- Posts: 799
- Joined: Fri Aug 26, 2016 1:41 pm
- Libera.chat IRC: mpetch
Re: FAT16 in real mode
Some time back someone asked me about GCC inline assembly and reading from disk in realmode using int 13h. The function with _i are inlined versions, and those without are non inlined. Writing to disk would have to be don, although the reading from disk is similar.
x86helper.h :biosdisk.h:biosdisk.c:When I get a chance I can write some code that uses these functions, but for now I'll present them as is. These functions were for floppy media and smaller hard drive media, and thus don't use extended disk reads and writes. They use CHS read/writes. These were written as a proof of concept for someone else's needs.
x86helper.h :
Code: Select all
#ifndef X86HELPER_H
#define X86HELPER_H
#include <stdint.h>
#define STR_TEMP(x) #x
#define STR(x) STR_TEMP(x)
#define TRUE 1
#define FALSE 0
#define NULL (void *)0
/* regparam(3) is a calling convention that passes first
three parameters via registers instead of on stack.
1st param = EAX, 2nd param = EDX, 3rd param = ECX */
#define fastcall __attribute__((regparm(3)))
/* noreturn lets GCC know that a function that it may detect
won't exit is intentional */
#define noreturn __attribute__((noreturn))
#define always_inline __attribute__((always_inline))
#define used __attribute__((used))
/* Define helper x86 function */
static inline void fastcall always_inline x86_hlt(void){
__asm__ ("hlt\n\t");
}
static inline void fastcall always_inline x86_cli(void){
__asm__ ("cli\n\t");
}
static inline void fastcall always_inline x86_sti(void){
__asm__ ("sti\n\t");
}
static inline void fastcall always_inline x86_cld(void){
__asm__ ("cld\n\t");
}
/* Infinite loop with hlt to end bootloader code */
static inline void noreturn fastcall haltcpu()
{
while(1){
x86_hlt();
}
}
#endif
Code: Select all
#ifndef BIOSDISK_H
#define BIOSDISK_H
#include <stdint.h>
/* BIOS Parameter Block (BPB) on floppy media */
typedef struct __attribute__((packed)) {
char OEMname[8];
uint16_t bytesPerSector;
uint8_t sectPerCluster;
uint16_t reservedSectors;
uint8_t numFAT;
uint16_t numRootDirEntries;
uint16_t numSectors;
uint8_t mediaType;
uint16_t numFATsectors;
uint16_t sectorsPerTrack;
uint16_t numHeads;
uint32_t numHiddenSectors;
uint32_t numSectorsHuge;
uint8_t driveNum;
uint8_t reserved;
uint8_t signature;
uint32_t volumeID;
char volumeLabel[11];
char fileSysType[8];
} disk_bpb_s;
/* State information for CHS disk accesses */
typedef struct __attribute__((packed)) {
uint16_t segment;
uint16_t offset;
uint16_t status;
/* Drive geometry needed to compute CHS from LBA */
uint16_t sectorsPerTrack;
uint16_t numHeads;
/* Disk parameters */
uint16_t cylinder;
uint8_t head;
uint8_t sector;
uint8_t driveNum;
uint8_t numSectors; /* # of sectors to read */
/* Number of retries for disk operations */
uint8_t retries;
} disk_info_s;
extern fastcall uint8_t
reset_disk (disk_info_s *const disk_info);
extern fastcall uint8_t
read_sector_chs (disk_info_s *const disk_info);
/* Forced inline version of reset_sector */
static inline fastcall always_inline uint8_t
reset_disk_i (disk_info_s *const disk_info)
{
uint16_t temp_ax = 0x0000;
uint8_t carryf;
__asm__ __volatile__ (
"int $0x13\n\t"
#ifdef __GCC_ASM_FLAG_OUTPUTS__
: [cf]"=@ccc"(carryf),
#else
"setc %[cf]\n\t"
: [cf]"=qm"(carryf),
#endif
"+a"(temp_ax)
: "d"(disk_info->driveNum)
: "cc");
disk_info->status = temp_ax;
return (carryf);
}
/* Forced inline version of read_sector */
static inline fastcall always_inline uint8_t
read_sector_chs_i (disk_info_s *const disk_info)
{
uint16_t temp_ax;
uint16_t temp_dx;
uint8_t carryf = 0;
uint8_t retry_count = 0;
#ifndef BUGGY_BIOS_SUPPORT
temp_dx = (disk_info->head << 8) | disk_info->driveNum;
#endif
do {
/* Only reset disk if error detected previously */
if (carryf)
reset_disk_i (disk_info);
/* Need to reload AX during each iteration since a previous
* int 0x13 call will destroy its contents. There was a bug on
* earlier BIOSes where DX may have been clobbered.
*/
temp_ax = (0x02 << 8) | disk_info->numSectors;
#ifdef BUGGY_BIOS_SUPPORT
temp_dx = (disk_info->head << 8) | disk_info->driveNum;
#endif
__asm__ __volatile__ (
"push %%es\n\t"
"mov %w[seg], %%es\n\t"
#ifdef BUGGY_BIOS_SUPPORT
"stc\n\t" /* Some early bioses have CF bug */
"int $0x13\n\t"
"sti\n\t" /* Some early bioses don't re-enable interrupts */
#else
"int $0x13\n\t"
#endif
"pop %%es\n\t"
#ifdef __GCC_ASM_FLAG_OUTPUTS__
: [cf]"=@ccc"(carryf),
#else
"setc %[cf]\n\t"
: [cf]"=qm"(carryf),
#endif
#ifdef BUGGY_BIOS_SUPPORT
"+a"(temp_ax),
"+d"(temp_dx)
:
#else
"+a"(temp_ax)
:
"d"(temp_dx),
#endif
"c"(((disk_info->cylinder & 0xff) << 8) |
((disk_info->cylinder >> 2) & 0xC0) |
(disk_info->sector & 0x3f)),
"b"(disk_info->offset),
[seg]"r"(disk_info->segment)
: "memory", "cc");
} while (carryf && (++retry_count < disk_info->retries));
disk_info->status = temp_ax;
return (carryf);
}
/* Forced inline version of read_sector_lba */
static inline fastcall always_inline uint8_t
read_sector_lba_i (disk_info_s *const disk_info, const uint32_t lba)
{
disk_info->cylinder = lba / disk_info->sectorsPerTrack / disk_info->numHeads;
disk_info->head = (lba / disk_info->sectorsPerTrack) % disk_info->numHeads;
disk_info->sector = (lba % disk_info->sectorsPerTrack) + 1;
return read_sector_chs_i (disk_info);
}
#endif
Code: Select all
#include <stdint.h>
#include "x86helper.h"
#include "biosdisk.h"
fastcall uint8_t
reset_disk (disk_info_s *const disk_info)
{
return reset_disk_i (disk_info);
}
fastcall uint8_t
read_sector_chs (disk_info_s *const disk_info)
{
return read_sector_chs_i (disk_info);
}
fastcall uint8_t
read_sector_lba (disk_info_s *const disk_info, const uint32_t lba)
{
return read_sector_lba_i (disk_info, lba);
}