16位装载程序 - 载入系统文件
为什么要在16位实模式载入系统文件
我这么做,其实是想取巧。比如,我想把系统装载u盘上,好像就没办法弄了,因为我要先搞出USB的驱动,才能读U盘,这就搞大发了,系统还没起来,就先搞了一堆的驱动。然而,扩展BIOS是支持读写U盘的,这就省了很多事情了,只需要实现一个简化的文件系统驱动就能读文件了,这个简化的文件系统驱动只需要具备按路径查找文件和读文件两个功能就行。因为很简化,所以我就把它叫做简化的文件系统驱动程序(Simplified File System Driver, SFSD)
简化的文件系统驱动(Simplified File System Driver, SFSD)
为什么只需要按路径查找文件和读文件两个功能
一般来说,出于文件管理的需要,都不会把所有文件放在根目录,而是会放在某个目录下,所以就需要提供按路径查找文件的功能。
找到文件后,自然要把文件读入内存,那当然需要读功能了。
至于其他的功能,都不需要了。
初始化文件系统SFSD_Initialize
先读取MBR,从其中找到活动分区,用分区号表示。然后将活动分区的启动扇区读入内存。初始化FAT32文件系统参数,其实也就是将分区的起始扇区和数据区起始扇区保存下来,方便后面用
void SFSD_Initialize(void)
{
static hdd_t HDD;
int i, nActCnt = 0;
pte_t * pPTE, * pAct = NULL;
//
LDR_Printf("prepare boot disk information\r\n");
//
pBootHDD = &HDD;
HDD.hdd_BIOSDrvNum = 0x80;
HDD.hdd_BytesPerSect = 512;
HDD.hdd_IOAddr[0] = NULL;
HDD.hdd_IOAddr[1] = NULL;
//
if( ReadHD(pBootHDD, 0,TO_PTR32(bufMBR), 512) != TRUE ){
BootFailed("FAILED TO READ MBR!\r\n\r\n");
}
LDR_Printf(" # Act TYPE START TOTAL\r\n");
LDR_Printf(" - --- ---- --------- ---------\r\n");
pPTE = (pte_t *)(bufMBR + 0x1BE);
for( i = 0; i < 4; i++, pPTE++ ){
if( 0x80 == pPTE->pte_Flag ){
if( NULL == pAct){
pAct = pPTE;
nActPart = i;
}
nActCnt++;
}
if(pPTE->pte_Type){
LDR_Printf(" %d %02X %02X %9lu %9lu \r\n", i, pPTE->pte_Flag,
pPTE->pte_Type, pPTE->pte_StartSector, pPTE->pte_TotalSector);
}
}
if( nActCnt != 1 ){
BootFailed("PARTITION TABLE ERROR!\r\n\r\n");
}
LDR_Printf("No.%d partition actived, ", nActPart);
LDR_Printf("start sector: %lu. total sectors: %lu\r\n",
pAct->pte_StartSector, pAct->pte_TotalSector);
if( ReadHD(pBootHDD, pAct->pte_StartSector, TO_PTR32(bufActPartBootSect),
512) != TRUE ){
BootFailed("FAILED TO READ ACTIVE PARTITION BOOT SECTOR!\r\n\r\n");
}
pActPartBPB = (fat32bpb_t *)bufActPartBootSect;
BPB_PART_START_SECT(pActPartBPB) = pAct->pte_StartSector;
BPB_FIRST_DATA_SECT(pActPartBPB) = FAT_FirstDataSect(pActPartBPB);
}
按路径查找文件 SFSD_Open
这个并不打算做成和标准库的一样参数,因为那还要引入一套框架,即使只是一个样子,也足够让人心烦的,不仅开发的时候麻烦,后面的维护也是啰嗦。
程序很简单,就是从指定的目录开始查找,找到目录就继续往下,直至路径消耗完。
在目录文件中查找文件的,我做了一个限制,就是只处理最原始8.3格式的目录名,为什么不处理长文件名呢?因为,最开始的启动文件,根本不必用到长文件名,而且处理短文件名要容易多了。然后在定文件名时刻意的设计8.3格式的文件名就可以了,系统启动就几十个文件,根本不用担心文件名重复的问题。
/* search directory in directory
#define SEARCH_TYPE_ALL 0
#define SEARCH_TYPE_FILE 1
#define SEARCH_TYPE_DIR 2
*/
bool_t SFSD_SearchDir(fat32bpb_t * pBPB, dword_t clus,const char * filename,
fatdir_t * dir, uint_t nSearchType)
{
uint32_t clussect;
uint_t iSect;
int dirn;
fatdir_t * tmpdir;
char strDirName[12];
//
if( FAT_ToDirName(filename, strDirName) == NULL )
return FALSE;
//LDR_Printf("FAT dir name: %s.\r\n", strDirName);
for( ; clus < 0x0FFFFFF7 ; clus = FAT32_NextClus(pBPB, clus) ){
clussect = FAT32_ClusToSector(pBPB, clus);
for( iSect = 0 ; iSect < pBPB->bpb_SectPerClus; 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;
/* skip the long name directory */
if( (tmpdir->fd_Attr & 0x3F) == DIR_ATTR_LONG_NAME)
continue;
if(SEARCH_TYPE_FILE == nSearchType){
if( tmpdir->fd_Attr & DIR_ATTR_DIRECTORY )
continue;
}else{
if(SEARCH_TYPE_DIR == nSearchType){
if( !(tmpdir->fd_Attr & DIR_ATTR_DIRECTORY) )
continue;
}
}
if( _memcmp(strDirName,tmpdir->fd_Name,11) == 0 ){
DBG_FOUND_DIR(tmpdir);
_memcpy(dir,tmpdir,sizeof(fatdir_t));
return TRUE;
}
}
}
}
return FALSE;
}
result_t SFSD_OpenFile(fat32bpb_t * pBPB, dword_t nStartDir,
const char * strFilePath, fatdir_t * pFileDir)
{
const char * strTmpPath = strFilePath;
char strSubName[12];
fatdir_t fdDir;
//
do{
if( !SFSD_SubDirPath(strTmpPath, strSubName, &strTmpPath) )
return RESULT_FAILED;
if( !SFSD_SearchDir(pBPB, nStartDir, strSubName, &fdDir, 0) ){
LDR_Printf("SFSD_OpenFile. directory '%s' not exist!\r\n",
strSubName);
return RESULT_FAILED;
}
/* if there is a remaining path, the name must be a directory */
if( strTmpPath && !(fdDir.fd_Attr & DIR_ATTR_DIRECTORY) ){
LDR_Printf(" %s is not a directory!\r\n", strSubName);
return RESULT_FAILED;
}
nStartDir = FAT32_GET_CLUSTER(&fdDir);
}while( strTmpPath );
_memcpy(pFileDir, &fdDir, sizeof(fatdir_t));
return RESULT_SUCCESS;
}
读文件SFSD_ReadFile
读文件就是安顺序把文件全部读入内存,都不用处理文件指针的问题,
bool_t SFSD_ReadFile(fat32bpb_t * pBPB, uint32_t nFileFirstClus,
uint32_t nFileSize, ptr32_t pPhyMemAddr)
{
uint32_t nClus,nFirstClusSect;
uint_t iSect;
#ifdef __DEBUG__
int nSect = 0;
#endif
//
nClus = nFileFirstClus;
for( ; nClus < 0x0FFFFFF7 && nFileSize; nClus = FAT32_NextClus(pBPB, nClus) ){
nFirstClusSect = FAT32_ClusToSector(pBPB, nClus);
#ifdef __DEBUG__
LDR_Printf("read cluster %ld to memory %08lX\r\n", nClus, pPhyMemAddr);
#endif
for( iSect = 0 ; iSect < pBPB->bpb_SectPerClus && nFileSize; iSect++ ){
#ifdef __DEBUG__
LDR_Printf("read sector:%ld to memory:%08lX, total sectors: %d\r\n",
nFirstClusSect + iSect, pPhyMemAddr, ++nSect);
#endif
if(((uint32_t)pPhyMemAddr + nFileSize) < 0x100000L){
if( ReadHD(pBootHDD, nFirstClusSect + iSect, pPhyMemAddr,
pBPB->bpb_BytesPerSect) != TRUE )
return FALSE;
}else{
/* if large than 1M, int 13h can not use, so use this */
if( ReadHD(pBootHDD, nFirstClusSect + iSect, TO_PTR32(buf),
pBPB->bpb_BytesPerSect) != TRUE )
return FALSE;
CopyTo4G(pPhyMemAddr, buf, pBPB->bpb_BytesPerSect);
}
(uint32_t)pPhyMemAddr += pBPB->bpb_BytesPerSect;
if( nFileSize < pBPB->bpb_BytesPerSect )
nFileSize = 0;
else
nFileSize -= pBPB->bpb_BytesPerSect;
}
}
return TRUE;
}
引导参数列表
我设计了一个引导参数列表(Loader Parameter List, LPL),把我需要的参数保存里面,这个参数表我把它安排在物理内存的最后16M的空间,如果内存大于3G,就在3G的最后16M的位置,也不在往上了。
列表头我保存在中断向量表的第254项中,是的,我就是当那个位置没有东西,如果有,呵呵,那就呵呵吧。
目前,我在引导参数列表中保存了内存布局、引导配置文件、核心文件和系统文件,注意啊,引导配置文件、核心文件和系统文件都是把全部的文件读入内存的,给后面的32位引导程序和正式系统使用
#define IVT ((dword_t FAR *)0L)
#define LDR_PARAM_LIST_HEAD (IVT[253])
typedef struct _loader_parameter_node_t
{
uint32_t lpn_Next;
uint32_t lpn_Size;
uint32_t lpn_Type; /* 1: address range descriptro, 2: file*/
}loaderparanode_t;