OSDev.org

The Place to Start for Operating System Developers
It is currently Tue Sep 24, 2019 3:04 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: FAT16 in real mode
PostPosted: Wed Dec 12, 2018 9:19 pm 
Offline
Member
Member
User avatar

Joined: Wed Dec 12, 2018 12:16 pm
Posts: 119
Location: Chile
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.)


Top
 Profile  
 
 Post subject: Re: FAT16 in real mode
PostPosted: Thu Dec 13, 2018 1:18 am 
Offline
Member
Member

Joined: Wed Jul 25, 2018 2:47 pm
Posts: 38
Location: Pizzaland, Southern Europe
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.


Top
 Profile  
 
 Post subject: Re: FAT16 in real mode
PostPosted: Thu Dec 13, 2018 6:14 am 
Offline
Member
Member
User avatar

Joined: Thu Oct 13, 2016 4:55 pm
Posts: 375
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
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:
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)

Quote:
(I can't find it in internet).
There are plenty of examples. Try the keywords "fat16 boot sector asm" in google.

For example here's alexfru's FAT16 reader in assembly:
https://github.com/alexfru/BootProg/blob/master/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-tutorial/blob/master/0D_readfile/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


Top
 Profile  
 
 Post subject: Re: FAT16 in real mode
PostPosted: Thu Dec 13, 2018 6:59 am 
Offline
Member
Member
User avatar

Joined: Wed Dec 12, 2018 12:16 pm
Posts: 119
Location: Chile
Is there a way to read hard disk without using assembler?, I wish to use C instead of assembler.
Or use inline assembler


Top
 Profile  
 
 Post subject: Re: FAT16 in real mode
PostPosted: Thu Dec 13, 2018 7:11 am 
Offline
Member
Member
User avatar

Joined: Mon Jan 15, 2018 2:27 pm
Posts: 71
Of course there is a way. Wrap x86 IO operations with C functions and write IDE(or AHCI) HDD driver.

_________________
https://github.com/pvc988/woot64


Top
 Profile  
 
 Post subject: Re: FAT16 in real mode
PostPosted: Thu Dec 13, 2018 11:58 am 
Offline
Member
Member

Joined: Fri Aug 26, 2016 1:41 pm
Posts: 482
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 :
Code:
#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
biosdisk.h:
Code:
#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
biosdisk.c:
Code:
#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);
}
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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Google [Bot], PTRNULL and 9 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group