U0 BlkDevLockFwdingSet(CBlkDev *bd)
{  //If two blkdevs on same controller, use just one lock
        CBlkDev *bd1;
        I64              i;

        switch (bd->type)
        {
                case BDT_RAM:
                        break;

                case BDT_ISO_FILE_READ:
                case BDT_ISO_FILE_WRITE:
                        bd->lock_fwding = Letter2BlkDev(*bd->file_disk_name);
                        break;

                case BDT_ATA:
                case BDT_ATAPI:
                        for (i = 0; i < BLKDEVS_NUM; i++)
                        {
                                bd1 = &blkdev.blkdevs[i];
                                if (bd1->bd_signature == BD_SIGNATURE_VAL && bd != bd1 &&
                                        (bd1->type == BDT_ATAPI || bd1->type == BDT_ATA) &&
                                        bd1->base0 == bd->base0)
                                {
                                        bd->lock_fwding = bd1;
                                        break;
                                }
                        }
                        break;
        }
}

I64 BlkDevAdd(CBlkDev *bd, I64 prt_num=I64_MIN, Bool whole_drive, Bool make_free)
{//It will mount just one partition of prt_num>=0.
//When repartitioing whole drive, whole_drive=TRUE.
        I64                      i, j, ext_base, offset, res = 0, num = 0;
        CDrive          *drive;
        CRedSeaBoot      br;
        CMasterBoot      mbr;

        bd->bd_signature = BD_SIGNATURE_VAL;
        if (make_free)
                drive = DriveMakeFreeSlot(bd->first_drive_let);
        else
                drive = DriveMakeFreeSlot(DriveNextFreeLet(bd->first_drive_let));
        drive->bd = bd;
        drive->drv_offset = bd->drv_offset;
        drive->size = bd->max_blk + 1 - bd->drv_offset;
        switch (bd->type)
        {
                case BDT_RAM:
                case BDT_ISO_FILE_READ:
                case BDT_ISO_FILE_WRITE:
                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                        drive->prt_num = num;
                        drive->fs_type = FSt_REDSEA;
//This is to force creation of a RAM
                        //drive during boot, so it is probably
                        //MAlloced to the same addr and can
                        //be assumed to be already formatted.
                        //If this line is removed, RAM Drives
                        //will be alloced on a just-in-time
                        //basis.
                        if (BlkDevInit(bd))
                                res++;
                        else
                                drive->drive_signature = 0;
                        break;

                case BDT_ATA:
                        drive->drive_signature = DRIVE_SIGNATURE_VAL; //Temporarily validate
                        if (!BlkDevInit(bd))
                                drive->drive_signature = 0; //Revoke validation
                        else
                        {
                                drive->drive_signature = 0; //Revoke validation
                                if (whole_drive)
                                {
                                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                                        drive->prt_num = num;
                                        res++;
                                        drive->fs_type = FSt_REDSEA;
                                        drive->size = bd->max_blk + 1 - bd->drv_offset;
//The following read is a test read.
                                        //if it hangs, the drive is not supported.
                                        AHCIAtaBlksRead(bd, &mbr, 0, 1);
                                        break;
                                }
                                offset = 0;
                                ext_base = INVALID_CLUS;
                                while (prt_num < 0 || num <= prt_num)
                                {
                                        AHCIAtaBlksRead(bd, &mbr, offset, 1);
                                        if (mbr.signature != 0xAA55)
                                                break;
                                        j = -1;
                                        for (i = 0; i < 4 && (prt_num < 0 || num <= prt_num); i++)
                                        {
                                                if (mbr.p[i].type)
                                                {
                                                        if (make_free)
                                                                drive = DriveMakeFreeSlot(bd->first_drive_let + res);
                                                        else
                                                                drive = DriveMakeFreeSlot(DriveNextFreeLet(bd->first_drive_let + res));
                                                        drive->bd                       = bd;
                                                        drive->drv_offset       = mbr.p[i].offset + offset;
                                                        drive->size             = mbr.p[i].size;
                                                        switch (mbr.p[i].type)
                                                        {
                                                                case MBR_PT_REDSEA:
                                                                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                                                                        drive->prt_num = num;
                                                                        res++;
                                                                        drive->fs_type = FSt_REDSEA;
                                                                        RedSeaInit(drive);
                                                                        break;

                                                                case MBR_PT_FAT32a:
                                                                case MBR_PT_FAT32b:
                                                                case MBR_PT_FAT32c:
                                                                case MBR_PT_FAT32d:
                                                                case MBR_PT_FAT32e:
                                                                case MBR_PT_FAT32f:
                                                                        AHCIAtaBlksRead(bd, &br, drive->drv_offset, 1);

                                                                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                                                                        drive->prt_num = num;
                                                                        res++;
                                                                        if (br.signature == MBR_PT_REDSEA)
                                                                        {
                                                                                drive->fs_type = FSt_REDSEA;
                                                                                RedSeaInit(drive);
                                                                        }
                                                                        else
                                                                        {
                                                                                drive->fs_type = FSt_FAT32;
                                                                                FAT32Init(drive);
                                                                        }
                                                                        break;

                                                                case MBR_PT_NTFS:
                                                                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                                                                        drive->prt_num = num;
                                                                        res++;
                                                                        drive->fs_type = FSt_NTFS;
                                                                        break;

                                                                case 5:
                                                                case 15:
                                                                        j = i;
                                                                        break;

                                                                default:
                                                                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                                                                        drive->prt_num = num;
                                                                        res++;
                                                                        drive->fs_type = FSt_UNKNOWN;
                                                        }
                                                        num++;
                                                }
                                        }
                                        if (Letter2BlkDevType(bd->first_drive_let + res) != bd->type)
                                                break;
                                        if (j < 0)
                                                break;
                                        if (!mbr.p[j].offset)
                                                break;
                                        if (ext_base == INVALID_CLUS)
                                        {
                                                offset = mbr.p[j].offset;
                                                ext_base = offset;
                                        }
                                        else
                                                offset = mbr.p[j].offset + ext_base;
                                }
                        }
                        break;

                case BDT_ATAPI:
                        drive->drive_signature = DRIVE_SIGNATURE_VAL;
                        drive->prt_num = num;
                        res++;
                        drive->fs_type = FSt_ISO9660; //Start with this
                        drive->size = 0;
                        break;
        }
        if (res)
                BlkDevLockFwdingSet(bd);
        else
                BlkDevDel(bd);

        return res;
}

Bool DriveEnable(U8 drv_let, Bool val)
{//Can unmount or remount, but not mount the first time.
        CDrive *drive;

        if (drive = Letter2Drive(drv_let, FALSE))
                return !LBEqual(&drive->fs_type, FStf_DISABLE, !val);
        else
                return FALSE;
}

I64 SysI64Get()
{
        U8 st[STR_LEN];

        StrNGet(st, STR_LEN, FALSE);

        return Str2I64(st, 16);
}

U0 BlkDevsInitAll()
{
        CBlkDev *bd;
        I64              i;

        blkdev.blkdevs  = CAlloc(sizeof(CBlkDev) * BLKDEVS_NUM);
        blkdev.drvs             = CAlloc(sizeof(CDrive)  * DRIVES_NUM);
        for (i = 0; i < DRIVES_NUM; i++)
                blkdev.let_to_drive[i] = &blkdev.drvs[i];

        AHCIInit;

        if (!blkdev.ahci_hba)
        {
                "\nZealOS requires AHCI.\n\n"
                "If in IDE compatibility mode, switch to SATA mode.\n"
                "If running in a VM, ensure disks are organized under a SATA controller.\n\n"
                "Rebooting in 10 seconds...\n";
                Sleep(10*1000);
                Reboot;
        }

        #exe {
                if (kernel_config->opts[CONFIG_MOUNT_IDE_AUTO])
                        StreamPrint("MountIDEAuto;");
                StreamPrint("#exe {Option(OPTf_WARN_PAREN, OFF);}");
                StreamDoc(kernel_config->add_dev);
                StreamPrint("#exe {Option(OPTf_WARN_PAREN, ON);}");
        };
}