asm.S是stage2的重要组成部分,它的作用主要是调用commen.c的c函数,而该函数会利用asm.S提供的函数进行各种引导前设置,将multiboot相关的各种内容准备好。-------------------------------------------------
I. asm.S
-------------------------------------------------
#ifdef STAGE1_5
# define ABS(x) ((x) - EXT_C(main) + 0x2200)
#else
# define ABS(x) ((x) - EXT_C(main) + 0x8200)
#endif
//pgnotes: 注意这里的EXT_C宏在调用c函数的时候,可以自动将函数名前面加上"_",视
//#ifdef HAVE_ASM_USCORE而定(FIXME)
ADDR32 movl %ebp, EXT_C(install_second_sector)
//pgnotes: ADDR32/DATA32编译器用的,因为前面有.code16实模式的说明
ENTRY(real_to_prot)
.code16
cli
/* load the GDT register */
DATA32 ADDR32 lgdt gdtdesc
/* turn on protected mode */
movl %cr0, %eax
orl $CR0_PE_ON, %eax
movl %eax, %cr0 //pgnotes: 参考ia,置1个bit即可进入保护模式
/* jump to relocation, flush prefetch queue, and reload %cs */
DATA32 ljmp $PROT_MODE_CSEG, $protcseg //pgnotes:将cs赋值为8000段
/*
* The ".code32" directive only works in GAS, the GNU assembler!
* This gets out of "16-bit" mode.
*/
.code32
protcseg:
/* reload other segment registers */
movw $PROT_MODE_DSEG, %ax //pgnotes: 0x10, 选择子,对应于gdt中5个描述符的第3个,装载了选择子,就有了BASE和LIMIT/ATTR
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* put the return address in a known safe location */
movl (%esp), %eax //pgnotes: call 指令将地址压入了堆栈
movl %eax, STACKOFF //pgnotes: 将返回地址放入0x2000-0x10这个地址内
/* get protected mode stack */
movl protstack, %eax //pgnotes: 这个地址现在不明白...
movl %eax, %esp
movl %eax, %ebp
/* get return address onto the right stack */
movl STACKOFF, %eax
movl %eax, (%esp) //pgnotes:将返回地址放入保护模式下的新堆栈
/* zero %eax */
xorl %eax, %eax
/* return on the old (or initialized) stack! */
ret //pgnotes:设置完保护模式后返回call之后的语句
/* clean out the bss */ //pgnotes:bss段,存储全局变量和静态变量,使用前要清零的。
/* set %edi to the bss starting address */
#if defined(HAVE_USCORE_USCORE_BSS_START_SYMBOL)
movl $__bss_start, %edi
#elif defined(HAVE_USCORE_EDATA_SYMBOL)
movl $_edata, %edi
#elif defined(HAVE_EDATA_SYMBOL)
movl $edata, %edi
#endif
/* set %ecx to the bss end */
#if defined(HAVE_END_SYMBOL)
movl $end, %ecx
#elif defined(HAVE_USCORE_END_SYMBOL)
movl $_end, %ecx
#endif
/* compute the bss length */
subl %edi, %ecx //pgnotes:bss段长度计算,起始、结束地址具体被哪个变量赋值的,望指教(FIXME)
/* zero %al */
xorb %al, %al
/* set the direction */
cld
/* clean out */
rep
stosb //pgnotes:循环将es:edi赋值为0, ecx计数
/*
* Call the start of main body of C code, which does some
* of it's own initialization before transferring to "cmain".
*/
call EXT_C(init_bios_info) //pgnotes:跳转到commen.c中的c函数执行
-------------------------------------------------
II. commen.c部分代码分析
-------------------------------------------------
//pgnotes: init_bios_info (void)首先调用get_memsize ();
/* memory probe routines */
int
get_memsize (int type)
{
if (! type)
return CONVENTIONAL_MEMSIZE >> 10; //pgnotes: 640*1024byte>>10= 640k, 按照multiboot协议,需要以k为单位
else
return EXTENDED_MEMSIZE >> 10; //pgnotes: 3*1024*1024byte>>10 = 3072k
}
gateA20 (1); //pgnotes: 允许访问1M以上内存,而不会回转
cont = get_mmap_entry ((void *) addr, cont); //pgnotes: 通过do.while逐步将预先定义好的内存map填到addr地址处
下面我们看看get_mmap_entry ():
int
get_mmap_entry (struct mmar_desc *desc, int cont)
{
/* Record the memory map statically. */
static struct mmar_desc desc_table[] =
{
/* The conventional memory. */
{
MMAR_DESC_LENGTH, //pgnotes: 20,每个struct24字节,所以每次需要加20+4,来逐步定位结构数组的下一个元素。
0,
CONVENTIONAL_MEMSIZE,
MMAR_DESC_TYPE_AVAILABLE //pgnotes: 0~640k, 是OS可用内存
},
/* BIOS RAM and ROM (such as video memory). */
{
MMAR_DESC_LENGTH,
CONVENTIONAL_MEMSIZE,
0x100000 - CONVENTIONAL_MEMSIZE, //pgnotes: 640k~1M的内存是BIOS使用的比如VGA ROM、shadow memory
MMAR_DESC_TYPE_RESERVED //pgnotes: reserved类型,OS不可用
},
/* The extended memory. */
{
MMAR_DESC_LENGTH,
0x100000,
EXTENDED_MEMSIZE, //pgnotes: 1M~ 1M+ 3M也是OS可用
MMAR_DESC_TYPE_AVAILABLE
}
};
int num = sizeof (desc_table) / sizeof (*desc_table); //pgnotes: num等于3
if (cont < 0 || cont >= num)
{
/* Should not happen. */
desc->desc_len = 0;
}
else
{
/* Copy the entry. */
*desc = desc_table[cont++];
/* If the next entry exists, return the index. */
if (cont < num)
return cont;
}
return 0;
}
/*
* This is to get the lower memory, and upper memory (up to the
* first memory hole), into the "mbi.mem_{lower,upper}"
* elements. This is for OS's that don't care about the memory
* map, but might care about total RAM available.
*/ //pgnotes: 有的OS不关心mmap...(懒啊),所以我们计算好可用的内存大小,分别到下面两个变量里
mbi.mem_lower = mmap_avail_at (0) >> 10; //pgnotes: 调用后mbi.mem_lower= 640, 单位k
mbi.mem_upper = mmap_avail_at (0x100000) >> 10; pgnotes: 调用后mbi.mem_lower= 3072, 单位k.
/* Find the maximum available address. Ignore any memory holes. */
for (max_addr = 0, addr = mbi.mmap_addr;
addr < mbi.mmap_addr + mbi.mmap_length;
addr += *((unsigned long *) addr) + 4)
{
struct AddrRangeDesc *desc = (struct AddrRangeDesc *) addr;
if (desc->Type == MB_ARD_MEMORY && desc->Length > 0
&& desc->BaseAddr + desc->Length > max_addr)
max_addr = desc->BaseAddr + desc->Length; //pgnotes:for完了,就把1M+3M这个地址放到了max_addr
}
下面这段程序将addr指向的地址放入drive相关的数据结构,并且保存该地址到mbi.drives_addr.
mbi.drives_length = 0;
mbi.drives_addr = addr;
/* For now, GRUB doesn't probe floppies, since it is trivial to map
floppy drives to BIOS drives. */
for (drive = 0x80; drive < 0x88; drive++)
{
struct geometry geom;
struct drive_info *info = (struct drive_info *) addr;
unsigned short *port;
/* Get the geometry. This ensures that the drive is present. */
if (get_diskinfo (drive, &geom)) //pgnotes: 该函数将struct geometry赋值
break;
/* Clean out the I/O map. */
grub_memset ((char *) io_map, 0,
IO_MAP_SIZE * sizeof (unsigned short));
/* Disable to probe I/O ports temporarily, because this doesn't
work with some BIOSes (maybe they are too buggy). */
#if 0
/* Track the int13 handler. */
track_int13 (drive);
#endif
/* Set the information. */
info->drive_number = drive;
info->drive_mode = ((geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
? MB_DI_LBA_MODE : MB_DI_CHS_MODE);
info->drive_cylinders = geom.cylinders;
info->drive_heads = geom.heads;
info->drive_sectors = geom.sectors;
addr += sizeof (struct drive_info);
for (port = io_map; *port; port++, addr += sizeof (unsigned short))
*((unsigned short *) addr) = *port;
info->size = addr - (unsigned long) info;
mbi.drives_length += info->size;
}
上面的程序中get_diskinfo (drive, &geom) 值得看看,但也就这一行比较有用:
version = check_int13_extensions (drive); //pgnotes:这个汇编函数有protect-->real-->protect,原因是需要用实模式下的中断,保护模式下还没有设置中断向量寄存器。类似的其他的中断处理,也都是在asm.S中这样处理的。
commen.c中的下面的部分就不细说了,最后会通过调用cmain()到stage2.c中继续执行。