操作系统的第二个程序 - 分区的启动程序

MBR程序找到活动分区,并且把活动分区的启动扇区读到了600处,然后开始执行这里的程序,所以把这个程序称为操作系统的第二个程序也算正确。

1. 我使用的分区格式

分区的启动程序自然会跟分区格式紧密相关了,首先要知道分区格式,才能根据分区格式来写启动程序。因为我不能在设计了自己的分区格式之后,才在自己的分区格式上开发操作系统,所以我只能使用现有的分区格式。

使用哪个?只能是 FAT32 。简单,好用等等等,其实,是因为我只熟悉FAT32。

等我把自己的简单文件系统(Simple File System, SFS)搞出来以后,就使用SFS了。

2. 分区启动程序的作用

我认为分区启动程序的主要作用是在启动分区里找到系统装载程序的文件,然后将系统装载程序读入内存,然后跳转到系统装载程序运行

要完成这些工作,就涉及到在分区中查找文件和读取文件,实际上就是分区启动程序需要包含文件系统驱动程序。这个驱动程序不需要很多功能,只需要具备查找文件和读取文件的功能就行了。

这里面有个小细节需要注意,一般来说,MBR程序只是将分区的启动扇区读入内存,就一个扇区。所以,最理想的状态就是在这一个扇区里完成在分区里查找文件和将装载文件读入内存。以FAT32文件系统为例,分区的启动扇区被BIOS参数块占用了开头的90个字节,签名又占用了最后4个字节,实际的可用空间就只有408个字节。要在这408字节中完成文件的查找和读入,是很有挑战性的事情。

不同的分区格式,需要不同的分区启动程序。原因是文件系统驱动不一样,如果想做一个通用的分区启动程序,就要在里面包含各种文件系统的驱动,这样的话,分区启动程序就会变得很大,一个扇区绝对装不下,这又引出了另一个问题:如果分区启动程序太大,要保存在哪里?其实因为空间有限,把系统装载程序读入内存之后,也干不了太多其他的事情。当然,如果保留空间足够大,也能干很多事情。然而,事实上不同的分区格式化程序设置的保留扇区是不一样的,如果按照某一个分区格式化软件的参数来设计分区启动程序,能正常运行,但是在其他的分区格式化软件格式化出来的分区,就有可能出问题。

各个操作系统都会有自己的分区启动程序,一般来说与其他操作的启动程序不兼容,而且也应该会故意做成不兼容。当然,如果直接使用现有的通用引导程序,比如bootloader,也就没这个问题了。

3. 分区启动程序设计

根据我认为的分区启动程序的作用,我的分区启动程序设计成两个部分的,是分开的两个部分。一个部分放在分区启动扇区,一个部分放在分区的保留扇区。

具体的要完成的工作是:1、第一部分将保存在保留分区里的第二部分程序读到9000:0100,然后跳转这个地址执行第二部分的分区启动程序。2、第二部分的分区启动程序会在分区的根目录下找到16位的系统装载文件,我现在定的文件名是RBXLDR。然后把这个文件全文读到内存0800:0100的地址处,然后跳转到0800:0100执行。

3.1 第一部分:分区启动扇区的部分

这一部分的工作是要定位保留扇区的位置,然后读入内存。定位方法是从BIOS参数块中找到保留扇区数,加上MBR程序保存在03F8的分区起始扇区。定位后,就用INT 013H把内容读到9000:0100处。

3.2 第二部分:分区保留扇区的部分

这部分要查找文件和读取文件。查找文件就要遍历目录、解析目录结构、将文件名转换成目录格式。读取文件就要解析文件簇链、从簇号解析为磁盘扇区号。这些就要学习FAT32文件系统格式了。

FAT32文件系统放到书里的文件系统部分在写了。

4. 源代码

这个程序分成了两个部分,源代码也就跟着分成两部分。

4.1 分区启动扇区的源代码

;
;==============================================================================
;                                   ROBIX
;         Robin's Operating System. Order Source Studio @ 2011 - 2019
;                             ALL RIGHTS RESERVED
;                     ***------------------------------***
;Name   : FAT32BTR.ASM   FAT32 Booter
;Create : 2019-09-21. robin
;Purpose:
;Remark :
; active partition start sector store at: 0000:03F8
;==============================================================================
;
.386p
.model tiny

TEXT   SEGMENT  USE16 PUBLIC 'CODE'
 assume cs:TEXT,ds:TEXT
    org 00600h
start:
    jmp     fat32_boot ; skip the FAT32 BPB
    
    org 065Ah
fat32_boot:
    cli
    xor     cx,cx
    mov     ds,cx
    mov     es,cx
    mov     ss,cx
    mov     sp,03FFCh

; check if the number of reserved sectors is greater than 18
    mov     si,0600h   
    mov     cx,[si + 14]  ; get the number of reserved sectors
    cmp     cx,18
    jl      failed
    sub     cx,16
 
    add     ax, cx
    adc     dx, 0

; read the real booter into 9000:0100
    mov     cx,08010h
    mov     bx,09000h
    mov     es,bx;
    mov     bx,0100h
    call    read_hd 
    test    ah, ah
    jnz     failed

    ; jmp 9000:0100
    DB      0EAh
    DW      0100h
    DW      09000h

read_hd:
    ; ax,dx,si,di = sect, 
    ; ch = hd
    ; cl = sect
    ; es:bx = buf
    push    bp
    sub     sp,32;
    mov     bp,sp

    mov     [bp +  8],ax
    mov     [bp + 10],dx
    mov     [bp + 12],si
    mov     [bp + 14],di
    mov     byte ptr [bp],16
    mov     byte ptr [bp + 1],0
    mov     byte ptr [bp + 2],cl
    mov     byte ptr [bp + 3],0
    mov     ax,es
    mov     [bp + 4],bx
    mov     [bp + 6],ax
    mov     ax,04200h
    mov     dl,ch;
    mov     si,bp
    int     013h

    mov     sp,bp
    add     sp,32
    pop     bp
    ret
failed:
    sti
    push    cs
    pop     es

    mov     ah,03h
    xor     bh,bh
    int     010H
    lea     bp,msg
    mov     bx,0Fh
    mov     cx,51
    mov     ax,01301h
    int     010h
    xor     ax,ax
    int     016h
    cmp     al,'r'
    je      reboot
    cmp     al,'R'
    je      reboot
    jmp     failed
reboot:
    ; jmp FFFF:0
    DB      0EAh
    DW      0
    DW      0FFFFh
align qword
    msg     DB "PARTITION OR DISK IO ERROR, PRESS 'R' TO REBOOT",
                0Dh,0Ah,0Dh,0Ah
TEXT ENDS
    end start

4.2 保留扇区的源代码

这部分包含一个汇编程序和一个C程序,汇编程序仅仅是提供了一个链接的入口,主要功能都在C程序中,而且这部分要编译成COM格式的可执行文件。C程序编译成COM格式的可执行文件在后面讲。

先贴汇编文件

;
;==============================================================================
;                                   ROBIX
;         Robin's Operating System. Order Source Studio @ 2011 - 2019
;                             ALL RIGHTS RESERVED
;                     ***------------------------------***
;Name   : STARTUP.ASM   FAT32 Booter startup
;Create : 2019-09-21. robin
;Purpose: this asm program only provide the DOS executable file entry, and 
;       initialize the segment register and stack pointer, and copy itself to 
;       9000:0000,
;Remark : 
;==============================================================================
;
.386p
.model tiny

EXTRN   _main:PROC

TEXT   SEGMENT  USE16 PUBLIC 'CODE'
 assume cs:TEXT,ds:TEXT
    org 0100h
start:
    cli
    mov     ax,cs
    mov     ds,ax
    mov     es,ax
    mov     ss,ax
    mov     sp,07FFCh

; here, Check if the booter is in 9000:0000. if not, copy to 9000:0000, and
; jump to that 9000:0100 to run.
    mov     dx,09000h
    cmp     ax,dx
    je      boot_start
    mov     es,dx
    mov     cx,08000h
    xor     di,di
    xor     si,si
    rep     movsb
    db      0EAh
    dw      0100h
    dw      09000h

boot_start:
    call _main
TEXT ENDS
    end start

C程序太多,就不全贴了,贴扫描目录和加载文件的部分。另一方面,这个C程序需要DOS和windows下调试,为了源代码能兼容BC3.1和VS2019,用了一些条件编译,所以就写成这样了。

bool_t SearchDirectory(dword_t clus,const char * filename,fatdir_t * dir)
{
    uint32_t        clussect;
    uint_t          iSect;
    int             dirn;
    fatdir_t      * tmpdir;

    for( ; clus < 0x0FFFFFF7 ; clus = NextClus(clus) ){
        clussect = ClusToSector(clus);
        for( iSect = 0 ; iSect < nSectPerClus ; iSect++,clussect++){
            ReadHD(pBootHDD, clussect, TO_PTR32(buf), 512);
            tmpdir = (fatdir_t *)buf;
            for( dirn = 0 ; dirn < 16 ; dirn++, tmpdir++){
                if( tmpdir->fd_Name[0] == 0xE5 )
                    continue;
                if( tmpdir->fd_Name[0] == 0 )
                    return FALSE;
                if( (tmpdir->fd_Attr & 0x3F) == DIR_ATTR_LONG_NAME)
                    continue;
                if( tmpdir->fd_Attr & DIR_ATTR_DIRECTORY )
                    continue;
                if( _memcmp(filename,tmpdir->fd_Name,11) == 0 ){
                    DBG_FOUND_DIR(tmpdir);
                    _memcpy(dir,tmpdir,sizeof(fatdir_t));
                    return TRUE;
                }
            }
        }
    }
    return FALSE;
}

bool_t LoadFileToMem(uint32_t nFileFirstClus, uint32_t nFileSize,
    ptr32_t pPhyMemAddr)
{
    uint32_t        nClus,nFirstClusSect;
    uint_t          iSect;
#if _OS_ == __DOS__
#ifdef __DEBUG__
    int             nSect = 0;
#endif
#elif _OS_ == __WINDOWS__
    int             nSect = 0;
#endif
    nClus = nFileFirstClus;
    for( ; nClus < 0x0FFFFFF7 && nFileSize; nClus = NextClus(nClus) ){
        nFirstClusSect = ClusToSector(nClus);
#ifdef __DEBUG__
        printf("read cluster %ld to memory %08lX\n", nClus, pPhyMemAddr);
#endif
        for( iSect = 0 ; iSect < nSectPerClus && nFileSize; iSect++ ){
#if _OS_ == __DOS__
#ifdef __DEBUG__
            printf("read sector:%ld to memory:%08lX, total sectors: %d\n",
                nFirstClusSect + iSect, pPhyMemAddr, ++nSect);
#endif
#elif _OS_ == __WINDOWS__
            printf("read sector:%d to memory:%08X, total sectors: %d\n",
                nFirstClusSect + iSect, pPhyMemAddr, ++nSect);
#endif
            if( ReadHD(pBootHDD, nFirstClusSect + iSect, pPhyMemAddr, 
                nBytesPerSect) != TRUE )
                return FALSE;
            (uint32_t)pPhyMemAddr += nBytesPerSect;
            if( nFileSize < nBytesPerSect )
                nFileSize = 0;
            else
                nFileSize -= nBytesPerSect;
        }
    }
    return TRUE;
}

int main(void)
{
    fat32bpb_t    * pBPB;
    fatdir_t        dir;
#if _OS_ == __DOS__
    Cls()
    SetCursor(0,0);
#endif
    prints("ROBIX FAT32 PARTITION BOOTER RUNNING\r\n");
    pActPartStartSect    = (uint32_t FAR *)0x000003F8L;
    pActPartStartSect[0] = 0l;
    pActPartStartSect[1] = 0l;
    if(BuildHDDTab() != TRUE){
        BootFailed(strDiskErr);
        return 0;
    }
    /* build the FAT32 parameter */
    pBPB = (fat32bpb_t  *)bufFAT32BPB;
    nActPartStartSect = *pActPartStartSect;
    nBytesPerSect = pBPB->bpb_BytesPerSect;
    nSectPerClus  = pBPB->bpb_SectPerClus;
    nRootClus     = pBPB->bpb_RoorClus;
    nRsvdSectCnt  = pBPB->bpb_RsvdSectCnt;
    nFirstDataSect = FirstDataSect(pBPB);
#ifdef __DEBUG__
#if _OS_ == __DOS__
    printf("active partition start sector: %ld\n", nActPartStartSect);
    printf("bytes per cluster: %d\n", nBytesPerSect);
    printf("reserved sector count: %ld\n", nRsvdSectCnt);
    printf("partition first data sector: %ld\n", nFirstDataSect);
#elif _OS_ == __WINDOWS__
    printf("active partition start sector: %d\n", nActPartStartSect);
    printf("bytes per cluster: %d\n", nBytesPerSect);
    printf("reserved sector count: %d\n", nRsvdSectCnt);
    printf("partition first data sector: %d\n", nFirstDataSect);
#else
#endif
#endif
    /*  search ROBIX LOADER file in root directory */
    if(SearchDirectory(nRootClus, LOADER_FILE, &dir) != TRUE){
        BootFailed(strLdrFileNotExist);
        return 0;
    }
    prints("ROBIX LOADER FOUND!\r\n");
    /*  load the ROBIX LOADER into memory, 
      default at 0800:0100. 32-BIT physical address is 0x00008100
    */
    LoadFileToMem(FAT32_GET_CLUSTER(&dir), dir.fd_FileSize, LOADER_BASE);
    IVT[243] = 0x08000100L;
    prints("ROBIX LOADER BEGIN\r\n");
#if _OS_ == __DOS__
    /* run the ROBIX LOADER via IVT */
    __asm int 243
#elif _OS_ == __WINDOWS__
    CloseHandle(pHDDTab->hdd_IOAddr[0]);
#endif
    return 0;
}

5. C语言源代码编译成COM格式的可执行文件

因为启动程序需要直接可执行程序,但一般编译链接得到的exe文件是包含格式信息的,还要经过整理才能成为可执行程序。所以只能以曲线的方式得到直接可执行的程序。

这么做是因为我只知道这种方法,并不是因为这种方法很好。至于用gcc的方法,其实吧,是我懒得学gcc了。

方法的大致是这样的:
第一步:自己准备一个汇编文件,完成寄存器初始化,包括栈。然后在里面调用main,main函数在C程序里提供。
第二步:编译C程序时,把代码段(_text)、已初始化数据段(_data)、未初始化数据段(_bss)的段名和类型都指定成一样的,并且要tiny模式编译,
第三步:链接程序的时候,指定链接成COM格式

这里把分区启动程序的第二部分的编译命令贴出来,这是在DOS下批处理

@ECHO off
ECHO ==========================================================
ECHO                  Order Source Studio
ECHO        robin  @ 2019-10-16
ECHO        copyright by robin, all rights reserved
@ECHO.
ECHO    this batch processing file use to compile rbxldr
ECHO ==========================================================
@ECHO.
IF EXIST rbxldr del rbxldr
IF EXIST rbxldr.exe del rbxldr.exe
IF EXIST rbxldr.com del rbxldr.com

tasm /dLOADER_ENTRY=019000h startup.asm, startup.obj
IF NOT EXIST startup.obj GOTO STARTUP_FAILED

bcc -c -mt -zCTEXT -zDTEXT -zRTEXT -zTCODE -zBCODE RBXLDR.c
IF NOT EXIST rbxldr.obj GOTO RBXLDR_C_FAILED

tlink /t startup.obj rbxldr.obj, RBXLDR.COM, RBXLDR.MAP,
IF NOT EXIST RBXLDR.COM GOTO RBXLDR_FAIELD
rename RBXLDR.COM RBXLDR
del rbxldr.obj
del startup.obj

@ECHO.
ECHO *** compile and link rbxldr.com [SUCCESS]. ***
GOTO END

:STARTUP_FAILED
ECHO compile startuo.asm failed
GOTO END

:RBXLDR_C_FAILED
ECHO compile rbxlbr.c failed
GOTO END

:RBXLDR_FAILED
ECHO link rbxldr.com failed
GOTO END

:END
@Echo on

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值