Linux 之 start_kernel() 下的 setup_arch()

2. Linux 之 start_kernel()

2.1 setup_arch()

2.1.1 show_memory_map()

void show_memory_map()
{
    uint32_t mmap_addr = ((multiboot_t*)glb_mboot_ptr)->mmap_addr;
    uint32_t mmap_length = ((multiboot_t*)glb_mboot_ptr)->mmap_length;

    printk("Memory map:\n");

    mmap_entry_t *mmap = (mmap_entry_t *)mmap_addr;
    for (mmap = (mmap_entry_t *)mmap_addr; (uint32_t)mmap < mmap_addr + mmap_length; mmap++) {
        printk("base_addr = 0x%X%08X, length = 0x%X%08X, type = 0x%X\n",
            (uint32_t)mmap->base_addr_high, (uint32_t)mmap->base_addr_low,
            (uint32_t)mmap->length_high, (uint32_t)mmap->length_low,
            (uint32_t)mmap->type);
    }
	// setup_memory_region();
}

运行结果:

在这里插入图片描述

2.1.2 setup_memory_region()

static void setup_memory_region(void)
{
    char *who = "BIOS-e820";

    init_biosmap();
    printk("biosmap.map = %p, biosmap.nr_map = %d\n", biosmap.map, biosmap.nr_map);
    sanitize_e820_map(biosmap.map, &biosmap.nr_map);
}
2.1.2.1 init_biosmap();

从 multiboot_t 中获取 addr 、size、type。放到 e820map 结构体中。

//multiboot.h
struct multiboot_t {
    uint32_t flags;			// Multiboot 的版本信息
    /**
     * 从 BIOS 获知的可用内存
     *
     * mem_lower和mem_upper分别指出了低端和高端内存的大小,单位是KB。
     * 低端内存的首地址是0,高端内存的首地址是1M。
     * 低端内存的最大可能值是640K。
     * 高端内存的最大可能值是最大值减去1M。但并不保证是这个值。
     */
    uint32_t mem_lower;
    uint32_t mem_upper;

    uint32_t boot_device;		// 指出引导程序从哪个 BIOS 磁盘设备载入的OS映像
    uint32_t cmdline;		// 内核命令行
    uint32_t mods_count;		// boot 模块列表
    uint32_t mods_addr;

    /**
    * "flags"字段的 bit-5 设置了
     * ELF 格式内核映像的section头表。
     * 包括ELF 内核的 section header table 在哪里、每项的大小、一共有几项以及作为名字索引的字符串表。
     */
    uint32_t num;
    uint32_t size;
    uint32_t addr;
    uint32_t shndx;

    /**
     * 以下两项指出保存由BIOS提供的内存分布的缓冲区的地址和长度
     * mmap_addr是缓冲区的地址,mmap_length是缓冲区的总大小
     * 缓冲区由一个或者多个下面的大小/结构对 mmap_entry_t 组成
     */
    uint32_t mmap_length;
    uint32_t mmap_addr;

    uint32_t drives_length; 	// 指出第一个驱动器结构的物理地址
    uint32_t drives_addr; 		// 指出第一个驱动器这个结构的大小
    uint32_t config_table; 		// ROM 配置表
    uint32_t boot_loader_name; 	// boot loader 的名字
    uint32_t apm_table; 	    	// APM 表
    uint32_t vbe_control_info;
    uint32_t vbe_mode_info;
    uint32_t vbe_mode;
    uint32_t vbe_interface_seg;
    uint32_t vbe_interface_off;
    uint32_t vbe_interface_len;
} __attribute__((packed)) multiboot_t;


struct e820map {
    int nr_map; //段数量
    struct e820entry {
        unsigned long long addr;	// start of memory segment
        unsigned long long size;	// size of memory segment 
        unsigned long type;		    // type of memory segment
    } map[E820MAX];
};
2.1.2.2 sanitize_e820_map(biosmap.map, &biosmap.nr_map);

其他几个结构体:


	//e820.h
    struct e820entry {
        __u64 addr;	// start of memory segment 
        __u64 size;	// size of memory segment 
        __u32 type;	// type of memory segment 
    } __attribute__((packed));
   
	//sanitize_e820_map 函数内结构体
	struct change_member
	{
		struct e820entry *pbios; 
		unsigned long long addr; 
	};

此函数:

1. 首先:把创建的 change_point_list[i] 地址 交给 change_point[i]。

struct change_member change_point_list[2 * E820MAX];
struct change_member *change_point[2 * E820MAX];
for(i = 0; i < 2 * old_nr; i++) {
	change_point[i] = &change_point_list[i];
}

change_point [ 0 ]->add 放第一个 段的起始地址
change_point [ 1 ]->add 放第一个 段的末尾地址-
change_point [ 0 ]->pbios / change_point [ 1 ]->pbios 存放的是同一个biosmap [ i ] 地址

chgidx = 0;
    for(i = 0; i < old_nr; i++){
        if(biosmap[i].size != 0){
            // 记录起始地址

            change_point[chgidx]->addr = biosmap[i].addr;
            change_point[chgidx++]->pbios = &biosmap[i];
            printk("%d # 0x%08X ",i,biosmap[i].addr);
            printk("+ 0x%08X    ",biosmap[i].size);
            // 记录结束地址
            
            change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
            change_point[chgidx++]->pbios = &biosmap[i];
            printk("= 0x%08X    ",biosmap[i].addr + biosmap[i].size);
            printk("address: 0x%08X\n",&biosmap[i]);
        }
    }
    chg_nr = chgidx;

显示结果:和 memory_map 中的一致。
在这里插入图片描述

我这里是6个段,因为分别将起点 add 和终点 end 放到两个 change_point 中:
所以代码也是这样的:

   // 记录起始地址
  change_point[chgidx]->addr = biosmap[i].addr;
  change_point[chgidx++]->pbios = &biosmap[i];
  // 记录结束地址
  change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
  change_point[chgidx++]->pbios = &biosmap[i];

所以 chgidx值 是 段数量 old_nr 的 2 倍。

2. 接下来,将按内存地址排序 change-point list(低 -> 高)排序
排序的规则:
/* if <current_addr> > <last_addr>, swap /
/
如果 当前地址小于上一个地址 /
/
or, if current=<start_addr> & last=<end_addr>, swap /
/
或是 当前的地址(起始地址) == 上一个地址(结束地址) */

	/* if <current_addr> > <last_addr>, swap */
	/* 如果 当前地址小于上一个地址 */
	/* or, if current=<start_addr> & last=<end_addr>, swap */
	/* 或是 当前的地址(起始地址) == 上一个地址(结束地址) */
if(	(change_point[i]->addr < change_point[i-1]->addr) || \
	 ((change_point[i]->addr == change_point[i-1]->addr) &&
	 (change_point[i]->addr == change_point[i]->pbios->addr) \
	 && (change_point[i-1]->addr != change_point[i-1]->pbios->addr)))
{
		change_tmp = change_point[i];
		change_point[i] = change_point[i-1];
		change_point[i-1] = change_tmp;
		still_changing=1;
}

第一个还能理解。
第二个 为什么( i -1 的 end ) == ( i 的 addr ) 时候要交换?

这里的 change_point[i-1]->addr != change_point[i-1]->pbios->addr 实际就是 end。end (->addr) 因为是 add+size,所以自然和 自己的起始地址 addr (->pbios->addr) 不相等。
在这里插入图片描述

我们看一下排序后的结果。
图中用 ^ 表示 addr v 表示 end。
在这里插入图片描述
这里可以明显看到

0x00000000 ^
0x0008FC00 ^
0x0008FC00 v
0x000A0000 v

很明显,这里的第二行是和第四行同属于一对,第一行和第三行是一对。由于之前的排队 第二行和第三行进行了交换。
这样一看,第一行就和第四行变成一对了。
第一个地址和第四个地址把他俩其中的地址变成了一块 (括号栈平衡) 。

这样划分得到了 3 块范围。
在这里插入图片描述
3. 排好序后,就是去重。
最主要就是这个 for 结构,以及打头的 if — is addr — else — is end — 操作。
is addr 就加入:
is end 的操作是删除,我们看看 OS 是如何操作的。

/* create a new bios memory map, removing overlaps */
	/* 创建一个新的 bios 内存映射,去除重叠 */
	overlap_entries = 0;   // 重叠表中的条目数(number of entries in the overlap table)
	new_bios_entry = 0;    // 创建新的 BIOS 映射条目的索引(index for creating new bios map entries)
	last_type = 0;         // start with undefined memory type = 从未定义的内存类型起始处
	last_addr = 0;         // start with 0 as last starting address = 以 0 开头作为最后一个起始地址
	/* loop through change-points, determining affect on the new bios map */
	/* 遍历更改点,确定对新的 bios map 的影响 */
    
    for(chgidx = 0; chgidx < chg_nr; chgidx++){
		/* keep track of all overlapping bios entries */
		/* 跟踪所有重叠的 bios 条目 */
        if(change_point[chgidx]->addr == change_point[chgidx]->pbios->addr){
            // 是一个 addr
            overlap_list[overlap_entries++] = change_point[chgidx]->pbios;
        }else{ //是一个 end
			/* remove entry from list (order independent, so swap with last) */
			/* 从列表中删除条目(顺序无关,因此与最后一个交换)*/
            for(i = 0; i < overlap_entries; i++){
                if(overlap_list[i] == change_point[chgidx]->pbios){
                    overlap_list[i] = overlap_list[overlap_entries - 1];
                }
                overlap_entries--;
            }
        }
        //overlap_entries of number
        printk("\n=== overlap_entries: 0x%08X ",overlap_entries);
        for(i = 0; i < overlap_entries; i++){
            printk("[%02d] : 0x%08X ",i,overlap_list[i]->addr);
        }

    }

在这里插入图片描述
结合代码:

	  //查找范围: 目前加入 overlap_list 中的
	  //注意:加入的都是 addr,只有 end 的时候才进行查找。
      for(i = 0; i < overlap_entries; i++){
      	  //这比较的是什么:比较 change_point[chgidx]->pbios (当前)的 end 是不是 和 overlap_list[i](addr) 是原一对。
          if(overlap_list[i] == change_point[chgidx]->pbios){
          	  //满足条件后,把当前最后一个(overlap_entries -1 )的 addr 替换代替给当前(i)的 addr
              overlap_list[i] = overlap_list[overlap_entries - 1];
          }
          overlap_entries--;
      }

接下来:
整个 for 就是一块, 【overlap_entries == 0】 的时候就是 【current_type == 0】 的 时候。不进入 for 循环,current_type 就会是默认的 0 。

        current_type = 0;
        //整个 for 中是一块, overlap_entries == 0 的时候就是 current_type == 0 的 时候。
        for(i = 0; i < overlap_entries; i++){
            if(overlap_list[i]->type > current_type){
                // 取大的
                current_type = overlap_list[i]->type;
            }
        }

而 current_type == 0 的时候,就是一个新的整块的开始。这里的关系比较动态,我明天会用视频来讲解,并附上链接。

【Linux2.4 初始化模块2 之 sanitize_e820_map 函数】

 if(current_type != last_type){
       if(last_type != 0){
          new_bios[new_bios_entry].size = change_point[chgidx]->addr - last_addr;
          /* move forward only if the new size was non-zero */
			/* 仅当新大小不为零时才向前移动 */
          if(new_bios[new_bios_entry].size != 0){
              /* 没有更多空间用于新的 bios 条目, no more space left for new bios entries */
              if(++new_bios_entry >= E820MAX){break;}
          }
       }
     

      if(current_type != 0){
         //chgidx 是当前匹配到的 坐标
         printk("chgidx = %02d ",chgidx);
         printk("change_point[]->addr = 0x%08X\n",change_point[chgidx]->addr);
         printk("current_type = %02d\n",current_type);
         
         new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
         new_bios[new_bios_entry].type = current_type;
         last_addr = change_point[chgidx]->addr;
      }

      last_type = current_type;
 }

最后的排序结果:

在这里插入图片描述

这里我明天会用一个视频作为动态讲解,把链接发在这里。

【Linux2.4 初始化模块2 之 sanitize_e820_map 函数】
后面待续。。。。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值