HylicOS已经完成了部分硬件抽象层的工作,包括MMU的初始化并对虚拟内存到物理内存做了映射,创建了页表目录。对串口进行了初始化,实现了printk格式化打印函数,方便了日志输出和程序调试。建立了异常向量表。
现在要做的是内存管理部分。
对内存资源的管理最终要实现对内存完全地掌控:知道哪些地方有可用合法内存。能够快速找到一块想要的大小的内存,这块内存想大就大,想小就小。
为了能够掌握硬件可用的所有物理内存资源,需要对所有的物理内存资源进行描述:
考虑到移植性,每个硬件平台上的物理内存的分布都是不同的,对不同硬件平台的物理内存进行描述也必将不同,所以不能写死。
mini2440有一块64M的SDRAM,有一些地址空间映射的是设备寄存器,有些是空洞,依据这些属性对每一类内存抽象出一个结构体
typedef enum
{
ADRSPACE_NOT = 0,
ADRSPACE_IO = 1,
ADRSPACE_SDRAM = 2,
ADRSPACE_RAM = 3,
ADRSPACE_ROM = 4,
ADRSPACE_NORFLASH = 5,
ADRSPACE_NANDFLASH = 6,
}phymem_type_e;
typedef struct
{
phymem_type_e phymem_type;
u32_t dev_type;
adr_t adr_start;
adr_t adr_end;
}phymemspce_t;
其中:
phymem_type成员指示这块物理内存的类型,dev_type是指示设备的类型
adr_start成员描述这块物理内存的起始地址,adr_end成员描述这块物理内存的终止地址。
有了这个结构体作为模版,就可以将各块物理内存创建为一个个对象,为了更好地管理这些物理内存对象,可以将他们放到一个数组中:
HAL_DEFGLOB_VARIABLE(phymemspce_t,phymemspce)[PLFM_ADRSPCE_NR] = {
{ADRSPACE_NORFLASH,0,0,0x001fffff},
{ADRSPACE_IO,0,0x08000000,0x0800000f},
{ADRSPACE_IO,0,0x10000000,0x1000000f},
{ADRSPACE_IO,0,0x19000000,0x190fffff},
{ADRSPACE_IO,0,0x20000000,0x2000000f},
{ADRSPACE_IO,0,0x28000000,0x28000007},
{ADRSPACE_IO,0,0x29000000,0x29000007},
{ADRSPACE_SDRAM,0,0x30000000,0x33ffffff},
{ADRSPACE_IO,0,0x48000000,0x48000030},
{ADRSPACE_IO,0,0x49000000,0x49000058},
{ADRSPACE_IO,0,0x4a000000,0x4a00001c},
{ADRSPACE_IO,0,0x4b000000,0x4b0000e0},
{ADRSPACE_IO,0,0x4c000000,0x4c000018},
{ADRSPACE_IO,0,0x4d000000,0x4d000060},
{ADRSPACE_IO,0,0x4e000000,0x4e00003c},
{ADRSPACE_IO,0,0x4f000000,0x4f0000a0},
{ADRSPACE_IO,0,0x50000000,0x50008028},
{ADRSPACE_IO,0,0x51000000,0x51000040},
{ADRSPACE_IO,0,0x52000000,0x5200026f},
{ADRSPACE_IO,0,0x53000000,0x53000008},
{ADRSPACE_IO,0,0x54000000,0x54000010},
{ADRSPACE_IO,0,0x55000000,0x55000012},
{ADRSPACE_IO,0,0x56000000,0x560000cc},
{ADRSPACE_IO,0,0x57000040,0x5700008b},
{ADRSPACE_IO,0,0x58000000,0x58000014},
{ADRSPACE_IO,0,0x59000000,0x59000034},
{ADRSPACE_IO,0,0x5a000000,0x5a000043},
{ADRSPACE_IO,0,0x5b000000,0x5b00001c},
{ADRSPACE_NOT,DEV_TYPE,0,0}};
这个数组的定义和初始化应该放在一个全局位置(hal_global.c中),后面很多模块都要使用到它。
对于这个HAL_DEFGLOB_VARIABLE宏,是仿UNIX美学所在。用于定义只在一个本文件定义,其他文件则为外部声明的方式
其定义在hal_global.c对应的头文件中hal_global_t.h为:
#define HAL_DEFGLOB_VARIABLE(vartype,varname) EXTERN (__attribute__((".head.text"))) vartype varname
这里EXTERN宏被定义为extern
除此之外还要再定义一个规则在hal_global.h里:
#ifndef HALGLOBAL_HEAD
#undef EXTERN
#define EXTERN
#endif
当一个模块include了hal_global_t.h和hal_global.h时,使用HAL_DEFGLOB_VARIABLE定义变量,此时有EXTERN的定义,就是外部声明。
当一个模块include了hal_global_t.h和hal_global.h前额外的#define HALGLOBAL_HEAD,此时没有EXTERN宏的定义,就是定义。
这种做法可以在一处定义变量,其他使用它的地方只做外部声明。
现在物理地址被分类且描述出来了,操作系统需要服务程序,在程序需要内存资源时给出一块内存的起始地址,所以在操作系统的设计阶段就应该将辽阔物理内存切割为一块块单元区域,在这里我们的系统每一个区域都是4MB,用一个结构体描述,保存该单元区域的起始地址和结束地址,还有这个区域内部更加细分的分区情况,更包括了细分后的分区的占用情况:
typedef struct mmapdsc
{
list_h_t map_list; //list to connection every memory map describtor
adr_t map_phyadr; //memory physical start address
adr_t map_phyadrend; //memory physical end address
u32_t map_flg; //used to determine next domain(memory map attribution status)
u32_t map_attrstatus; //map_flg=6 map_attrstatus[0] 4MB=>4MB 0=>available | 1=>busy
//map_flg=5 map_attrstatus[1:0] 4MB=>2MB+2MB
//map_flg=4 map_attrstatus[3:0] 4MB=>1MB+1MB+1MB+1MB
spinlock_t map_lock;
}mmapdsc_t;
从每个成员的注释中已经可以一窥究竟,以表格形式列出更加清晰:
member | function |
---|---|
map_list | 区域单元彼此需要链接起来,该成员作为链表节点 |
map_phyadr | 区域单元的起始物理地址 |
map_phyadrend | 区域单元的结束物理地址 |
map_flg | 决定了该块区域单元以怎样精度细分 |
map_attrstatus | 细分后区域的位图描述 |
map_lock | 保护该结构的自旋锁 |
现在这个结构体还只是一纸空谈,需要实际为每个区域单元分配实际物理地址,一一绑定到每个实际的4MB地址上去。需要写一个初始化函数。在此之前我们需要一个更加宏观的数据结构,将前面两种结构的信息都存储进去:
我们将这个结构整体叫做机器数据结构,由下面注释可见,核心就是
mmapdscadr, mmapdscnum以及phyadrdsc,phyadrdscnum四个成员。前俩是对每一个4MB物理内存单元的抽象和描述,后俩是对整个物理内存的描述。
typedef struct mach
{
spinlock_t mlock;
list_h_t mlist;
adr_t krlposramstart; //kernel position start at ram
adr_t krlposramend; //kernel position end at ram
mmapdsc_t* mmapdscadr; //memory map describe start addr at ram
uint_t mmapdscnum; //memory map describe number
uint_t phyadrdscnum; //physical address space describer number
phymemspce_t* phyadrdsc; //physical address space describer start address
// ilnedsc_t* ilnedsc;
uint_t ilnedscnum;
// intfltdsc_t* intfltdsc; //interrupt source describer address
//uint_t intfltnum; //interrupt source describer number
}mach_t;
首先需要定义一个mach_t类型的全局变量,同样是使用HAL_DEFGLOB_VARIABLE宏定义在hal_global.c中:
HAL_DEFGLOB_VARIABLE(mach_t,osmach);
对机器数据结构单独开一个C文件进行初始化与操作,machine.c被创建出来以承担这个责任:
对机器数据结构的初始化,首先是krlposramstart,这个成员用KRNL_INRAM_START赋值,指示内核在RAM中的起始位置
值为0x30000000,顺带,我们开一下HyliOS各个部分在内存的排布吧
橙色部分是中断向量表所在位置,也是内核起始位置,0X3000_0000
绿色部分是MMU的页表目录所在位置,0x3000_4000
红色部分是内核代码段起始位置,0x3000_8000。
__end_kernel这个宏在链接文件里定义,由链接器符号链接完之后自动计算,下面代码也引用了这个宏(记得带这个“&”)
在__end_kernel之后就是mmapdsc结构组成的数组所在了,mmapdsc的结束位置一下子不好确定。
void machine_init(void)
{
osmach_init(&osmach);
return;
}
void osmach_init(mach_t* initp)
{
hal_spinlock_init(&initp->mlock);
list_init(&initp->mlist);
initp->krlposramstart = KRNL_INRAM_START;
initp->krlposramend = (adr_t) (&__end_kernel);
// initp->mmapdsc bitmap address
initp->mmapdscadr = (mmapdsc_t* )(ALIGN((uint_t)(&__end_kernel),4096));
initp->mmapdscnum = 0;
initp->phyadrdsc = phymemspce;
return;
}
现在再来写初始化mmapdsc_t结构数组的代码
void init_mmapdsc(mach_t* mach_spedsc)
{
phymemspce_t* pmsp = mach_spedsc->phyadrdsc; //build in hal_global.c
uint_t space_num = mach_spedsc->phyadrdscnum;
uint_t mapdsccnt = 0;
//init all mmapdsc
for(uint_t i=0; i<space_num; i++)
{
//just sdram can be used to shared with app
if(pmsp[i].phymem_type == ADRSPACE_SDRAM)
{
mapdsccnt = init_core_mmapdsc(pmsp[i].adr_start,
pmsp[i].adr_end,
mach_spedsc->mmapdscadr,
mapdsccnt ) ;
}
}
mach_spedsc->mmapdscnum = mapdsccnt;
//added mmapdsc thus need to modify kernel end address in ram
mach_spedsc->krlposramend = (adr_t)( (uint_t)mach_spedsc->mmapdscadr + \
mapdsccnt*sizeof(mach_spedsc));
}
uint_t init_core_mmapdsc(adr_t base_adr,adr_t end_adr,mmapdsc_t* mmapdsc,uint_t mapdsc_curindex)
{
uint_t curindex = mapdsc_curindex;
adr_t tmpadr = end_adr;
for( ;base_adr < end_adr
;base_adr += EACH_MAP_SIZE, curindex++)
{
if((base_adr+EACH_MAP_SIZE) < end_adr)
{
tmpadr = ( base_adr+EACH_MAP_SIZE) - 1;
}
else
{
tmpadr = end_adr;
}
mmapdsc_init(mmapds[curindex],
base_adr,
tmpadr,
0,
MAP_FLAGS_VAL(0,MAPF_ACSZ_4MB,MAPF_SZ_4MB));
}
return curindex;
}
void mmapdsc_init(mmapdsc_t* mmp,adr_t phyadr,adr_t phyadre,u32_t attrstatus,u32_t flgs)
{
list_init(&mmp->map_list);
hal_spinlock_init(&mmp->mlock);
mmp->map_phyadr = phyadr;
mmp->map_phyadrend = phyadre;
mmp->map_attrstatus = attrstatus;
mmp->map_flg = flgs;
return;
}