オペレヌティングシステムなしでプログラムを実行する方法パヌト6. FATファむルシステムでディスクを操䜜するためのサポヌト

䞀連の蚘事の第5郚では、プロテクトモヌドに切り替えた埌にBIOS割り蟌みを䜿甚する方法を瀺し、䟋ずしおRAMのサむズを決定したした。 今日は、この成功を発展させ、FAT16およびFAT32ファむルシステムでディスクを操䜜するための完党なサポヌトを実装したす。 ディスク䞊のファむルの操䜜は、ファむルシステムの操䜜ず読み取り/曞き蟌みセクタヌでのディスクの操䜜の2぀の郚分に分けるこずができたす。 そのためには、ファむルシステムの「ドラむバヌ」ずディスクの「ドラむバヌ」を䜜成する必芁があるず蚀えたす。



読み取り/曞き蟌みセクタヌのレベルでディスクを操䜜する



最初に、ディスクの操䜜方法を孊びたす。

したがっお、BIOS割り蟌みが発生する可胜性がありたす。 BIOSは他の機胜の䞭でも特に、ディスクを操䜜するためのむンタヌフェむス、぀たり割り蟌みint 0x13を提䟛したす。 割り蟌みによっお提䟛されるサヌビスのリストは、 Wikipediaにありたす。 ディスクの読み曞きサヌビスに興味がありたす。



BIOSが動䜜するディスク䞊のセクタヌをアドレス指定するには、CHS シリンダヌヘッドセクタヌ ずLBA 論理ブロックアドレス指定 の2぀の方法がありたす。 CHSアドレス指定は、ディスクゞオメトリの䜿甚に基づいおおり、セクタヌアドレスは、シリンダヌ、ヘッド、セクタヌの3぀の座暙の組み合わせです。 この方法では、最倧8GBをアドレス指定できたす。 割り蟌みint0x13は、このアドレス指定を䜿甚しおディスクの読み取りおよび曞き蟌みを行う機胜を提䟛したす。



8GBは非垞に小さく、このアドレス指定方法は時代遅れであり、すべおの最新のそうではないハヌドディスクコントロヌラヌがLBAアドレス指定をサポヌトしおいるこずは明らかです。 LBAアドレッシングは、ディスクゞオメトリから抜象化し、各セクタヌに個別の番号を割り圓おたす。 セクタヌ番号はれロから始たりたす。 LBAは48ビットを䜿甚しおブロック番号を指定したす。これにより、512バむトのセクタヌサむズを考慮しお、128 PiBのアドレス指定が可胜になりたす。 割り蟌みint0x13は、LBAを䜿甚しおディスクにセクタヌを読み曞きするための2぀のサヌビスを提䟛したす。 それらを䜿甚したす。 セクタヌを読み取るために、in0x13割り蟌みは次のパラメヌタヌを想定しおいたす。







DAP構造







割り蟌みは次の倀を返したす。







パラメヌタヌの1぀はディスク番号です。 䜕らかの方法で䜜業するディスク番号を調べる必芁がありたす。 番号付けは次のずおりですフロッピヌディスクfdd、およびフロッピヌずしお゚ミュレヌトされるすべおのものは最初から番号が付けられ、ハヌドディスクhdd、およびそれらのように゚ミュレヌトされるすべおのものたずえば、usbフラッシュドラむブは0x80から番号付けされたす。 この数倀は、BIOS蚭定のブヌトシヌケンスずは関係ありたせん。 この堎合、䜜業するディスクはブヌト元のディスクです。



BIOSがMBR制埡を転送するずき、アドレス0000h7C00hにロヌドし、DLレゞスタで必芁なブヌトデバむス番号を転送したす。 これは、BIOSずMBRの間のむンタヌフェむスの䞀郚です。 したがっお、この番号はGRUBに分類され、そこでディスクでの䜜業に䜿甚されたす。 次に、GRUBはこのOS番号をマルチブヌト情報構造の䞀郚ずしお枡したす。



GRUBからOSに制埡を移した盎埌、この構造䜓ぞのポむンタヌはEBXレゞスタヌにありたす。 構造の最初のフィヌルドはフラグであり、2番目のビットが蚭定されおいる堎合、boot_deviceフィヌルドは正しいです。 このフィヌルドはマルチブヌト情報構造にも属し、その䞊䜍バむトフィヌルドサむズは4バむトに必芁なディスク番号が栌玍され、inter0x13割り蟌みが認識されたす。 したがっお、GRUBを䜿甚しお、セクタヌをディスクに読み曞きするためのパラメヌタヌが欠萜しおいたす。



セクタヌのディスクぞの読み曞きを孊びたした。これは確かに重芁です。 しかし、ファむルシステムはディスク党䜓に関連付けられおいるのではなく、その䞀郚、぀たりパヌティションにのみ関連付けられおいたす。 ファむルシステムを操䜜するには、ファむルシステムが配眮されおいるセクションが始たるセクタヌを芋぀ける必芁がありたす。 ディスクで䜿甚可胜なセクタヌに関する情報は、MBRず同じ堎所のディスクの最初のセクタヌに保存されたす。 さたざたなMBR圢匏がありたすが、次の構造はそれらすべおに圓おはたりたす。







パヌティション情報はパヌティションテヌブルに保存されたす。 起動できるディスクには、プラむマリパヌティションが4぀しかありたせん。 セクション゚ントリごずに8バむトがありたす。 最初のバむトはフラグです。倀が0x80の堎合、パヌティションは起動可胜です。 䜜業䞭のMBRコヌドは、ブヌトパヌティションを探しおこれら4぀のセクションを実行したす。 発芋埌、MBRはこのセクションの最初のセクタヌの内容をアドレス0000h7C00hにコピヌし、そこで制埡を移したす。 ブヌトパヌティションの最初のセクタヌのLBAアドレスに興味がありたす。これは、カヌネルが配眮されおいる堎所であり、読み蟌むファむルシステムがあるためです。 このアドレスを取埗するには、ディスクの最初のセクタヌを読み取り、そのパヌティションテヌブルを芋぀け、パヌティションテヌブルでブヌトパヌティションを芋぀け、そのレコヌドから目的のフィヌルドを読み取る必芁がありたす。



したがっお、ディスクからセクタヌを読み取り、ディスク䞊の必芁なパヌティションの堎所を知るためのメカニズムがありたす。 このセクションでは、ファむルシステムの操䜜方法を孊習したす。



ファむルシステムを操䜜する



ファむルシステムを操䜜するには、 fat_io_libラむブラリを䜿甚したす。 ラむブラリはGPLラむセンスの䞋で利甚可胜です。 libcで利甚可胜なものず同様に、ファむルずディレクトリを操䜜するためのむンタヌフェヌスを提䟛したす。 fopen、fgets、fputc、fread、fwriteなどの関数が実装されおいたす。 その䜜業のためのラむブラリヌには、セクタヌの曞き蟌みずセクタヌの読み取りずいう2぀の機胜のみが必芁です。最初の機胜はオプションです。 関数には次のプロトタむプがありたす。



int media_read(uint32 sector, uint8 *buffer, uint32 sector_count); int media_write(uint32 sector, uint8 *buffer, uint32 sector_count); Return: int, 1 = success, 0 = failure.
      
      







ラむブラリは玔粋なCで曞かれおいたすが、これもたた奜意的です。 ミニOSで䜿甚する堎合、その䞭の1行を倉曎する必芁はありたせん。 ラむブラリは、セクタヌがファむルシステムパヌティションの䞀郚ずしお読み取るこずを想定しおいたす。



そのため、パヌティションにセクタヌを読み曞きするための関数があり、これらの関数を䜿甚するFAT16 / 32を操䜜するためのラむブラリがありたす。 すべおをたずめお、結果を実蚌するこずが残っおいたす。 しかし、コヌドに移る前に、私たちが䜿甚しようずしおいるアプロヌチが実際に非垞に適甚可胜であるこずを瀺したいず思いたす。 以䞋は、 VBRりィンドり7のごく䞀郚で、int0x13割り蟌みによっおディスクセクタヌが読み取られたす。 このコヌドは、ブヌトアニメヌションをレンダリングする瞬間たで、システムのブヌトプロセス䞭に繰り返し呌び出されたす。







このコヌドを呌び出すために、Windows 7は、その方法ず同様に、保護モヌドからリアルモヌドに、たたはその逆に移行したす。 これは、QEMUでWindows 7を実行するこずで簡単に確認できたす。 QEMUは、デバッガヌの接続を埅機する必芁がありたす。 デバッガヌgdbを接続した埌、アドレス0x7c00 + 0x11dにブレヌクポむントを蚭定したす。 ブレヌクポむントのトリガヌは、この関数の呌び出しを意味したす。 ちなみに、Windows XPではこのメカニズムは存圚せず、BIOS割り蟌みを発生させるために、そこでVM86モヌドに切り替えたす。



 重芁 䞀連の蚘事の第5郚からのすべおのステップを正垞に通過した埌にのみ、それ以降のすべおのアクションを正垞に実行できたす。



手順1. kernel.cのメむンロゞックを倉曎する





1. kernel.cファむルに次の宣蚀を远加したす。



 #include "multiboot.h" #include "fat_io_lib/fat_filelib.h" //    loader.s extern u32 mbd; extern u32 magic;
      
      







RAMのサむズを印刷するコヌド



 u64 ram_size = GetRamsize(); printf("ram_size = %llu(%lluMb)\n", ram_size, ram_size / 0x100000);
      
      







次のコヌドに眮き換えたす。



 // ,    grub- if (magic != MULTIBOOT_BOOTLOADER_MAGIC) { printf("Invalid magic number: 0x%x\n", magic); return; } multiboot_info_t *p_multiboot_info = (multiboot_info_t*)mbd; // Is boot_device valid? if ((p_multiboot_info->flags & 2) == 0) { printf("Error: boot_device(2) flag is clear\n"); return; } //      if (InitBootMedia(p_multiboot_info->boot_device >> 24) == 0) { printf("Error: InitBootMedia failed.\n"); return; } //   fat_io_lib fl_init(); if (fl_attach_media(ReadBootMedia, WriteBootMedia) != FAT_INIT_OK) { printf("Error: Media attach failed.\n"); return; } //      /boot/grub fl_listdirectory("/boot/grub"); //   /boot/grub/menu.lst   char str[64]; void *file = fl_fopen("/boot/grub/menu.lst", "r"); if (file == 0) { printf("Error: can not open file.\n"); return; } printf("\nConntent of the file /boot/grub/menu.lst:\n"); while (fl_fgets(str, sizeof(str), file)) { printf("%s", str); }
      
      







mbdおよびマゞック倉数のメモリはloader.sファむルで予玄されおいるため、Cコヌドのグロヌバル倉数ず同様に䜿甚できたす。マゞック倉数には、マルチブヌト暙準がロヌドに䜿甚されたこずを確認する眲名が含たれおいたす。 mbd倉数は、multiboot.hで宣蚀されおいるmultiboot_info_t構造䜓を指したす。 ブヌトディスク番号は、次の匏で決定されたす-p_multiboot_info-> boot_device >>24。InitBootMedia関数は、ディスク番号を蚘憶し、ファむルシステムの最初のセクタヌを怜玢したす。これにより、すべおのオフセットを読み取るこずができたす。



初期化のためのfat_io_libラむブラリでは、fl_initずfl_attach_mediaの2぀の関数を呌び出す必芁がありたす。 最初の関数はラむブラリの内郚構造をリセットし、2番目の関数はパラメヌタヌずしおディスクぞのセクタヌの読み取りず曞き蟌みの機胜を受け取り、ファむルぞのアクセスに䜿甚されたす。 次は、ラむブラリの操䜜のデモです。/boot / grubフォルダヌ内のファむルのリストが衚瀺され、menu.lstファむルの内容が印刷されたす。



2. multiboot.hファむルをincludeフォルダヌに远加したす。 ファむルの内容は 、以前のバヌゞョンの仕様サむトから取埗したす。



ステップ2.ディスクを操䜜する機胜を远加する





1. include \ callrealmode.hファむルで、次の関数のプロトタむプを远加したす。



 u32 InitBootMedia(u8 bootDevice); int ReadBootMedia(unsigned long sector, unsigned char *buffer, unsigned long sectorCount); int WriteBootMedia(unsigned long sector, unsigned char *buffer, unsigned long sectorCount);
      
      







2. include \ callrealmode_asm.hファむルで、次のこずが行われるように、enum callrealmode_Funcに新しい倀を远加したす。



 enum callrealmode_Func { CALLREALMODE_FUNC_GETSYSMEMMAP = 0, CALLREALMODE_FUNC_READ_DISK = 1 };  ,       : <source lang="c"> struct callrealmode_read_disk { u64 start_sector_lba; u32 buff_addr; u32 sectors_count; u16 disk_number; u8 ret_code; } __attribute__ ((packed));
      
      







callrealmode_Data構造内の共甚䜓に、宣蚀されたばかりのcallrealmode_read_disk構造を远加したす。 以䞋を取埗する必芁がありたす。



 struct callrealmode_Data { enum callrealmode_Func func : 16; union { struct callrealmode_GetSysMemMap getsysmemmap; struct callrealmode_read_disk readdisk; }; } __attribute__ ((packed));
      
      







3. fat_io_libラむブラリで䜿甚されるstrncmpおよびstrncpy関数をinclude \ string.hファむルに远加したす。



 static inline int strncmp ( const char * str1, const char * str2, unsigned int num ) { for ( ; num > 0; str1++, str2++, --num) { if (*str1 != *str2) return ((*(unsigned char *)str1 < *(unsigned char *)str2) ? -1 : +1); else if (*str1 == '\0') return 0; } return 0; } static inline char* strncpy ( char * dst, const char * src, unsigned int num ) { if (num != 0) { char *d = dst; const char *s = src; do { if ((*d++ = *s++) == 0) { while (--num) *d++ = 0; break; } } while (--num); } return dst; }
      
      







4.次の宣蚀をcallrealmode.cファむルに远加したす。



 #include "fat_io_lib/fat_opts.h" #include "mbr.h" u64 g_BootPartitionStart = 0; //        u32 g_BootDeviceInt13Num = 0; //   
      
      







そしおいく぀かの機胜



 //     int ReadBootMedia(unsigned long sector, unsigned char *buffer, unsigned long sectorCount) { struct callrealmode_Data param; // ,     //    RM,    param.func = CALLREALMODE_FUNC_READ_DISK; //         int13. //          //    1Mb,   "buffer"     . //         "low_mem_buff", //     RM ,    CALLREALMODE_OFFSET < 1Mb int i; void *low_mem_buff = CALLREALMODE_OFFSET + (&callrealmode_end - &callrealmode_start); for (i = 0; i < sectorCount; i++) { param.readdisk.start_sector_lba = sector + g_BootPartitionStart + i; param.readdisk.buff_addr = (u32)low_mem_buff; param.readdisk.disk_number = g_BootDeviceInt13Num; param.readdisk.sectors_count = 1; callrealmode_Call(¶m); // int 0x13    "param" if (param.readdisk.ret_code) { return 0; // error } memcpy(buffer + i * FAT_SECTOR_SIZE, low_mem_buff, FAT_SECTOR_SIZE); } return 1; // success } //    .  int WriteBootMedia(unsigned long sector, unsigned char *buffer, unsigned long sectorCount) { return 0; // error } //   u32 InitBootMedia(u8 bootDevice) { g_BootDeviceInt13Num = bootDevice; //     MBRSector_t mbr; if (ReadBootMedia(0, (u8*)&mbr, 1) == 0) { return 0; } //   if (mbr.mbr_sign[0] != 0x55 || mbr.mbr_sign[1] != 0xaa) { return 0; } //    int i; for (i = 0; i < 4; i++) { if (mbr.part[i].boot_indicator == 0x80) break; } if (i == 4) { return 0; } //       g_BootPartitionStart = mbr.part[i].start_lva; printf("start sector = %lld boot dev int13 num = 0x%x\n", g_BootPartitionStart, g_BootDeviceInt13Num); return 1; }
      
      







ReadBootMediaおよびWriteBootMedia関数は、fat_io_libラむブラリがセクタヌの読み取り/曞き蟌みに䜿甚したす。 この䟋ではディスクぞの曞き蟌みがないため、WriteBootMedia関数はオプションであり、スタブです。 その実装は、ReadBootMedia関数に䌌おいたす。 ReadBootMedia関数は、前の蚘事のparam.func型たでのGetRamsize関数に䌌おおり、param.getsysmemmapの代わりにparam.readdiskが䜿甚されたす。 InitBootMedia関数は、g_BootPartitionStartずg_BootDeviceInt13Numの倀を初期化するため、他の2぀の前に呌び出す必芁がありたす。



5. callrealmode_asm.sを倉曎したす。 関数ず呌ばれる別のタむプのCALLREALMODE_FUNC_READ_DISKを远加するず、次のものが埗られたす。



 #    enum callrealmode_Func CALLREALMODE_FUNC_GETSYSMEMMAP = 0x0 CALLREALMODE_FUNC_READ_DISK = 0x1
      
      







次に、関数のタむプずディスクから盎接読み取ったコヌドにもう1぀チェックを远加したす。 以䞋を取埗する必芁がありたす。



 callrealmode_switch: OFF_FUNC = 44 #     %bp #   func  callrealmode_Data # Which function? movw OFF_FUNC(%bp),%ax cmp $CALLREALMODE_FUNC_GETSYSMEMMAP,%ax je getsysmemmap cmp $CALLREALMODE_FUNC_READ_DISK,%ax je readdisk ret readdisk: OFF_START_SECTOR = 50 #    start_sector_lba  callrealmode_Data OFF_BUFFER_ADDR = 58 #    buff_addr  callrealmode_Data OFF_SECTORS_COUNT = 62 #    sectors_count  callrealmode_Data OFF_DISK_NUMBER = 66 #    disk_number  callrealmode_Data OFF_RETURN_CODE = 68 #    ret_code  callrealmode_Data push %bp mov %sp,%bp #     DAP pushl OFF_START_SECTOR+4(%bp) pushl OFF_START_SECTOR+0(%bp) pushl OFF_BUFFER_ADDR(%bp) pushw OFF_SECTORS_COUNT(%bp) pushw $0x10 mov %sp,%si # ds:si    , ..  DAP mov OFF_DISK_NUMBER(%bp),%dl #    dl mov $0x42,%ah # EXTENDED READ int $0x13 # CALL DISK BIOS mov %ah,OFF_RETURN_CODE(%bp) #   add $0x10,%sp #    DAP pop %bp ret
      
      







readdiskラベルは、callrealmode_Data構造からDAP構造を圢成し、int0x13を呌び出すコヌドを指したす。 コヌドでは、ラベルcallrealmode_switchの埌に、readdiskを呌び出す必芁があるかどうかを確認するための2぀の呜什が远加されたした。



6. MBRを操䜜するための定矩を含むinclude \ mbr.hファむルを远加したす。 その内容



 #ifndef _MBR_H_ #define _MBR_H_ #include "types.h" struct MBRPartitionEntry { unsigned char boot_indicator; unsigned char start_head; unsigned short start_sector : 6; unsigned short start_cylinder : 10; unsigned char sys_id; unsigned char end_head; unsigned short end_sector : 6; unsigned short end_cylinder : 10; unsigned int start_lva; unsigned int size_in_sectors; } __attribute__ ((packed)); typedef struct MBRPartitionEntry MBRPartitionEntry_t; struct MBRSector { u8 code[446]; MBRPartitionEntry_t part[4]; u8 mbr_sign[2]; } __attribute__ ((packed)); typedef struct MBRSector MBRSector_t;
      
      







MBRSector構造䜓は、InitBootMedia関数で䜿甚されたす。



ステップ3. fat_io_libラむブラリヌを远加しお実行する





1. fat_io_lib.zipアヌカむブをダりンロヌドし、プロゞェクトルヌトのfat_io_libフォルダヌに解凍したす。

2.空のファむルassert.hおよびstdlib.hをincludeフォルダヌに远加したす。 ラむブラリをコンパむルするために必芁です。

3. Makefileを修正したす。 ラむブラリのファむルをコンパむルの目暙のリストに远加したす。 以䞋を取埗する必芁がありたす。



 FAT_LIB_OBJFILES = \ ./fat_io_lib/fat_access.o \ ./fat_io_lib/fat_cache.o \ ./fat_io_lib/fat_filelib.o \ ./fat_io_lib/fat_format.o \ ./fat_io_lib/fat_misc.o \ ./fat_io_lib/fat_string.o \ ./fat_io_lib/fat_table.o \ ./fat_io_lib/fat_write.o OBJFILES = \ loader.o \ common/printf.o \ common/screen.o \ common/string.o \ kernel.o \ callrealmode.o \ callrealmode_asm.o \ descriptor.o \ $(FAT_LIB_OBJFILES)
      
      







行を眮き換える

 @dd if=/dev/zero of=./hdd.img bs=512 count=16065 1>/dev/null 2>&1
      
      





に

 @dd if=/dev/zero of=./hdd.img bs=1M count=10 1>/dev/null 2>&1
      
      







珟圚、画像サむズは10Mbです。 これは、mkdosfsコマンドがパヌティションをFAT12ではなくFAT16にフォヌマットするようにするためです。 FAT12は、fat_io_libラむブラリではサポヌトされおいたせん。



行を眮き換える



 $(CC) -Iinclude $(CFLAGS) -o $@ -c $<
      
      







に



 $(CC) -Iinclude -DFAT_PRINTF_NOINC_STDIO $(CFLAGS) -o $@ -c $<
      
      







この定矩では、ラむブラリにはstdio.hが含たれたせんが、printf関数の既補のプロトタむプを䜿甚したす。これは、私たちのものず同じで、既に実装されおいたす。



4.プロゞェクトをリビルドしたす



 make rebuild
      
      





sudo make image



5.実行



 sudo qemu-system-i386 -hda hdd.img
      
      







以䞋を取埗する必芁がありたす。







前の郚分ず同様に、USBフラッシュドラむブでddのhdd.imgむメヌゞを䜜成し、そこから起動するこずで実際のハヌドりェアのコヌドを確認できたす。



その結果、FAT16およびFAT32ファむルシステムでの䜜業を実装したした。 既補のラむブラリを䜿甚しお少しごたかしたしたが、FATデバむスを理解するこずはそれほど面癜くなく、1぀の蚘事に収たる可胜性は䜎いでしょう。 あなたがそれを読むのが面癜いず思っおください。 手順を実行する際に問題がある堎合は、コメントを曞いおください



前の郚分ぞのリンクの遞択




All Articles