Linux Kernel 2.6.35 启动过程笔记 :set

    技术2022-05-20  59

    现在就让我们进去到现在为止我们面对的最复杂的代码set_video。望文生义,这个段代码一定做了些初始化显示设备的工作。那么让我们一起来看一下它到底是怎么干的。

    set_video的代码在arch/x86/boot/Video.c里面 void set_video(void){    u16 mode = boot_params.hdr.vid_mode; //显示模式由bootloader设定       //参见以下的1.     RESET_HEAP()   //参见以下的2.     store_mode_params();       //参见以下3.     save_screen();    //参见以下4.     probe_cards(0);    for (;;) {        if (mode == ASK_VGA)  //如果bootloader告诉kernel具体采用什么VGA模式需要用户自己设置            mode = mode_menu();  //通知用户自己输入VGA模式                    //参见以下5.         if (!set_mode(mode)) //只有设置了正确的显示模式后才允许继续,否则永远循环在for里面            break;        printf("Undefined video mode number: %x/n", mode);        mode = ASK_VGA;    }    boot_params.hdr.vid_mode = mode;    vesa_store_edid();    store_mode_params();    if (do_restore)        restore_screen();} 1. 怎么又要初始化HEAP了?我们不是在main里面做过init_heap了吗?     我们来把RESET_HEAP从宏还原到具体代码, 这个宏在boot.h里面定义如下 #define RESET_HEAP() ((void *)( HEAP = _end ))   那么HEAP和_end又在什么地方呢?   HEAP在main.c里面被定义为全局变量。 char *HEAP = _end;  char *heap_end = _end;        /* Default end of heap = no heap */   而_end则在我在上一篇所提到的setup.ld里。_end其实就是所有.text, .data, .bss被装载进内存之后的内存上段。 . = ALIGN(16); _end = .; 所以,堆HEAP的底就是_end,这是编译器根据每次编译所需要生成的代码,变量在内存中的分布状况自动生成的。堆的顶heap_end就是init_heap里面建立的。堆的使用是从低地址向高地址分配。而栈使用是从高地址向低地址延伸。堆的底和栈的底之间的空间就是HEAP和stack可以使用的空间。 多说一句,栈的底是在header.S里面 movw heap_end_ptr, %dx addw $STACK_SIZE, %dx movzwl %dx, %esp #esp现在就是栈底 栈顶就在init_heap里面 asm("leal %P1(%%esp),%0"         : "=r" (stack_end) : "i" (-STACK_SIZE)); 而RESET_HEAP从理论上来看是多余的,因为HEAP一早就被初始化成_end。 2. store_mode_params的定义就在Video.c里面。代码如下 /* * Store the video mode parameters for later usage by the kernel. * This is done by asking the BIOS except for the rows/columns * parameters in the default 80x25 mode -- these are set directly, * because some very obscure BIOSes supply insane values. */static void store_mode_params(void){    u16 font_size;    int x, y;    /* For graphics mode, it is up to the mode-setting driver     (currently only video-vesa.c) to store the parameters *//*graphic_mode是一个定义在Video-mode.c里面的全局变量,我们之前知道一个定义而未被初始化的变量会放在.bss段里面,而在header.S里面,.bss段里的所有内容已经被初始化为0了,所以这里graphic_mode就应该是0, 而return其实将永远无法执行到。*/     if (graphic_mode        return;       /*store_cursor_position实际上是调用INT 10 AH=0x03 来获取当前光标的位置并将其保存在boot_params.screen_info.orig_x和boot_params.screen_info.orig_y里面 */     store_cursor_position();       /*store_video_mode调用的是INT 10 AH=0x0f来获取当前的显示模式通过AH来返回当前显示模式下总共有可在屏幕上显示几列字符,AL返回当前的显示模式.AL=03代表是彩色显示模式,AL=07代表黑白显示模式。BH里面返回的是当前活动的显示页面是第几个。一般的设计,显卡工作在DOS兼容模式下显示页面是有2个,一个是0,一个是1.这是由于当期,显示都是准备好一个页面然后将其激活。关于显示页面我没有找google大神和翻资料。记得大学时候老师和教材里面是这么说的,懒得查了。有人知道我错的话欢迎指出。 最终返回的AH和BH会被保存到boot_params.screen_info.orig_video_mode和boot_params.screen_info.orig_video_page中*/     store_video_mode();     /*video_segment指向当前的显示内存,在PC中是确定的位置。具体设置的值看以下代码,它会在save_screen中被用到*/     if (boot_params.screen_info.orig_video_mode == 0x07) {        /* MDA, HGC, or VGA in monochrome mode */        video_segment = 0xb000;    } else {        /* CGA, EGA, VGA and so forth */        video_segment = 0xb800;    }    set_fs(0); //将fs段寄存器设置为0    font_size = rdfs16(0x485); /* Font size, BIOS area */ //从fs:0x485的内存处读取一个字,将其存储在font_size中。问题是fs:0x485里面是什么?    boot_params.screen_info.orig_video_points = font_size;    x = rdfs16(0x44a);//从fs:0x44a的内存处读取一个字。问题是fs:0x44a里面是什么?     y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1;//从fs:0x44a的内存处读取一个字。问题是fs:0x484里面是什么?       //至今为止force_x和force_y从来没有被引用和初始话,因为它们在bss段,现在的值就应该是0,x和y不会给改变     if (force_x)        x = force_x;    if (force_y)        y = force_y;    boot_params.screen_info.orig_video_cols = x; /*难道fs:0x44a里面就是当前显示模式下的显示列数目?*/    boot_params.screen_info.orig_video_lines = y;/*难道fs:0x484里面就是当前显示模式下的显示行数目*/} 3. 好了,显示模式已经保存在boot_params.screen_info里面了。接下来save_screen()开始了。它的工作就是保存当前的显示内存(由video_segment指出当前的显示内存的起始地址)。这段代码相对简单,就不做解释了。 static void save_screen(void){    /* Should be called after store_mode_params() */    saved.= boot_params.screen_info.orig_video_cols;    saved.= boot_params.screen_info.orig_video_lines;    saved.curx = boot_params.screen_info.orig_x;    saved.cury = boot_params.screen_info.orig_y;    if (!heap_free(saved.x*saved.y*sizeof(u16)+512))        return;        /* Not enough heap to save the screen */    saved.data = GET_HEAP(u16, saved.x*saved.y);    set_fs(video_segment);    copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16));} 4. 接下来就是probe_cards了 /* Probe the video drivers and have them generate their mode lists. */void probe_cards(int unsafe) //当前unsafe=0{    struct card_info *card;    static u8 probed[2]; //静态未初始化变量应该分配在BSS段,并被header.S里面的bss初始化代码初始为0    if (probed[unsafe])  //这是我们第一次运行, 所以probed应该是初始值0        return;    probed[unsafe] = 1; //置位,下次将不在probe相同的Video Card     for (card = video_cards; card < video_cards_end; card++) {          if (card->unsafe == unsafe) {  //这是我们要probe的卡吗?            if (card->probe)                card->nmodes = card->probe();            else                card->nmodes = 0;        }    }} video_cards和video_cards_end的定义如下并在setup.ld中给出内存中的section位置,但是该值并未在以前的启动代码中被初始化,也没有看到任何对于其的引用。按照一种说法是这个值是由grub填入的,但是问题又出来了。该数组的大小是多少,怎么样分配空间的?由于下载不了grub的源码,没法继续看了。 struct card_info {    const char *card_name;    int (*set_mode)(struct mode_info *mode);    int (*probe)(void);    struct mode_info *modes;    int nmodes;        /* Number of probed modes so far */    int unsafe;        /* Probing is unsafe, only do after "scan" */    u16 xmode_first;    /* Unprobed modes to try to call anyway */    u16 xmode_n;        /* Size of unprobed mode range */};#define __videocard struct card_info __attribute__((section(".videocards")))  //video_cards被关联到.videocards这个段。extern struct card_info video_cards[], video_cards_end[]; 5. set_mode(mode) 其代码如下: /* Set mode (with recalc if specified) */int set_mode(u16 mode){    int rv;    u16 real_mode;    /* Very special mode numbers... */    if (mode == VIDEO_CURRENT_MODE) //如果不想改变当前显示模式,那么什么也别干吧,没人在乎的。        return 0;    /* Nothing to do... */    else if (mode == NORMAL_VGA        mode = VIDEO_80x25; //标准VGA模式是80列25行文本显示    else if (mode == EXTENDED_VGA)        mode = VIDEO_8POINT;//扩展VGA模式是80列50行文本显示    rv = raw_set_mode(mode, &real_mode);    if (rv)        return rv;    if (mode & VIDEO_RECALC)        vga_recalc_vertical();    /* Save the canonical mode number for the kernel, not     an alias, size specification or menu position */#ifndef _WAKEUP    boot_params.hdr.vid_mode = real_mode;#endif    return 0;} raw_set_mode又涉及到video_cards和video_cards_end。看来我必须找出到底哪里初始化了video_cards和video_cards_end. 首先让我们来看一下video_cards在哪里。那么就要首先编译kernel然后使用以下命令来对于setup.elf进行检查。 objdump -D setup.elf|less 我得到的结果如下: Disassembly of section .videocards: 00003a14 <video_cards>: 3a14: b1 39 mov $0x39,%cl 3a16: 00 00 add %al,(
    转载请注明原文地址: https://ibbs.8miu.com/read-2228028.html

    最新回复(0)