FAT16 - 创建虚拟硬盘驱动器并读取它 - 由于某种原因不存在数据

问题描述 投票:0回答:1

我正在编写一个操作系统项目。我正在用 C++ 和一些汇编语言开发我的项目 linux mint(并使用 gcc 和 GAS 编译它)并在 qemu 机器中运行它(我从编译后的代码中创建一个 .iso 文件,然后将其加载到 qemu 机器中)。 我目前正处于编写文件系统的阶段,为此我选择了FAT16,因为我已经开发了一个磁盘接口(具体来说是ATA PIO)。虚拟硬盘驱动器以及 .iso 文件被加载到 qemu 机器中。然而,在将虚拟硬盘加载到 qemu 机器之前,我首先将其格式化(使用 GParted 工具),使其拥有一个跨越整个磁盘且定义为 FAT16 的分区,具体方式如下:

/usr/bin/qemu-img create -f qcow2 os-disl.qcow2 512M
    # Start GParted virtual machine to define ext2 partition
    /usr/bin/qemu-system-i386 -cdrom gparted-live-1.6.0-3-i686.iso -hda os-disk.qcow2 -boot d -m 512

此外,一旦虚拟硬盘驱动器存在,我会将其与我的 iso 文件一起加载到新的 qemu 机器中:

/usr/bin/qemu-system-i386 -cdrom ./objects/mykernel.iso -drive file="$DISK_PATH",cache=none -boot d -m 512 -no-reboot -no-shutdown -d int -M smm=off -s

在打开的GParted虚拟机中我创建一个新的分区表,然后添加一个新分区,包括图片:

enter image description here (我使用 msdos 选项创建了一个新的分区表)

enter image description here

enter image description here

enter image description here

创建虚拟硬盘后,我想对其执行读取操作,并解析数据,以便我可以真正开始实现文件系统。 我首先通过以下方式实现磁盘通信接口:

磁盘.h:

#pragma once
#include <types.h>
#include <port/port.h>

void printf(uint8_t* ltr, int flag);   
void printfHex(uint8_t key);
class ata
{
protected:
    bool master;
    Port16Bit dataPort;
    Port8Bit errorPort;
    Port8Bit sectorCountPort;
    Port8Bit lbaLowPort;
    Port8Bit lbaMidPort;
    Port8Bit lbaHiPort;
    Port8Bit devicePort;
    Port8Bit commandPort;
    Port8Bit controlPort;
public:
    
    ata(bool master, uint16_t portBase);
    ~ata();
    
    void Identify();
    void Read28(uint32_t sectorNum, int count = 512, uint8_t* ptr=nullptr);
    void Write28(uint32_t sectorNum, uint8_t* data, uint32_t count);
    void Flush();
    
    
};

磁盘.cpp:

#include <fat16/disk.h>


ata::ata(bool master, uint16_t portBase)
:   dataPort(portBase),
    errorPort(portBase + 0x1),
    sectorCountPort(portBase + 0x2),
    lbaLowPort(portBase + 0x3),
    lbaMidPort(portBase + 0x4),
    lbaHiPort(portBase + 0x5),
    devicePort(portBase + 0x6),
    commandPort(portBase + 0x7),
    controlPort(portBase + 0x206)
{
    this->master = master;
}

ata::~ata()
{
}
            
void ata::Identify()
{
    devicePort.Write(master ? 0xA0 : 0xB0);
    controlPort.Write(0);
    
    devicePort.Write(0xA0);
    uint8_t status = commandPort.Read();
    if(status == 0xFF)
        return;
    
    
    devicePort.Write(master ? 0xA0 : 0xB0);
    sectorCountPort.Write(0);
    lbaLowPort.Write(0);
    lbaMidPort.Write(0);
    lbaHiPort.Write(0);
    commandPort.Write(0xEC); // identify command
    
    
    status = commandPort.Read();
    if(status == 0x00)
        return;
    
    while(((status & 0x80) == 0x80)
       && ((status & 0x01) != 0x01))
        status = commandPort.Read();
        
    if(status & 0x01)
    {
        return;
    }
    
    for(int i = 0; i < 256; i++)
    {
        uint16_t data = dataPort.Read();
        char *text = "  \0";
        text[0] = (data >> 8) & 0xFF;
        text[1] = data & 0xFF;
    }
}

void ata::Read28(uint32_t sectorNum, int count, uint8_t* ptr)
{
    if(sectorNum > 0x0FFFFFFF)
        return;
    
    devicePort.Write((master ? 0xE0 : 0xF0) | ((sectorNum >> 24) & 0x0F));
    errorPort.Write(0);
    sectorCountPort.Write(1);
    lbaLowPort.Write(sectorNum & 0x000000FF);
    lbaMidPort.Write((sectorNum & 0x0000FF00) >> 8);
    lbaHiPort.Write((sectorNum & 0x00FF0000) >> 16);
    commandPort.Write(0x20);
    
    uint8_t status = commandPort.Read();
    while (((status & 0x80) == 0x80) && ((status & 0x01) != 0x01))
        status = commandPort.Read();
        
    if (status & 0x01)
        return;
    
    for (int i = 0; i < count; i += 2)
    {
        uint16_t wdata = dataPort.Read();
        
        ptr[i] = wdata & 0xFF;
        if (i + 1 < count)
            ptr[i + 1] = (wdata >> 8) & 0xFF;
    }
    
    for (int i = count + (count % 2); i < 512; i += 2)
        dataPort.Read();
}

void ata::Write28(uint32_t sectorNum, uint8_t* data, uint32_t count)
{
    if(sectorNum > 0x0FFFFFFF)
        return;
    if(count > 512)
        return;

    // Set the drive and LBA high 4 bits
    devicePort.Write((master ? 0xE0 : 0xF0) | ((sectorNum >> 24) & 0x0F));
    
    errorPort.Write(0);
    sectorCountPort.Write(1);
    lbaLowPort.Write(sectorNum & 0xFF); // LBA low byte
    lbaMidPort.Write((sectorNum >> 8) & 0xFF); // LBA mid byte
    lbaHiPort.Write((sectorNum >> 16) & 0xFF); // LBA high byte
    commandPort.Write(0x30); // Write sectors command
    
    // Wait until the drive is ready
    uint8_t status = commandPort.Read();
    while ((status & 0x80) && !(status & 0x08)) {
        status = commandPort.Read();
    }
    
    // Write the data
    for (int i = 0; i < count; i += 2)
    {
        uint16_t wdata = data[i];
        if (i + 1 < count)
            wdata |= ((uint16_t)data[i + 1]) << 8;
        dataPort.Write(wdata);
    }
    
    // Pad remaining data with zeros if count is less than 512
    for (int i = count + (count % 2); i < 512; i += 2)
        dataPort.Write(0x0000);
}

void ata::Flush()
{
    devicePort.Write( master ? 0xE0 : 0xF0 );
    commandPort.Write(0xE7);

    uint8_t status = commandPort.Read();
    if(status == 0x00)
        return;
    
    while(((status & 0x80) == 0x80)
       && ((status & 0x01) != 0x01))
        status = commandPort.Read();
        
    if(status & 0x01)
    {
        return;
    }
}

创建磁盘接口后,我创建该类的实例并开始读取操作:

ata ata0m(true, 0x1F0);
ata0m.Identify();

FAT16.h:

#pragma once
#include <types.h>
#include <fat16/disk.h>
#include <stddef.h>
extern ata ata0m;
typedef struct 
{
    // The mbr contains three different parts
    // Boot code - first 446 bytes
    // Partition tables - 4 entries with each sized 16 bytes that describe different partitions on the disk 
    // The struct describes a single entry
    uint8_t  bootIndicator;
    uint8_t  startHead;
    uint8_t  startSector;
    uint8_t  startCylinder;
    uint8_t  partitionType;
    uint8_t  endHead;
    uint8_t  endSector;
    uint8_t  endCylinder;
    uint32_t relativeSector;
    uint32_t totalSectors;
} __attribute__((packed)) MBR_PartitionEntry;


typedef struct 
{
    // Implement mbr strcure, we will load this into sector 0
    uint8_t  bootstrap[446];
    MBR_PartitionEntry partitionTable[4];
    uint16_t signature; // 0x55AA
} __attribute__((packed)) MBR;


typedef struct 
{
    // The first sector of each partition is called the boot sector and contains some metadata about the partition and the file system
    uint8_t  BS_jmpBoot[3];
    uint8_t  BS_OEMName[8];
    uint16_t BPB_BytsPerSec; // 512
    uint8_t  BPB_SecPerClus; // 8 
    uint16_t BPB_RsvdSecCnt;
    uint8_t  BPB_NumFATs; // usually 2
    uint16_t BPB_RootEntCnt; // garbage values, must be set
    uint16_t BPB_TotSec16; 
    uint8_t  BPB_Media;
    uint16_t BPB_FATSz16;
    uint16_t BPB_SecPerTrk;
    uint16_t BPB_NumHeads;
    uint32_t BPB_HiddSec;
    uint32_t BPB_TotSec32;
    uint8_t  BS_DrvNum;
    uint8_t  BS_Reserved1;
    uint8_t  BS_BootSig;
    uint32_t BS_VolID;
    uint8_t  BS_VolLab[11];
    uint8_t  BS_FilSysType[8];
} __attribute__((packed)) FAT16_BootSector;



void Read_MBR();
void readBootSector(); // Use ata_read_sector  
FAT16_BootSector parseBootSector(uint8_t* bootSectorBuffer); // Receive the data from previous function and parse

// MBR information about our FAT16 partition
extern uint32_t lba_start;
extern uint32_t lba_limit;
extern uint8_t partition_type;

// Boot sector information about FAT16
extern uint16_t bytes_per_sector;
extern uint8_t sectors_per_cluster;
extern uint16_t reserved_sectors;
extern uint8_t number_of_FATs;
extern uint16_t root_entries;
extern uint16_t total_sectors;

extern uint16_t sectors_per_FAT;
extern uint32_t root_directory_sectors;
extern uint32_t first_data_sector;
extern uint32_t total_clusters;
// Calculate the starting sector of the FAT and root directory
extern uint32_t fat_start_sector;
extern uint32_t root_dir_start_sector;

FAT.cpp:

#include <fat16/fat16.h>
// This is the beggining of the file system - which is FAT16


MBR mbr;
FAT16_BootSector boot_sector;
uint32_t lba_start;
uint32_t lba_limit;

void Read_MBR()
{
    // Write to the first sector of the disk - which is the MBR
    // In: struct sized 512 that includes a partition table
    // Out: None

    ata0m.Read28(0, 512, (uint8_t*)&mbr); // Load MBR into mbr struct, this will actually load correctly the data

    if (mbr.signature != 0xaa55) printf((uint8_t*)"error", 0);

    MBR_PartitionEntry* firstPartition = &(mbr.partitionTable[0]); // pointer to the first partition table entry

    uint32_t lba_start = firstPartition->relativeSector; // Actual beggining of our partition

    uint32_t lba_limit = firstPartition->totalSectors; // Size of partition

    uint8_t partition_type = firstPartition->partitionType;

    printfHex(partition_type);
    printf((uint8_t*)"\n",0);

    // Purpose of function: to set up values in the lba global variables  
}


void readBootSector() 
{
    // Implement reading of the boot sector using your disk I/O functions

    // First we read the entire sector into the bootsector structure
    // Then parse it anf gain the useful data
    ata0m.Read28(lba_start, 512, (uint8_t*)&boot_sector);

    uint16_t bytes_per_sector = boot_sector.BPB_BytsPerSec;
    uint8_t sectors_per_cluster = boot_sector.BPB_SecPerClus;
    uint16_t reserved_sectors = boot_sector.BPB_RsvdSecCnt;
    uint8_t number_of_FATs = boot_sector.BPB_NumFATs;
    uint16_t root_entries = boot_sector.BPB_RootEntCnt;
    uint16_t total_sectors = boot_sector.BPB_TotSec16;
    if (total_sectors == 0) 
    {
        total_sectors = boot_sector.BPB_TotSec32;
    }
    uint16_t sectors_per_FAT = boot_sector.BPB_FATSz16;
    uint32_t root_directory_sectors = ((root_entries * 32) + (bytes_per_sector - 1)) / bytes_per_sector;
    uint32_t first_data_sector = reserved_sectors + (number_of_FATs * sectors_per_FAT) + root_directory_sectors;
    uint32_t total_clusters = (total_sectors - first_data_sector) / sectors_per_cluster;

    // Calculate the starting sector of the FAT and root directory - this is the important thing
    uint32_t fat_start_sector = lba_start + reserved_sectors;
    uint32_t root_dir_start_sector = fat_start_sector + (number_of_FATs * sectors_per_FAT);


    printfHex(sectors_per_cluster);
    printf((uint8_t*)"\n",0);


    printfHex(number_of_FATs);
    printf((uint8_t*)"\n",0);

    
}

我首先调用Read_MBR,然后调用readBootSector。在这些函数中,我使用 print 语句来产生以下输出:

enter image description here

这对我来说很奇怪。根据我的理解,我应该在像 number_of_FATs 这样的变量中看到不同的值(通常是 2)。因此,我使用以下命令查看了虚拟硬盘文件 (os-disk.qcow2) 的十六进制转储:

sudo dd if=/dev/loop0 bs=4096 count=1 | hexdump -C

在此之前我使用

将虚拟硬盘安装到我的机器上
sudo losetup -P /dev/loop0 os-disk.qcow2
sudo fdisk -l /dev/loop0
sudo dd if=/dev/loop0 bs=512 count=1 | hexdump -C

最后一个命令查看文件的前 512 个字节。我得到的输出是:

fridkin@fridkin-virtual-machine:~/os/yb-os$ sudo dd if=/dev/loop0 bs=512 count=1 | hexdump -C
00000000  51 46 49 fb 00 00 00 03  00 00 00 00 00 00 00 00  |QFI.............|
00000010  00 00 00 00 00 00 00 10  00 00 00 00 20 00 00 00  |............ ...|
00000020  00 00 00 00 00 00 00 01  00 00 00 00 00 03 00 00  |................|
00000030  00 00 00 00 00 01 00 00  00 00 00 01 00 00 00 00  |................|
00000040  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000060  00 00 00 04 00 00 00 70  00 00 00 00 00 00 00 00  |.......p........|
00000070  68 03 f8 57 00 00 01 80  00 00 64 69 72 74 79 20  |h..W......dirty |
00000080  62 69 74 00 00 00 00 00  00 00 00 00 00 00 00 00  |bit.............|
00000090  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000a0  00 00 00 00 00 00 00 00  00 01 63 6f 72 72 75 70  |..........corrup|
000000b0  74 20 62 69 74 00 00 00  00 00 00 00 00 00 00 00  |t bit...........|
000000c0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000000d0  00 00 00 00 00 00 00 00  00 02 65 78 74 65 72 6e  |..........extern|
000000e0  61 6c 20 64 61 74 61 20  66 69 6c 65 00 00 00 00  |al data file....|
000000f0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000100  00 00 00 00 00 00 00 00  00 03 63 6f 6d 70 72 65  |..........compre|
00000110  73 73 69 6f 6e 20 74 79  70 65 00 00 00 00 00 00  |ssion type......|
00000120  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000130  00 00 00 00 00 00 00 00  00 04 65 78 74 65 6e 64  |..........extend|
00000140  65 64 20 4c 32 20 65 6e  74 72 69 65 73 00 00 00  |ed L2 entries...|
00000150  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000160  00 00 00 00 00 00 00 00  01 00 6c 61 7a 79 20 72  |..........lazy r|
00000170  65 66 63 6f 75 6e 74 73  00 00 00 00 00 00 00 00  |efcounts........|
00000180  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000190  00 00 00 00 00 00 00 00  02 00 62 69 74 6d 61 70  |..........bitmap|
000001a0  73 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |s...............|
000001b0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000001c0  00 00 00 00 00 00 00 00  02 01 72 61 77 20 65 78  |..........raw ex|
000001d0  74 65 72 6e 61 6c 20 64  61 74 61 00 00 00 00 00  |ternal data.....|
000001e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
1+0 records in
1+0 records out
512 bytes copied, 0.0314317 s, 16.3 kB/s
00000200

这对我来说根本不正确。因此我怀疑我在格式化文件时在某个地方犯了错误。有人有想法吗?

我希望至少能在文件的最后两个字节处看到 MBR 签名,即

0x55AA
。再次感谢! 对于感兴趣的人,这里是 github 存储库的链接:https://github.com/yfrandom2020/yb-os/tree/main

c++ osdev hexdump fat ata
1个回答
0
投票

这里有多个问题。

1。代码错误

uint32_t lba_start;

void Read_MBR()
{
    /// ...
    uint32_t lba_start = firstPartition->relativeSector; // Actual beggining of our partition
   /// ...
}

您看到这里的问题了吗?

以后如何避免这种情况?打开编译器警告。如果它们已打开,请阅读它们。如果您愿意的话,gcc 会警告您此错误。更好的是,将警告变成错误。

-Wall -Werror
是一种很好的生活方式——你会收到一些烦人的警告来处理,但这比不得不寻找一些愚蠢的错误要好得多。另外,删除
-fpermissive

此外,学习如何调试内核也是个好主意。对于您当前的设置来说,这并不是一件难事,如果您知道如何进行调试,它可能会有很大帮助。只需将

-s -S
添加到您的 qemu 命令中,这样它就会等待 gdb 连接到它,运行 qemu,运行
gdb -ex "target remote :1234" objects/mykernel.bin
,在您想要挂钩的任何位置设置一些断点(比方说
break initializers()
),然后开始加载使用
cont
进行处理。

2。 Qcow 文件格式

告诉过你一次,现在再告诉你一次。使用原始磁盘驱动器格式而不是 qcow2。一般来说,qcow 是一种很好的用于虚拟机的格式,但在 qemu 之外使用它并不是那么透明。使用原始磁盘映像进行操作系统开发 - 您可以使用循环设备安装它,您可以

dd
它,您可以启动该死的十六进制编辑器并查看那里的实际是什么。如果你使用它,你可能会发现第三个问题:

3.没有 FAT 文件系统

正如标题所说。您已创建分区但从未格式化它。没有要读取的 FAT 元数据。

© www.soinside.com 2019 - 2024. All rights reserved.