自学内容网 自学内容网

grub之loongarch架构调试

一 什么是grub

GNU GRUB 是一个多重操作系统启动管理器。GNU GRUB是由GRUB(GRandUnified Bootloader)派生而来。
GRUB最初由Erich Stefan Boleyn 设计和应用;
主流发行版 Fedora、Redhat、Centos、Kylin 等基于RPM包的系统,在最新版本中都默认GRUB引导;
Slackware目前仍采用LILO;而Debian发行版目前最新的版本也是采用GRUB;

Grub在uefi上也是以一个Grub.efi的方式运行的,但是这个efi是放在操作系统下面的boot/EFI/下面的, 
uefi在运行过程的后期加载Grub的时候会去这个目录load这个Grub模块,Load到内存之后,就开始执行。 

二 grub源码环境的安装

笔者以国产Kylin操作系统为例进行安装:
a) 先拿到grub2 对应的rpm包 
https://download.csdn.net/download/qq_33559839/89560518 
b) 下载之后,将rpm报进行安装 
rpm -ivh grub2-2.12-21.se.02.p01.ky11.src.rpm  
c) 将源码展示出来  
cd /root/rpmbuild
rpmbuild -bp SPECS/grub2.spec 
cd BUILD 
grub-2.12 --》 就是grub2的源码 
find -name main.c 
找到grub2的入口点文件 ---》 ./grub-core/kern/main.c 

三 grub源码分析:

3.0 首先是grub的入口点文件:
vi grub-core/kern/loongarch64/efi/startup.S 就是入口点文件
FUNCTION(_start)
    /*
     * EFI_SYSTEM_TABLE and EFI_HANDLE are passed in $a1/$a0.
     */
    la      $a2, EXT_C(grub_efi_image_handle)
    st.d        $a0, $a2, 0
    la      $a2, EXT_C(grub_efi_system_table)
    st.d        $a1, $a2, 0

    b       EXT_C(grub_main) //跳转到grub_main函数,所以gruub_main就是grub的入口函数
3.1 我们找到入口点文件之后,就可以分析grub的源码了 
grub_main 是grub执行的main函数,绝大多数grub的主要工作都是在这个函数中完成的
/* The main routine.  */
void __attribute__ ((noreturn))
grub_main (void)
{
  /* First of all, initialize the machine.  */
  grub_machine_init ();

  grub_boot_time ("After machine init.");

  /* This breaks flicker-free boot on EFI systems, so disable it there. */
#ifndef GRUB_MACHINE_EFI
  /* Hello.  */
  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
  grub_printf ("Welcome to GRUB!\n\n");
  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
#endif
  ...
}
3.2 先来分析第一个函数:
初始化grub在本平台上要做的各种操作,这个函数与架构是强相关的,x86架构,arm架构,loongarch架构上
的实现都不一样
笔者就以国产loongarch架构为例进行讲解,该函数位于grub-core/kern/loongarch64/efi/init.c中
(loongson架构的位于./grub-core/kern/mips/loongson/init.c文件中) 
grub_machine_init 函数的主要工作:
a) 先获取grub要加载的各个模块的基地址 
问题:grub要加载哪些模块呢?

b) 初始化控制台
c) 堆区内存的映射及初始化(初始化内存管理系统、grub中有一套自己的内存管理方式)
d)  初始化frame buffer
e)初始化字体及终端
f)  ftdbus、网卡、系统定时器、串口等设备的初始化
void      
grub_machine_init (void)                                                              
{
  grub_efi_boot_services_t *b;
  grub_efi_init ();
  b = grub_efi_system_table->boot_services;
  b->create_event (GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL,                   
           GRUB_EFI_TPL_CALLBACK, grub_loongson_increment_timer, NULL, &tmr_evt);     
  b->set_timer (tmr_evt, GRUB_EFI_TIMER_PERIODIC, EFI_TIMER_PERIOD_MILLISECONDS(10)); 
  grub_install_get_time_ms (grub_efi_get_time_ms);                                    
}
3.2.1 grub_efi_init 
我们先简单的分析一下这个函数(位于grub-core/kern/loongarch64/efi/init.c中):
void
grub_machine_init (void)
{
  grub_efi_boot_services_t *b; 
  grub_efi_init (); 
  b = grub_efi_system_table->boot_services;
  b->create_event (GRUB_EFI_EVT_TIMER | GRUB_EFI_EVT_NOTIFY_SIGNAL,
           GRUB_EFI_TPL_CALLBACK, grub_loongson_increment_timer, NULL, &tmr_evt);
  b->set_timer (tmr_evt, GRUB_EFI_TIMER_PERIODIC, EFI_TIMER_PERIOD_MILLISECONDS(10));
  grub_install_get_time_ms (grub_efi_get_time_ms);
}

__attribute__ ((__optimize__ ("-fno-stack-protector"))) void
grub_efi_init (void)
{
  grub_modbase = grub_efi_section_addr ("mods");
  /* First of all, initialize the console so that GRUB can display
     messages.  */
  grub_console_init (); 
  stack_protector_init (); 
  /* Initialize the memory management system.  */
  grub_efi_mm_init (); 
  /*  
   * Lockdown the GRUB and register the shim_lock verifier
   * if the UEFI Secure Boot is enabled.
   */
  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
    {   
      grub_lockdown (); 
      grub_shim_lock_verifier_setup (); 
    }   
  grub_efi_system_table->boot_services->set_watchdog_timer (0, 0, 0, NULL);
  grub_efidisk_init (); 
  grub_efi_register_debug_commands (); 
}
别的先不说,我们先来看看grub的内存是怎么管理的:
void
grub_efi_mm_init (void)
{
  /*
  #ifdef GRUB_CPU_LOONGARCH64
  #define DEFAULT_HEAP_SIZE   0x10000000 
  #else
  #define DEFAULT_HEAP_SIZE   0x2000000
  #endif
  */
  //如果是loongarch架构,grub会在堆区分配256M的内存空间
  if (grub_efi_mm_add_regions (DEFAULT_HEAP_SIZE, GRUB_MM_ADD_REGION_NONE) != GRUB_ERR_NONE)
    grub_fatal ("%s", grub_errmsg);
  grub_mm_add_region_fn = grub_efi_mm_add_regions;
}

static grub_err_t
grub_efi_mm_add_regions (grub_size_t required_bytes, unsigned int flags)
{
  grub_efi_memory_descriptor_t *memory_map;
  grub_efi_memory_descriptor_t *memory_map_end;
  grub_efi_memory_descriptor_t *filtered_memory_map;
  grub_efi_memory_descriptor_t *filtered_memory_map_end;
  grub_efi_uintn_t map_size;
  grub_efi_uintn_t desc_size;
  grub_err_t err;
  int mm_status;

  /* Prepare a memory region to store two memory maps.  */
  /*
  #define MEMORY_MAP_SIZE 0x3000 
  void *
  grub_efi_allocate_any_pages (grub_efi_uintn_t pages)
  {
  #ifdef GRUB_CPU_LOONGARCH64
    return grub_efi_allocate_pages_real (grub_efi_max_usable_address(),
                         pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
                         GRUB_EFI_LOADER_DATA);
  #else
    return grub_efi_allocate_pages_real (GRUB_EFI_MAX_USABLE_ADDRESS,
                         pages, GRUB_EFI_ALLOCATE_MAX_ADDRESS,
                         GRUB_EFI_LOADER_DATA);
  #endif
  }
  //可以看出来loongarch架构grub的最高的可用地址是从csr寄存器中读出来的
  //而其他架构grub可用的最高地址是0xffffffff
  static inline grub_uint64_t grub_efi_max_usable_address(void)
  {                      
    grub_uint64_t addr;  
    asm volatile ("csrrd %0, 0x181" : "=r" (addr));
    return addr |= 0xffffffffffUL;
  }
  所以下面的这个函数,是grub先申请6页的内存保存memory map
  */
  memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));
  if (! memory_map)
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for memory map");

  /* Obtain descriptors for available memory.  */
  map_size = MEMORY_MAP_SIZE;
  //获取EFI规范中定义的内存映射。如果成功返回1,如果部分返回0,或者如果发生错误返回-1。
  mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0);

  if (mm_status == 0)
    {
      grub_efi_free_pages
    ((grub_efi_physical_address_t) ((grub_addr_t) memory_map),
     2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));

      /* Freeing/allocating operations may increase memory map size.  */
      map_size += desc_size * 32;

      memory_map = grub_efi_allocate_any_pages (2 * BYTES_TO_PAGES (map_size));
      if (! memory_map)
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "cannot allocate memory for new memory map");

      mm_status = grub_efi_get_memory_map (&map_size, memory_map, 0,
                       &desc_size, 0);
    }
  if (mm_status < 0)
    return grub_error (GRUB_ERR_OUT_OF_MEMORY, "error fetching memory map from EFI");

  memory_map_end = NEXT_MEMORY_DESCRIPTOR (memory_map, map_size);

  filtered_memory_map = memory_map_end;

  filtered_memory_map_end = filter_memory_map (memory_map, filtered_memory_map,
                           desc_size, memory_map_end);

  /* Sort the filtered descriptors, so that GRUB can allocate pages
     from smaller regions.  */
  sort_memory_map (filtered_memory_map, desc_size, filtered_memory_map_end);

  /* Allocate memory regions for GRUB's memory management.  */
  err = add_memory_regions (filtered_memory_map, desc_size,
                filtered_memory_map_end,
                BYTES_TO_PAGES (required_bytes),
                flags);
  if (err != GRUB_ERR_NONE)
    return err;
    
 #if 0
  /* For debug. 该打印可以将grub分配的内存信息打印出来*/
  map_size = MEMORY_MAP_SIZE;

  if (grub_efi_get_memory_map (&map_size, memory_map, 0, &desc_size, 0) < 0)
    grub_fatal ("cannot get memory map");
  grub_printf ("printing memory map\n");
  print_memory_map (memory_map, desc_size,
            NEXT_MEMORY_DESCRIPTOR (memory_map, map_size));
  grub_fatal ("Debug. ");
#endif

  /* Release the memory maps.  */
  grub_efi_free_pages ((grub_addr_t) memory_map,
               2 * BYTES_TO_PAGES (MEMORY_MAP_SIZE));

  return GRUB_ERR_NONE;
}

原文地址:https://blog.csdn.net/qq_33559839/article/details/140573431

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!