Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2151625
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-09-08 15:31:53

1. 重新规划内存
如下图所示:

注:上图蓝色的还没有实现
2. 
a. mbr.S:
    a. 从0x7c00启动,打印mbr三个字符
    b. 从硬盘LBA_2读1个sector的loader.bin到0x40000
    c. 从硬盘LBA_9读0x80+0x48=200个sector的kernel.elf到0x70000
    d. 跳到jmp 0x4000:0, loader.bin
b. loader.S:
    a. 从0x40000运行,打开A20,加载GDT,cr0第0位置1
    b. 跳到保护模式,并打印P
    c. 在0x0处设置setup_page_table
    d. 新gdt_ptr的base设为0xC0009000,将gdt_base复制到0x9000,现在固定复制64字节
    e. 打开分页机制,重新加载gdt
    f. 将0x70000处的kernel.elf,复制到0x50000
    g. 跳到0x50000处运行   
c. 关于setup_page_table
   a. 将PDE清0,即将0x0000-0x1000大小为4K的地址清0
   b. PDE0(0x0) PDE768(768*4=0xc00) 指向PTE(0x1000)
   c. 设置0x1000处的PTE,  [0-1M]=1*1024*1024/4096=256项=0x100PAGE
        pg0[0x1000,0x1400]-->物理地址[0x0--1M]  ;只映射1M空间
3. 因为PDE是从0开始的,其转化过程比较简洁
  1. cong@msi:/work/os/code/8memory$ cat mm/memory.c
  2. #include <memory.h>
  3. #include <printk.h>
  4. #include <string.h>
  5. #define MM_K_VADDR_START 0xC0100000 //1M
  6. #define MM_SIZE (32*1024*1024) //32M
  7. #define MM_START (1*1024*1024) //1M
  8. #define MM_FOR_K (15*1024*1024) //15M for kernel
  9. #define MM_FOR_U (16*1024*1024) //16M for user

  10. #define PDE_IDX(addr) ((addr & 0xffc00000) >> 22)
  11. #define PTE_IDX(addr) ((addr & 0x003ff000) >> 12)

  12. MM_POOL k_pool, u_pool, v_pool; //kernel, user, vaddr

  13. uint8_t mm_map_k[MM_FOR_K>>12] = {0,}; //15*1024*1024/4096
  14. uint8_t mm_map_k_vaddr[MM_FOR_K>>12] = {0,}; //15*1024*1024/4096
  15. uint8_t mm_map_u[MM_FOR_U>>12] = {0,}; //16*1024*1024/4096
  16. static void* addr_get(MM_POOL* pool, uint32_t cnt);
  17. static void map_one_page(uint32_t paddr, uint32_t vaddr);
  18. static void* addr_get(MM_POOL* pool, uint32_t cnt)
  19. {
  20.     uint32_t addr;
  21.     uint32_t i;
  22.     int32_t bit_idx = -1;
  23.     bit_idx = bitmap_scan(&pool->bm, cnt);
  24.     if(-1 == bit_idx)
  25.         return NULL;
  26.     for(i=0; i<cnt; i++)
  27.     {
  28.         bitmap_set(&pool->bm, bit_idx+i, 1);
  29.     }
  30.     addr = (bit_idx<<12) + pool->start;
  31.     return ((void*)addr);
  32. }

  33. /* 得到虚拟地址vaddr对应的pde的指针 */
  34. uint32_t* pde_ptr(uint32_t vaddr) {
  35.     /* 0xfffff是用来访问到页表本身所在的地址 */
  36.     uint32_t* pde = (uint32_t*)(PDE_IDX(vaddr) * 4);
  37.     return pde;
  38. }

  39. /* 得到虚拟地址vaddr对应的pte指针*/
  40. uint32_t* pte_ptr(uint32_t vaddr) {
  41.     /* 先访问到页表自己 +
  42.      * 再用页目录项pde(页目录内页表的索引)做为pte的索引访问到页表 +
  43.      * 再用pte的索引做为页内偏移*/
  44.     //uint32_t* pte = (uint32_t*)(((vaddr&0xffc00000)>>10) + PTE_IDX(vaddr)*4);
  45.     uint32_t* pte = (uint32_t*)(PAGE + PTE_IDX(vaddr)*4);
  46.     return pte;
  47. }

  48. /* 页表中添加虚拟地址_vaddr与物理地址_page_phyaddr的映射 */
  49. static void map_one_page(uint32_t paddr, uint32_t vaddr)
  50. {
  51.     uint32_t* pde = pde_ptr(vaddr);
  52.     uint32_t* pte = pte_ptr(vaddr);
  53.     /************************ 注意 *************************
  54.      * 执行*pte,会访问到空的pde。所以确保pde创建完成后才能执行*pte,
  55.      * 否则会引发page_fault。因此在*pde为0时,*pte只能出现在下面else语句块中的*pde后面。
  56.      * *********************************************************/
  57.     /* 先在页目录内判断目录项的P位,若为1,则表示该表已存在 */
  58.     if (*pde & 0x00000001) { // 页目录项和页表项的第0位为P,此处判断目录项是否存在
  59.         if (!(*pte & 0x00000001)) { // 只要是创建页表,pte就应该不存在,多判断一下放心
  60.             *pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
  61.         } else { //应该不会执行到这,因为上面的ASSERT会先执行。
  62.             printk("pte repeat");
  63.             *pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
  64.         }
  65.     } else {
  66.         printk("not exist\n");
  67.         // 页目录项不存在,所以要先创建页目录再创建页表项.
  68.         /* 页表中用到的页框一律从内核空间分配 */
  69.         uint32_t pde_phyaddr = (uint32_t)addr_get(&k_pool, 1);

  70.         *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);

  71.         /* 分配到的物理页地址pde_phyaddr对应的物理内存清0,
  72.          * 避免里面的陈旧数据变成了页表项,从而让页表混乱.
  73.          * 访问到pde对应的物理地址,用pte取高20位便可.
  74.          * 因为pte是基于该pde对应的物理地址内再寻址,
  75.          * 把低12位置0便是该pde对应的物理页的起始*/
  76.         memset((void*)((int)pte & 0xfffff000), 0, PAGE);

  77.         *pte = (paddr | PG_US_U | PG_RW_W | PG_P_1); // US=1,RW=1,P=1
  78.     }
  79. }

  80. void* malloc_pages(uint32_t flag, uint32_t cnt)
  81. {
  82.     uint32_t i;
  83.     void* vaddr, *paddr;
  84.     MM_POOL* mm_pool;
  85.     if(cnt > (uint32_t)bitmap_get_left_bits(&v_pool.bm))
  86.     {
  87.         printk("no enough vaddr mem left\n");
  88.         return NULL;
  89.     }
  90.     if(cnt > (uint32_t)bitmap_get_left_bits(&k_pool.bm))
  91.     {
  92.         printk("no enough phy mem left\n");
  93.         return NULL;
  94.     }
  95.     //a. get vaddr
  96.     vaddr = addr_get(&v_pool, cnt);
  97.     if(NULL == vaddr)
  98.     {
  99.         printk("vaddr get error\n");
  100.         return NULL;
  101.     }
  102.     mm_pool = (flag&PF_KERNEL)?(&k_pool):(&u_pool);
  103.     for(i=0; i<cnt; i++)
  104.     {
  105.         //b. get phy addr
  106.         paddr = addr_get(mm_pool, 1);
  107.         //c. map vaddr to phy addr
  108.         map_one_page((uint32_t)paddr, (uint32_t)vaddr);
  109.         vaddr += PAGE;
  110.     }
  111.     return vaddr;
  112. }

  113. //从内核物理内存池中申请pg_cnt页内存,成功则返回其虚拟地址,失败则返回NULL
  114. void* get_kernel_pages(uint32_t cnt)
  115. {
  116.     void* vaddr = malloc_pages(PF_KERNEL, cnt);
  117.     if(NULL == vaddr)
  118.     {
  119.         printk("malloc page eror\n");
  120.         return NULL;
  121.     }
  122.     return vaddr;
  123. }

  124. //内存管理部分初始化入口
  125. int mem_init(void)
  126. {
  127.     k_pool.bm.len = (MM_FOR_K>>12)>>3; //k_pages in byte
  128.     k_pool.bm.pbit = mm_map_k;
  129.     k_pool.start = MM_START;

  130.     u_pool.bm.len = (MM_FOR_U>>12)>>3; //user_pages in bits
  131.     u_pool.bm.pbit = mm_map_u;
  132.     u_pool.start = MM_START + MM_FOR_K;

  133.     v_pool.bm.len = (MM_FOR_K>>12)>>3; //k_pages in bits
  134.     v_pool.bm.pbit = mm_map_k_vaddr;
  135.     v_pool.start = MM_K_VADDR_START;

  136.     bitmap_init(&k_pool.bm);
  137.     bitmap_init(&u_pool.bm);
  138.     bitmap_init(&v_pool.bm);

  139.     printk("k_pages in bytes=%d\n", k_pool.bm.len);
  140.     printk("u_pages in bytes=%d\n", u_pool.bm.len);
  141.     return 0;
  142. }
说明:  《操作系统真相还原》中页目录表是放在0x100000(1M)处,还要存放256个页表
现在是把页目录表放在0x0处后面跟8个页表这样32M的内存都被映射了,
页表的分配是只分配1M地址之后的空间,这样就可以直接从1M-32M去分配内存了。
不需要去整那么多边界,这样代码也会简洁很多。
4. 运行
  1. <bochs:2> info tab
  2. cr3: 0x000000000000
  3. 0x00000000-0x00102fff -> 0x000000000000-0x000000102fff
  4. 0xc0000000-0xc0102fff -> 0x000000000000-0x000000102fff
  5. 0xffc00000-0xffc00fff -> 0x000000001000-0x000000001fff
  6. 0xfff00000-0xfff00fff -> 0x000000001000-0x000000001fff
  7. 0xfffff000-0xffffffff -> 0x000000000000-0x000000000fff
  8. <bochs:3> page 0xc0103000
  9.  PDE: 0x0000000000001027 ps A pcd pwt U W P
  10.  PTE: 0x0000000000000000 g pat d a pcd pwt S R p
  11. physical address not available for linear 0xc0103000
  12. <bochs:4> page 0xc0102000
  13.  PDE: 0x0000000000001027 ps A pcd pwt U W P
  14.  PTE: 0x0000000000102007 g pat d a pcd pwt U W P
  15. linear page 0xc0102000 maps to physical page 0x000000102000
  16. <bochs:5> page 0xc0101000
  17.  PDE: 0x0000000000001027 ps A pcd pwt U W P
  18.  PTE: 0x0000000000101007 g pat d a pcd pwt U W P
  19. linear page 0xc0101000 maps to physical page 0x000000101000
  20. <bochs:6> page 0xc0100000
  21.  PDE: 0x0000000000001027 ps A pcd pwt U W P
  22.  PTE: 0x0000000000100007 g pat d a pcd pwt U W P
  23. linear page 0xc0100000 maps to physical page 0x000000100000
5. 代码打包
8memory.rar(下载后改名为8memory.tar.gz)
以前的printk打印老有问题重新整了一个
阅读(796) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~