首页 > 其他 > 详细

Linux初始化的汇编代码

时间:2014-02-11 16:03:29      阅读:661      评论:0      收藏:0      [点我收藏+]

1. 内核文件布局

首先看一下arch/x86/boot/Setup.ld文件,它定义了链接后的内核文件布局。

   1: /*
   2:  * setup.ld
   3:  *
   4:  * Linker script for the i386 setup code
   5:  */
   6: OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386")
   7: OUTPUT_ARCH(i386)
   8: ENTRY(_start)
   9:  
  10: SECTIONS
  11: {
  12:     . = 0;
  13:     .bstext        : { *(.bstext) }
  14:     .bsdata        : { *(.bsdata) }
  15:  
  16:     . = 497;
  17:     .header        : { *(.header) }
  18:     .entrytext    : { *(.entrytext) }
  19:     .inittext    : { *(.inittext) }
  20:     .initdata    : { *(.initdata) }
  21:     __end_init = .;
  22:  
  23:     .text        : { *(.text) }
  24:     .text32        : { *(.text32) }
  25:  
  26:     . = ALIGN(16);
  27:     .rodata        : { *(.rodata*) }
  28:  
  29:     .videocards    : {
  30:         video_cards = .;
  31:         *(.videocards)
  32:         video_cards_end = .;
  33:     }
  34:  
  35:     . = ALIGN(16);
  36:     .data        : { *(.data*) }
  37:  
  38:     .signature    : {
  39:         setup_sig = .;
  40:         LONG(0x5a5aaa55)
  41:     }
  42:  
  43:  
  44:     . = ALIGN(16);
  45:     .bss        :
  46:     {
  47:         __bss_start = .;
  48:         *(.bss)
  49:         __bss_end = .;
  50:     }
  51:     . = ALIGN(16);
  52:     _end = .;
  53:  
  54:     /DISCARD/ : { *(.note*) }
  55:  
  56:     /*
  57:      * The ASSERT() sink to . is intentional, for binutils 2.14 compatibility:
  58:      */
  59:     . = ASSERT(_end <= 0x8000, "Setup too big!");
  60:     . = ASSERT(hdr == 0x1f1, "The setup header has the wrong offset!");
  61:     /* Necessary for the very-old-loader check to work... */
  62:     . = ASSERT(__end_init <= 5*512, "init sections too big!");
  63:  
  64: }

 

2. Boot Sector启动扇区

通过该文件,可以看到,.bstext以及.bsdata段都定义在第一个sector中,实际上bs就是boot sector的简称。

在通过Grub、Lilo等引导程序启动内核时,不会将控制权交给boot sector中的代码来执行,只有在没有安装任何引导程序,并且将内核映像写到了软盘中时,才会从boot sector引导。

因此,boot sector的存在,只是为了兼容Linux诞生时的做法,即从软盘启动,就像PE文件头部都会有一段

This program cannot be run in DOS mode.

 

的提示一样。

 

我们看一下Boot Sector里面存放了什么内容:

.bstext section

   1: .section ".bstext", "ax"
   2:  
   3: .global bootsect_start
   4: sect_start:
   5:  
   6: # Normalize the start address
   7: ljmp    $BOOTSEG, $start2
   8:  
   9: t2:
  10: movw    %cs, %ax
  11: movw    %ax, %ds
  12: movw    %ax, %es
  13: movw    %ax, %ss
  14: xorw    %sp, %sp
  15: sti
  16: cld
  17:  
  18: movw    $bugger_off_msg, %si
  19:  
  20: loop:
  21: lodsb
  22: andb    %al, %al
  23: jz    bs_die
  24: movb    $0xe, %ah
  25: movw    $7, %bx
  26: int    $0x10
  27: jmp    msg_loop
  28:  
  29: ie:
  30: # Allow the user to press a key, then reboot
  31: xorw    %ax, %ax
  32: int    $0x16
  33: int    $0x19
  34:  
  35: # int 0x19 should never return.  In case it does anyway,
  36: # invoke the BIOS reset code...
  37: ljmp    $0xf000,$0xfff0

.bsdata section

   1: .section ".bsdata", "a"
   2: er_off_msg:
   3: .ascii    "Direct booting from floppy is no longer supported.\r\n"
   4: .ascii    "Please use a boot loader program instead.\r\n"
   5: .ascii    "\n"
   6: .ascii    "Remove disk and press any key to reboot . . .\r\n"
   7: .byte    0
   8:  
   9:  
  10: # Kernel attributes; used by setup.  This is part 1 of the
  11: # header, from the old boot sector.

.header section的一部分

   1: .section ".header", "a"
   2: .globl    hdr
   3:  
   4: p_sects:    .byte 0            /* Filled in by build.c */
   5: _flags:    .word ROOT_RDONLY
   6: ize:    .long 0            /* Filled in by build.c */
   7: size:    .word 0            /* Obsolete */
   8: mode:    .word SVGA_MODE
   9: _dev:    .word 0            /* Filled in by build.c */
  10: _flag:    .word 0xAA55
  11:  
  12: # offset 512, entry point
基本上没有什么有价值的信息。

 

在LXR上面的最新代码(http://lxr.oss.org.cn/source/arch/x86/boot/header.S)中,有

 45 #ifdef CONFIG_EFI_STUB
 46         # "MZ", MS-DOS header
 47         .byte 0x4d
 48         .byte 0x5a
 49 #endif
这是为了支持UEFI启动,UEFI Image的头部是用PE32+格式定义的,因此有“MZ”的Signature。

2.1.1 UEFI Images
UEFI Images are a class of files defined by UEFI that contain executable code. The most
distinguishing feature of UEFI Images is that the first set of bytes in the UEFI Image file contains an
image header that defines the encoding of the executable image.

UEFI uses a subset of the PE32+ image format with a modified header signature. The modification
to the signature value in the PE32+ image is done to distinguish UEFI images from normal PE32
executables. The “+” addition to PE32 provides the 64-bit relocation fix-up extensions to standard
PE32 format.

参考:http://www.uefi.org/sites/default/files/resources/UEFI_2.4_0.pdf

3. Bootloader会首先将执行权利交给内核的哪段代码?

 

我们看常用的Grub:

3.4 BIOS installation
MBR
The partition table format traditionally used on PC BIOS platforms is called the Master
Boot Record (MBR) format; this is the format that allows up to four primary partitions
and additional logical partitions. With this partition table format, there are two ways to
install GRUB: it can be embedded in the area between the MBR and the first partition
(called by various names, such as the "boot track", "MBR gap", or "embedding area", and
which is usually at least 31 KiB), or the core image can be installed in a file system and a
list of the blocks that make it up can be stored in the first sector of that partition.
Each of these has different problems. There is no way to reserve space in the embedding area with complete safety, and some proprietary software is known to use it to
make it difficult for users to work around licensing restrictions; and systems are sometimes
partitioned without leaving enough space before the first partition. On the other hand,
installing to a filesystem means that GRUB is vulnerable to its blocks being moved around
by filesystem features such as tail packing, or even by aggressive fsck implementations, so
this approach is quite fragile; and this approach can only be used if the ‘/boot’ filesystem
is on the same disk that the BIOS boots from, so that GRUB does not have to rely on
guessing BIOS drive numbers.
The GRUB development team generally recommends embedding GRUB before the
first partition, unless you have special requirements. You must ensure that the first partition
starts at least 31 KiB (63 sectors) from the start of the disk; on modern disks, it is often a
performance advantage to align partitions on larger boundaries anyway, so the first partition
might start 1 MiB from the start of the disk.

参考:http://www.gnu.org/software/grub/manual/grub.pdf

 

当计算机加电自检后,ROM BIOS加载MBR(主引导扇区,即硬盘第一扇区)中的代码到内存中,这个扇区一共512字节,前446字节内容存放grub(bootloader)的关键引导程序,接着64字节放置硬盘分区表DPT(Disk Partition Table),一共四可以有四个主分区,占64个字节,这也是为什么主分区最多只有四个的原因,最后2个字节是固定的标志0x55AA。当BIOS把引导程序加载到内存后就把控制权交给grub,而后grub的剩余代码将完成其它代码的加载和搬移以及文件系统初始化查找等工作,最终加载内核映像文件,从而把控制权交给真正的内核运行。

参考:http://www.linuxidc.com/Linux/2013-03/81119.htm

还有几篇关于Linux引导程序的介绍性文章:

http://www.ibm.com/developerworks/cn/linux/l-lpic1-v3-102-2/

http://www.ibm.com/developerworks/cn/linux/l-linuxboot/

 

都没有直接回答这个问题。

 

查看Grub的源码:grub_linux_boot函数

   1: static grub_err_t
   2: grub_linux_boot (void)
   3: {
   4:   int e820_num;
   5:   grub_err_t err = 0;
   6:   const char *modevar;
   7:   char *tmp;
   8:   struct grub_relocator32_state state;
   9:   void *real_mode_mem;
  10:   grub_addr_t real_mode_target = 0;
  11:   grub_size_t real_size, mmap_size;
  12:   grub_size_t cl_offset;
  13:  
  14: #ifdef GRUB_MACHINE_IEEE1275
  15:   {
  16:     const char *bootpath;
  17:     grub_ssize_t len;
  18:  
  19:     bootpath = grub_env_get ("root");
  20:     if (bootpath)
  21:       grub_ieee1275_set_property (grub_ieee1275_chosen,
  22:                   "bootpath", bootpath,
  23:                   grub_strlen (bootpath) + 1,
  24:                   &len);
  25:     linux_params.ofw_signature = GRUB_LINUX_OFW_SIGNATURE;
  26:     linux_params.ofw_num_items = 1;
  27:     linux_params.ofw_cif_handler = (grub_uint32_t) grub_ieee1275_entry_fn;
  28:     linux_params.ofw_idt = 0;
  29:   }
  30: #endif
  31:  
  32:   modevar = grub_env_get ("gfxpayload");
  33:  
  34:   /* Now all graphical modes are acceptable.
  35:      May change in future if we have modes without framebuffer.  */
  36:   if (modevar && *modevar != 0)
  37:     {
  38:       tmp = grub_xasprintf ("%s;" DEFAULT_VIDEO_MODE, modevar);
  39:       if (! tmp)
  40:     return grub_errno;
  41: #if ACCEPTS_PURE_TEXT
  42:       err = grub_video_set_mode (tmp, 0, 0);
  43: #else
  44:       err = grub_video_set_mode (tmp, GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
  45: #endif
  46:       grub_free (tmp);
  47:     }
  48:   else
  49:     {
  50: #if ACCEPTS_PURE_TEXT
  51:       err = grub_video_set_mode (DEFAULT_VIDEO_MODE, 0, 0);
  52: #else
  53:       err = grub_video_set_mode (DEFAULT_VIDEO_MODE,
  54:                  GRUB_VIDEO_MODE_TYPE_PURE_TEXT, 0);
  55: #endif
  56:     }
  57:  
  58:   if (err)
  59:     {
  60:       grub_print_error ();
  61:       grub_puts_ (N_("Booting in blind mode"));
  62:       grub_errno = GRUB_ERR_NONE;
  63:     }
  64:  
  65:   if (grub_linux_setup_video (&linux_params))
  66:     {
  67: #if defined (GRUB_MACHINE_PCBIOS) || defined (GRUB_MACHINE_COREBOOT) || defined (GRUB_MACHINE_QEMU)
  68:       linux_params.have_vga = GRUB_VIDEO_LINUX_TYPE_TEXT;
  69:       linux_params.video_mode = 0x3;
  70: #else
  71:       linux_params.have_vga = 0;
  72:       linux_params.video_mode = 0;
  73:       linux_params.video_width = 0;
  74:       linux_params.video_height = 0;
  75: #endif
  76:     }
  77:  
  78:  
  79: #ifndef GRUB_MACHINE_IEEE1275
  80:   if (linux_params.have_vga == GRUB_VIDEO_LINUX_TYPE_TEXT)
  81: #endif
  82:     {
  83:       grub_term_output_t term;
  84:       int found = 0;
  85:       FOR_ACTIVE_TERM_OUTPUTS(term)
  86:     if (grub_strcmp (term->name, "vga_text") == 0
  87:         || grub_strcmp (term->name, "console") == 0
  88:         || grub_strcmp (term->name, "ofconsole") == 0)
  89:       {
  90:         grub_uint16_t pos = grub_term_getxy (term);
  91:         linux_params.video_cursor_x = pos >> 8;
  92:         linux_params.video_cursor_y = pos & 0xff;
  93:         linux_params.video_width = grub_term_width (term);
  94:         linux_params.video_height = grub_term_height (term);
  95:         found = 1;
  96:         break;
  97:       }
  98:       if (!found)
  99:     {
 100:       linux_params.video_cursor_x = 0;
 101:       linux_params.video_cursor_y = 0;
 102:       linux_params.video_width = 80;
 103:       linux_params.video_height = 25;
 104:     }
 105:     }
 106:  
 107:   mmap_size = find_mmap_size ();
 108:   /* Make sure that each size is aligned to a page boundary.  */
 109:   cl_offset = ALIGN_UP (mmap_size + sizeof (linux_params), 4096);
 110:   if (cl_offset < ((grub_size_t) linux_params.setup_sects << GRUB_DISK_SECTOR_BITS))
 111:     cl_offset = ALIGN_UP ((grub_size_t) (linux_params.setup_sects
 112:                      << GRUB_DISK_SECTOR_BITS), 4096);
 113:   real_size = ALIGN_UP (cl_offset + maximal_cmdline_size, 4096);
 114:  
 115: #ifdef GRUB_MACHINE_EFI
 116:   efi_mmap_size = find_efi_mmap_size ();
 117:   if (efi_mmap_size == 0)
 118:     return grub_errno;
 119: #endif
 120:  
 121:   grub_dprintf ("linux", "real_size = %x, mmap_size = %x\n",
 122:         (unsigned) real_size, (unsigned) mmap_size);
 123:  
 124:   auto int NESTED_FUNC_ATTR hook (grub_uint64_t, grub_uint64_t,
 125:                   grub_memory_type_t);
 126:   int NESTED_FUNC_ATTR hook (grub_uint64_t addr, grub_uint64_t size,
 127:                  grub_memory_type_t type)
 128:     {
 129:       /* We must put real mode code in the traditional space.  */
 130:       if (type != GRUB_MEMORY_AVAILABLE || addr > 0x90000)
 131:     return 0;
 132:  
 133:       if (addr + size < 0x10000)
 134:     return 0;
 135:  
 136:       if (addr < 0x10000)
 137:     {
 138:       size += addr - 0x10000;
 139:       addr = 0x10000;
 140:     }
 141:  
 142:       if (addr + size > 0x90000)
 143:     size = 0x90000 - addr;
 144:  
 145:       if (real_size + efi_mmap_size > size)
 146:     return 0;
 147:  
 148:       grub_dprintf ("linux", "addr = %lx, size = %x, need_size = %x\n",
 149:             (unsigned long) addr,
 150:             (unsigned) size,
 151:             (unsigned) (real_size + efi_mmap_size));
 152:       real_mode_target = ((addr + size) - (real_size + efi_mmap_size));
 153:       return 1;
 154:     }
 155: #ifdef GRUB_MACHINE_EFI
 156:   grub_efi_mmap_iterate (hook, 1);
 157:   if (! real_mode_target)
 158:     grub_efi_mmap_iterate (hook, 0);
 159: #else
 160:   grub_mmap_iterate (hook);
 161: #endif
 162:   grub_dprintf ("linux", "real_mode_target = %lx, real_size = %x, efi_mmap_size = %x\n",
 163:                 (unsigned long) real_mode_target,
 164:         (unsigned) real_size,
 165:         (unsigned) efi_mmap_size);
 166:  
 167:   if (! real_mode_target)
 168:     return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate real mode pages");
 169:  
 170:   {
 171:     grub_relocator_chunk_t ch;
 172:     err = grub_relocator_alloc_chunk_addr (relocator, &ch,
 173:                        real_mode_target,
 174:                        (real_size + efi_mmap_size));
 175:     if (err)
 176:      return err;
 177:     real_mode_mem = get_virtual_current_address (ch);
 178:   }
 179:   efi_mmap_buf = (grub_uint8_t *) real_mode_mem + real_size;
 180:  
 181:   grub_dprintf ("linux", "real_mode_mem = %lx\n",
 182:                 (unsigned long) real_mode_mem);
 183:  
 184:   struct linux_kernel_params *params;
 185:  
 186:   params = real_mode_mem;
 187:  
 188:   *params = linux_params;
 189:   params->cmd_line_ptr = real_mode_target + cl_offset;
 190:   grub_memcpy ((char *) params + cl_offset, linux_cmdline,
 191:            maximal_cmdline_size);
 192:  
 193:   grub_dprintf ("linux", "code32_start = %x\n",
 194:         (unsigned) params->code32_start);
 195:  
 196:   auto int NESTED_FUNC_ATTR hook_fill (grub_uint64_t, grub_uint64_t,
 197:                   grub_memory_type_t);
 198:   int NESTED_FUNC_ATTR hook_fill (grub_uint64_t addr, grub_uint64_t size, 
 199:                   grub_memory_type_t type)
 200:     {
 201:       grub_uint32_t e820_type;
 202:       switch (type)
 203:         {
 204:         case GRUB_MEMORY_AVAILABLE:
 205:       e820_type = GRUB_E820_RAM;
 206:       break;
 207:  
 208:         case GRUB_MEMORY_ACPI:
 209:       e820_type = GRUB_E820_ACPI;
 210:       break;
 211:  
 212:         case GRUB_MEMORY_NVS:
 213:       e820_type = GRUB_E820_NVS;
 214:       break;
 215:  
 216:         case GRUB_MEMORY_BADRAM:
 217:       e820_type = GRUB_E820_BADRAM;
 218:       break;
 219:  
 220:         default:
 221:           e820_type = GRUB_E820_RESERVED;
 222:         }
 223:       if (grub_e820_add_region (params->e820_map, &e820_num,
 224:                 addr, size, e820_type))
 225:     return 1;
 226:  
 227:       return 0;
 228:     }
 229:  
 230:   e820_num = 0;
 231:   if (grub_mmap_iterate (hook_fill))
 232:     return grub_errno;
 233:   params->mmap_size = e820_num;
 234:  
 235: #ifdef GRUB_MACHINE_EFI
 236:   {
 237:     grub_efi_uintn_t efi_desc_size;
 238:     grub_size_t efi_mmap_target;
 239:     grub_efi_uint32_t efi_desc_version;
 240:     err = grub_efi_finish_boot_services (&efi_mmap_size, efi_mmap_buf, NULL,
 241:                      &efi_desc_size, &efi_desc_version);
 242:     if (err)
 243:       return err;
 244:     
 245:     /* Note that no boot services are available from here.  */
 246:     efi_mmap_target = real_mode_target 
 247:       + ((grub_uint8_t *) efi_mmap_buf - (grub_uint8_t *) real_mode_mem);
 248:     /* Pass EFI parameters.  */
 249:     if (grub_le_to_cpu16 (params->version) >= 0x0208)
 250:       {
 251:     params->v0208.efi_mem_desc_size = efi_desc_size;
 252:     params->v0208.efi_mem_desc_version = efi_desc_version;
 253:     params->v0208.efi_mmap = efi_mmap_target;
 254:     params->v0208.efi_mmap_size = efi_mmap_size;
 255:  
 256: #ifdef __x86_64__
 257:     params->v0208.efi_mmap_hi = (efi_mmap_target >> 32);
 258: #endif
 259:       }
 260:     else if (grub_le_to_cpu16 (params->version) >= 0x0206)
 261:       {
 262:     params->v0206.efi_mem_desc_size = efi_desc_size;
 263:     params->v0206.efi_mem_desc_version = efi_desc_version;
 264:     params->v0206.efi_mmap = efi_mmap_target;
 265:     params->v0206.efi_mmap_size = efi_mmap_size;
 266:       }
 267:     else if (grub_le_to_cpu16 (params->version) >= 0x0204)
 268:       {
 269:     params->v0204.efi_mem_desc_size = efi_desc_size;
 270:     params->v0204.efi_mem_desc_version = efi_desc_version;
 271:     params->v0204.efi_mmap = efi_mmap_target;
 272:     params->v0204.efi_mmap_size = efi_mmap_size;
 273:       }
 274:   }
 275: #endif
 276:  
 277:   /* FIXME.  */
 278:   /*  asm volatile ("lidt %0" : : "m" (idt_desc)); */
 279:   state.ebp = state.edi = state.ebx = 0;
 280:   state.esi = real_mode_target;
 281:   state.esp = real_mode_target;
 282:   state.eip = params->code32_start;
 283:   return grub_relocator32_boot (relocator, state, 0);
 284: }

也没有明确的暗示。

 

How to boot an OS directly with GRUB

Multiboot (see Multiboot Specification) is the native format supported by GRUB. For the sake of convenience, there are also support for Linux, FreeBSD, NetBSD and OpenBSD. If you want to boot other operating systems, you will have to chain-load them (see Chain-loading).

Generally, GRUB can boot any Multiboot-compliant OS in the following steps:

  1. Set GRUB‘s root device to the drive where the OS images are stored by the command root (see root).
  2. Load the kernel image by the command kernel (see kernel).
  3. If you need modules, load them with the command module (see module) or modulenounzip (see modulenounzip).
  4. Run the command boot (see boot).

Linux, FreeBSD, NetBSD and OpenBSD can be booted in a similar manner. You can load a kernel image by the command kernel and then run the command boot. If the kernel requires some parameters, just append the parameters to kernel, after the file name of the kernel. Also, please refer to OS-specific notes, for the information on your OS-specific issues.

参考:http://ftp.gnu.org/pub/pub/pub/old-gnu/Manuals/grub-0.92/html_mono/grub.html#Loading%20an%20operating%20system%20directly

所有这些尝试都失败后,我们先假设Grub会将控制权交给Setup Sector的起始位置,即内核映像中的第二个扇区。

即跳转到_start标号处执行:

   1: .globl    _start
   2: rt:
   3:     # Explicitly enter this as bytes, or the assembler
   4:     # tries to generate a 3-byte jump here, which causes
   5:     # everything else to push off to the wrong offset.
   6:     .byte    0xeb        # short (2-byte) jump
   7:     .byte    start_of_setup-1f
   8: 1:
   9:  
  10: # Part 2 of the header, from the old setup.S

Linux初始化的汇编代码

原文:http://www.cnblogs.com/long123king/p/3543872.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!