u-boot(六) - 详细启动流程

news2024/12/26 0:32:02

一,u-boot启动第一阶段

1,启动流程

ENTRY(_start) //arch/arm/lib/vectors.S
----b resets //arch/arm/cpu/armv7/start.S
--------b save_boot_params
------------b save_boot_params_ret //将cpu的工作模式设置为SVC32模式(即管理模式),同时将中断禁止位与快速中断禁止位都设置为1, 以此屏蔽IRQ和FIQ的中断
--------bl    cpu_init_cp15 //关闭mmu,不需要它转换地址,直接操作寄存器方便快捷
--------bl    cpu_init_crit
------------b    lowlevel_init //arch/arm/cpu/armv7/lowlevel_init.S ,与特定开发板相关的初始化函数,在这个函数里会做一些pll初始化,如果不是从内存启动,则会做内存初始化,方便后续拷贝到内存中运行。
--------bl    _main //arch/arm/lib/crt0.S //初始化c语言环境,以便调用board_init_f函数。这个环境只提供了一个堆栈和一个存储GD(全局数据)结构的地方,两者都位于一些可用的RAM中。在调用board_init_f()之前,GD应该被归零。
------------bl          board_init_f_alloc_reserve // common/init/board_init.c,该函数主要作用是保留早期malloc区域,且为GD(全局数据区)留出空间,函数返回值也是r0,r0保存着预留早期malloc区域和GD后的地址
------------bl        board_init_f_init_reserve //common/init/board_init.c 将GD区域清零,返回最初malloc区域的地址
------------bl        board_init_f //common/board_f.c
----------------initcall_run_list(init_sequence_f)
------------b    relocate_code //arch/arm/lib/relocate.S,实现u-boot自身重定位的
----------------ldr       r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
----------------ldr       r2, =__image_copy_end   /* r2 <- SRC &__image_copy_end */ 从__image_copy_start地址到__image_copy_end地址中间包含了代码段、数据段以及只读数据段,但是不包括动态链接rel_dyn部分
----------------ldr       r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
----------------ldr       r3, =__rel_dyn_end      /* r3 <- SRC &__rel_dyn_end */ 直到修改完整个__rel_dyn段后结束,完成重定位
------------bl    relocate_vectors //主要完成的工作就是实现异常向量表的重定位,拷贝到正确的地址中去

------------bl    c_runtime_cpu_setup //关闭指令缓存I-cache,重定位后到了新的介质中运行也是要设置一下运行环境

------------ldr    r0, =__bss_start    /* this is auto-relocated! */
------------ldr    r3, =__bss_end        /* this is auto-relocated! */
------------bl    memset //清除BSS段


------------ldr    pc, =board_init_r    /* this is auto-relocated! */ 第二阶段的入口board_init_r,common/board_r.c
----------------initcall_run_list(init_sequence_r)

_main:

arch/arm/lib/crt0.S

/*
* entry point of crt0 sequence
*/

ENTRY(_main)

/*
* Set up initial C runtime environment and call board_init_f(0).
*/

#if defined(CONFIG_TPL_BUILD) && defined(CONFIG_TPL_NEEDS_SEPARATE_STACK)
    ldr    r0, =(CONFIG_TPL_STACK)
#elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
    ldr    r0, =(CONFIG_SPL_STACK)
#else
    ldr    r0, =(CONFIG_SYS_INIT_SP_ADDR)
#endif
    bic    r0, r0, #7    /* 8-byte alignment for ABI compliance */
    mov    sp, r0
    bl    board_init_f_alloc_reserve
    mov    sp, r0
    /* set up gd here, outside any C code */
    mov    r9, r0 //gd是一个保存在ARM的r9寄存器中的gd_t结构体的指针
    bl    board_init_f_init_reserve

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_EARLY_BSS)
    CLEAR_BSS
#endif

    mov    r0, #0
    bl    board_init_f

#if ! defined(CONFIG_SPL_BUILD)

/*
* Set up intermediate environment (new sp and gd) and call
* relocate_code(addr_moni). Trick here is that we'll return
* 'here' but relocated.
*/

    ldr    r0, [r9, #GD_START_ADDR_SP]    /* sp = gd->start_addr_sp */
    bic    r0, r0, #7    /* 8-byte alignment for ABI compliance */
    mov    sp, r0
    ldr    r9, [r9, #GD_NEW_GD]        /* r9 <- gd->new_gd */

    adr    lr, here
    ldr    r0, [r9, #GD_RELOC_OFF]        /* r0 = gd->reloc_off */
    add    lr, lr, r0
#if defined(CONFIG_CPU_V7M)
    orr    lr, #1                /* As required by Thumb-only */
#endif
    ldr    r0, [r9, #GD_RELOCADDR]        /* r0 = gd->relocaddr */
    b    relocate_code
here:
/*
* now relocate vectors
*/

    bl    relocate_vectors

/* Set up final (full) environment */

    bl    c_runtime_cpu_setup    /* we still call old routine here */
#endif
#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)

#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_SPL_EARLY_BSS)
    CLEAR_BSS
#endif

# ifdef CONFIG_SPL_BUILD
    /* Use a DRAM stack for the rest of SPL, if requested */
    bl    spl_relocate_stack_gd
    cmp    r0, #0
    movne    sp, r0
    movne    r9, r0
# endif

#if ! defined(CONFIG_SPL_BUILD)
    bl coloured_LED_init
    bl red_led_on
#endif
    /* call board_init_r(gd_t *id, ulong dest_addr) */
    mov     r0, r9                  /* gd_t */
    ldr    r1, [r9, #GD_RELOCADDR]    /* dest_addr */
    /* call board_init_r */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
    ldr    lr, =board_init_r    /* this is auto-relocated! */
    bx    lr
#else
    ldr    pc, =board_init_r    /* this is auto-relocated! */
#endif
    /* we should not return here. */
#endif

ENDPROC(_main)

2,global data

2.1 为什么使用global data

u-boot是一个bootloader,有些情况下,它可能位于系统的只读存储器(ROM或者flash)中,并从那里开始执行。

因此,这种情况下,在u-boot执行的前期(在将自己copy到可读写的存储器之前),它所在的存储空间,是不可写的,这会有两个问题:

    1)堆栈无法使用,无法执行函数调用,也即C环境不可用。

    2)没有data段(或者正确初始化的data段)可用,不同函数或者代码之间,无法通过全局变量的形式共享数据。

对于问题1,通常的解决方案是:

u-boot运行起来之后,在那些不需要执行任何初始化动作即可使用的、可读写的存储区域,开辟一段堆栈(stack)空间。

一般来说,大部分的平台(如很多ARM平台),都有自己的SRAM,可用作堆栈空间。如果实在不行,也有可借用CPU的data cache的方法。

对于问题2,解决方案要稍微复杂一些:

首先,对于开发者来说,在u-boot被拷贝到可读写的RAM(这个动作称作relocation)之前,永远不要使用全局变量。

其次,在relocation之前,不同模块之间,确实有通过全局变量的形式传递数据的需求。怎么办?这就是global data需要解决的事情。

2.2 GD空间分配和初始化

为了在relocation前通过全局变量的形式传递数据,u-boot设计了一个巧妙的方法:

    1)定义一个struct global_data类型的数据结构,里面保存了各色各样需要传递的数据。

    2)堆栈配置好之后,在堆栈开始的位置,为struct global_data预留空间,并将开始地址(就是一个struct global_data指针)保存在一个寄存器中,后续的传递,都是通过保存在寄存器中的指针实现。

arch/arm/lib/crt0.S

ENTRY(_main)

/** Set up initial C runtime environment and call board_init_f(0).*/

#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
   ldr       r0, =(CONFIG_SPL_STACK)
#else
   ldr       r0, =(CONFIG_SYS_INIT_SP_ADDR) //加载CONFIG_SYS_INIT_SP_ADDR到r0寄存器
#endif
   bic       r0, r0, #7      /* 8-byte alignment for ABI compliance */ 遵从ABI的8字节对齐
   mov       sp, r0 //将堆栈指针指向r0寄存器的值
   bl        board_init_f_alloc_reserve //主要作用是保留早期malloc区域,且为GD(全局数据区)留出空间,函数返回值也是r0,r0保存着预留早期malloc区域和GD后的地址
   mov       sp, r0
   /* set up gd here, outside any C code */
   mov       r9, r0 //定义一个寄存器全局变量指针,并指定其使用的寄存器是r9,类型为gd_t
   bl        board_init_f_init_reserve //该函数主要作用是将GD区域清零,返回最初malloc区域的地址

   mov       r0, #0 //清空r0,然后把参数r0传给board_init_f函数,并调用board_init_f
   bl        board_init_f
2.3 gt_t 和 bd_t 结构体

gd_t结构体几乎包含了u-boot中用到的所有全局变量, gd_t和bd_t都u-boot中两个重要的数据结构,在初始化操作很多都要靠这两个数据结构来保存或传递。 gd_t结构体如下所示:

include/asm-generic/global_data.h

typedef struct global_data {
    bd_t *bd;
    unsigned long flags;
    unsigned int baudrate;
    unsigned long cpu_clk;        /* CPU clock in Hz!        */
    unsigned long bus_clk;
    /* We cannot bracket this with CONFIG_PCI due to mpc5xxx */
    unsigned long pci_clk;
    unsigned long mem_clk;
#if defined(CONFIG_LCD) || defined(CONFIG_VIDEO)
    unsigned long fb_base;        /* Base address of framebuffer mem */
#endif
#if defined(CONFIG_POST) || defined(CONFIG_LOGBUFFER)
    unsigned long post_log_word;    /* Record POST activities */
    unsigned long post_log_res;    /* success of POST test */
    unsigned long post_init_f_time;    /* When post_init_f started */
#endif
#ifdef CONFIG_BOARD_TYPES
    unsigned long board_type;
#endif
    unsigned long have_console;    /* serial_init() was called */
#if CONFIG_IS_ENABLED(PRE_CONSOLE_BUFFER)
    unsigned long precon_buf_idx;    /* Pre-Console buffer index */
#endif
    unsigned long env_addr;        /* Address  of Environment struct */
    unsigned long env_valid;    /* Checksum of Environment valid? */

    unsigned long ram_top;        /* Top address of RAM used by U-Boot */
    unsigned long relocaddr;    /* Start address of U-Boot in RAM */
    phys_size_t ram_size;        /* RAM size */
    unsigned long mon_len;        /* monitor len */
    unsigned long irq_sp;        /* irq stack pointer */
    unsigned long start_addr_sp;    /* start_addr_stackpointer */
    unsigned long reloc_off;
    struct global_data *new_gd;    /* relocated global data */

#ifdef CONFIG_DM
    struct udevice    *dm_root;    /* Root instance for Driver Model */
    struct udevice    *dm_root_f;    /* Pre-relocation root instance */
    struct list_head uclass_root;    /* Head of core tree */
#endif
#ifdef CONFIG_TIMER
    struct udevice    *timer;        /* Timer instance for Driver Model */
#endif

    const void *fdt_blob;        /* Our device tree, NULL if none */
    void *new_fdt;            /* Relocated FDT */
    unsigned long fdt_size;        /* Space reserved for relocated FDT */
    struct jt_funcs *jt;        /* jump table */
    char env_buf[32];        /* buffer for getenv() before reloc. */
#ifdef CONFIG_TRACE
    void        *trace_buff;    /* The trace buffer */
#endif
#if defined(CONFIG_SYS_I2C)
    int        cur_i2c_bus;    /* current used i2c bus */
#endif
#ifdef CONFIG_SYS_I2C_MXC
    void *srdata[10];
#endif
    unsigned long timebase_h;
    unsigned long timebase_l;
#ifdef CONFIG_SYS_MALLOC_F_LEN
    unsigned long malloc_base;    /* base address of early malloc() */
    unsigned long malloc_limit;    /* limit address */
    unsigned long malloc_ptr;    /* current address */
#endif
#ifdef CONFIG_PCI
    struct pci_controller *hose;    /* PCI hose for early use */
    phys_addr_t pci_ram_top;    /* top of region accessible to PCI */
#endif
#ifdef CONFIG_PCI_BOOTDELAY
    int pcidelay_done;
#endif
    struct udevice *cur_serial_dev;    /* current serial device */
    struct arch_global_data arch;    /* architecture-specific data */
#ifdef CONFIG_CONSOLE_RECORD
    struct membuff console_out;    /* console output */
    struct membuff console_in;    /* console input */
#endif
#ifdef CONFIG_DM_VIDEO
    ulong video_top;        /* Top of video frame buffer area */
    ulong video_bottom;        /* Bottom of video frame buffer area */
#endif
} gd_t;

bd_t结构体如下所示:

typedef struct bd_info {
    unsigned long    bi_memstart;    /* start of DRAM memory */
    phys_size_t    bi_memsize;    /* size     of DRAM memory in bytes */
    unsigned long    bi_flashstart;    /* start of FLASH memory */
    unsigned long    bi_flashsize;    /* size     of FLASH memory */
    unsigned long    bi_flashoffset; /* reserved area for startup monitor */
    unsigned long    bi_sramstart;    /* start of SRAM memory */
    unsigned long    bi_sramsize;    /* size     of SRAM memory */
#ifdef CONFIG_AVR32
    unsigned char   bi_phy_id[4];   /* PHY address for ATAG_ETHERNET */
    unsigned long   bi_board_number;/* ATAG_BOARDINFO */
#endif
#ifdef CONFIG_ARM
    unsigned long    bi_arm_freq; /* arm frequency */
    unsigned long    bi_dsp_freq; /* dsp core frequency */
    unsigned long    bi_ddr_freq; /* ddr frequency */
#endif
#if defined(CONFIG_5xx) || defined(CONFIG_8xx) || defined(CONFIG_MPC8260) \
    || defined(CONFIG_E500) || defined(CONFIG_MPC86xx)
    unsigned long    bi_immr_base;    /* base of IMMR register */
#endif
#if defined(CONFIG_MPC5xxx) || defined(CONFIG_M68K)
    unsigned long    bi_mbar_base;    /* base of internal registers */
#endif
#if defined(CONFIG_MPC83xx)
    unsigned long    bi_immrbar;
#endif
    unsigned long    bi_bootflags;    /* boot / reboot flag (Unused) */
    unsigned long    bi_ip_addr;    /* IP Address */
    unsigned char    bi_enetaddr[6];    /* OLD: see README.enetaddr */
    unsigned short    bi_ethspeed;    /* Ethernet speed in Mbps */
    unsigned long    bi_intfreq;    /* Internal Freq, in MHz */
    unsigned long    bi_busfreq;    /* Bus Freq, in MHz */
#if defined(CONFIG_CPM2)
    unsigned long    bi_cpmfreq;    /* CPM_CLK Freq, in MHz */
    unsigned long    bi_brgfreq;    /* BRG_CLK Freq, in MHz */
    unsigned long    bi_sccfreq;    /* SCC_CLK Freq, in MHz */
    unsigned long    bi_vco;        /* VCO Out from PLL, in MHz */
#endif
#if defined(CONFIG_MPC512X)
    unsigned long    bi_ipsfreq;    /* IPS Bus Freq, in MHz */
#endif /* CONFIG_MPC512X */
#if defined(CONFIG_MPC5xxx) || defined(CONFIG_M68K)
    unsigned long    bi_ipbfreq;    /* IPB Bus Freq, in MHz */
    unsigned long    bi_pcifreq;    /* PCI Bus Freq, in MHz */
#endif
#if defined(CONFIG_EXTRA_CLOCK)
    unsigned long bi_inpfreq;    /* input Freq in MHz */
    unsigned long bi_vcofreq;    /* vco Freq in MHz */
    unsigned long bi_flbfreq;    /* Flexbus Freq in MHz */
#endif
#if defined(CONFIG_405)   || \
        defined(CONFIG_405GP) || \
        defined(CONFIG_405EP) || \
        defined(CONFIG_405EZ) || \
        defined(CONFIG_405EX) || \
        defined(CONFIG_440)
    unsigned char    bi_s_version[4];    /* Version of this structure */
    unsigned char    bi_r_version[32];    /* Version of the ROM (AMCC) */
    unsigned int    bi_procfreq;    /* CPU (Internal) Freq, in Hz */
    unsigned int    bi_plb_busfreq;    /* PLB Bus speed, in Hz */
    unsigned int    bi_pci_busfreq;    /* PCI Bus speed, in Hz */
    unsigned char    bi_pci_enetaddr[6];    /* PCI Ethernet MAC address */
#endif

#ifdef CONFIG_HAS_ETH1
    unsigned char   bi_enet1addr[6];    /* OLD: see README.enetaddr */
#endif
#ifdef CONFIG_HAS_ETH2
    unsigned char    bi_enet2addr[6];    /* OLD: see README.enetaddr */
#endif
#ifdef CONFIG_HAS_ETH3
    unsigned char   bi_enet3addr[6];    /* OLD: see README.enetaddr */
#endif
#ifdef CONFIG_HAS_ETH4
    unsigned char   bi_enet4addr[6];    /* OLD: see README.enetaddr */
#endif
#ifdef CONFIG_HAS_ETH5
    unsigned char   bi_enet5addr[6];    /* OLD: see README.enetaddr */
#endif

#if defined(CONFIG_405GP) || defined(CONFIG_405EP) || \
        defined(CONFIG_405EZ) || defined(CONFIG_440GX) || \
        defined(CONFIG_440EP) || defined(CONFIG_440GR) || \
        defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
        defined(CONFIG_460EX) || defined(CONFIG_460GT)
    unsigned int    bi_opbfreq;        /* OPB clock in Hz */
    int        bi_iic_fast[2];        /* Use fast i2c mode */
#endif
#if defined(CONFIG_4xx)
#if defined(CONFIG_440GX) || \
        defined(CONFIG_460EX) || defined(CONFIG_460GT)
    int        bi_phynum[4];           /* Determines phy mapping */
    int        bi_phymode[4];          /* Determines phy mode */
#elif defined(CONFIG_405EP) || defined(CONFIG_405EX) || defined(CONFIG_440)
    int        bi_phynum[2];           /* Determines phy mapping */
    int        bi_phymode[2];          /* Determines phy mode */
#else
    int        bi_phynum[1];           /* Determines phy mapping */
    int        bi_phymode[1];          /* Determines phy mode */
#endif
#endif /* defined(CONFIG_4xx) */
    ulong            bi_arch_number;    /* unique id for this board */
    ulong            bi_boot_params;    /* where this board expects params */
#ifdef CONFIG_NR_DRAM_BANKS
    struct {            /* RAM configuration */
        phys_addr_t start;
        phys_size_t size;
    } bi_dram[CONFIG_NR_DRAM_BANKS];
#endif /* CONFIG_NR_DRAM_BANKS */
} bd_t;

3,前置的板级初始化操作

3.1 board_init_f

global data准备好之后,u-boot会执行前置的板级初始化动作,即board_init_f。所谓的前置的初始化动作,主要是relocation之前的初始化操作,也就是说:执行board_init_f的时候,u-boot很有可能还在只读的存储器中。

common/board_f.c

void board_init_f(ulong boot_flags)
{
   gd->flags = boot_flags;
   gd->have_console = 0;
   if (initcall_run_list(init_sequence_f))
      hang();

#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \      !defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64) && \      !defined(CONFIG_ARC)
   /* NOTREACHED - jump_to_copy() does not return */
   hang();
#endif
}

对global data进行简单的初始化之后,调用位于init_sequence_f数组中的各种初始化API,进行各式各样的初始化动作。后面将会简单介绍一些和ARM平台有关的、和平台的移植工作有关的、比较重要的API。其它API,大家可以参考source code自行理解。

3.2 init_sequence_f

common/board_f.c

static const init_fnc_t init_sequence_f[] = {
    setup_mon_len,          /* 设置gd->mon_len为编译出来的u-boot.bin+bss段的大小 */
    fdtdec_setup,           /* 和设备树有关 */
    initf_malloc,           /* 初始化并设置内存池 */
    log_init,               /* log初始化 */
    initf_bootstage,        /* 用于记录board_init_f()的引导阶段 */
    setup_spl_handoff,
    initf_console_record,   /* 平台信息记录初始化 */
    arch_cpu_init,                  /* 空函数 */
    mach_cpu_init,                  /* 空函数 */
    initf_dm,               /* 驱动模型初始化 */
    arch_cpu_init_dm,       /* 空函数 */
    board_early_init_f,     /* 设置时钟和GPIO */
    timer_init,                        /* 定时器初始化 */
    env_init,                  /* 找到最适合存放环境变量的地址,并初始化 */
    init_baud_rate,              /* 波特率初始化 */
    serial_init,                    /* 串口初始化 */
    console_init_f,              /* 使能在重定位之前用的串口功能 gd->have_console = 1 */
    display_options,        /* 显示banner,如u-boot版本、编译时间等信息 */
    display_text_info,           /* 显示调试信息 */
    print_cpuinfo,                  /* 显示cpu信息,如cpu速度 */
    show_board_info,        /* 显示板子信息 */
    announce_dram_init,     /* 准备显示DRAM大小,在u-boot启动时可以看到DRAM大小的信息 */
    dram_init,                         /* DRAM初始化,对于本imx6ull设置dg->ram_size = 512 MiB */
    setup_dest_addr,        /* 设置重定位地址,gd->relocaddr = gd->ram_top */
    reserve_round_4k,       /* 4字节对齐,将内存指针调到下一个4 kB */
    reserve_mmu,            /* 为mmu区域腾出空间 */
    reserve_video,          /* 预留video显示内存 */
    reserve_trace,
    reserve_uboot,          /* 预留U-Boot代码、data和bss区  */
    reserve_malloc,         /* 预留malloc区 */
    reserve_board,          /* 预留存放板子信息区 */
    setup_machine,          /* 板子ID,这里没有用到 */
    reserve_global_data,    /* 预留GD区域,栈gd->start_addr_sp指向gd段基地址*/
    reserve_fdt,            /* 预留设备树区域 */
    reserve_bootstage,
    reserve_bloblist,
    reserve_arch,           /* 架构相关预留区 */
    reserve_stacks,         /* 预留栈区,gd->start_addr_sp指向栈底基地址 */
    dram_init_banksize,     /* DRAM的大小初始化 */
    show_dram_config,       /* 显示DRAM的配置 */
    display_new_sp,         /* 显示新的栈地址 */
    reloc_fdt,              /* 和设备树有关 */
    reloc_bootstage,        /* 和u-boot阶段有关 */
    reloc_bloblist,         /* 和blob列表有关 */
    setup_reloc,            /* 重定位 */
    NULL,
};
3.3 env_init

common/env_mmc.c

int env_init(void)
{
    /* use default */
    gd->env_addr    = (ulong)&default_environment[0]; //default_environment[]数组存放着默认的环境变量,将默认环境变量default_environment的地址赋值给全局变量gd->env_addr
    gd->env_valid    = 1;

    return 0;
}

include/env_default.h

static char default_environment[] = {
#else
const uchar default_environment[] = {
#endif
#ifdef    CONFIG_ENV_CALLBACK_LIST_DEFAULT
    ENV_CALLBACK_VAR "=" CONFIG_ENV_CALLBACK_LIST_DEFAULT "\0"
#endif
#ifdef    CONFIG_ENV_FLAGS_LIST_DEFAULT
    ENV_FLAGS_VAR "=" CONFIG_ENV_FLAGS_LIST_DEFAULT "\0"
#endif
#ifdef    CONFIG_BOOTARGS
    "bootargs="    CONFIG_BOOTARGS            "\0"
#endif
#ifdef    CONFIG_BOOTCOMMAND
    "bootcmd="    CONFIG_BOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_RAMBOOTCOMMAND
    "ramboot="    CONFIG_RAMBOOTCOMMAND        "\0"
#endif
#ifdef    CONFIG_NFSBOOTCOMMAND
    "nfsboot="    CONFIG_NFSBOOTCOMMAND        "\0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
    "bootdelay="    __stringify(CONFIG_BOOTDELAY)    "\0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
    "baudrate="    __stringify(CONFIG_BAUDRATE)    "\0"
#endif
#ifdef    CONFIG_LOADS_ECHO
    "loads_echo="    __stringify(CONFIG_LOADS_ECHO)    "\0"
#endif
#ifdef    CONFIG_ETHPRIME
    "ethprime="    CONFIG_ETHPRIME            "\0"
#endif
#ifdef    CONFIG_IPADDR
    "ipaddr="    __stringify(CONFIG_IPADDR)    "\0"
#endif
#ifdef    CONFIG_SERVERIP
    "serverip="    __stringify(CONFIG_SERVERIP)    "\0"
#endif
#ifdef    CONFIG_SYS_AUTOLOAD
    "autoload="    CONFIG_SYS_AUTOLOAD        "\0"
#endif
#ifdef    CONFIG_PREBOOT
    "preboot="    CONFIG_PREBOOT            "\0"
#endif
#ifdef    CONFIG_ROOTPATH
    "rootpath="    CONFIG_ROOTPATH            "\0"
#endif
#ifdef    CONFIG_GATEWAYIP
    "gatewayip="    __stringify(CONFIG_GATEWAYIP)    "\0"
#endif
#ifdef    CONFIG_NETMASK
    "netmask="    __stringify(CONFIG_NETMASK)    "\0"
#endif
#ifdef    CONFIG_HOSTNAME
    "hostname="    __stringify(CONFIG_HOSTNAME)    "\0"
#endif
#ifdef    CONFIG_BOOTFILE
    "bootfile="    CONFIG_BOOTFILE            "\0"
#endif
#ifdef    CONFIG_LOADADDR
    "loadaddr="    __stringify(CONFIG_LOADADDR)    "\0"
#endif
#ifdef    CONFIG_CLOCKS_IN_MHZ
    "clocks_in_mhz=1\0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
    "pcidelay="    __stringify(CONFIG_PCI_BOOTDELAY)"\0"
#endif
#ifdef    CONFIG_ENV_VARS_UBOOT_CONFIG
    "arch="        CONFIG_SYS_ARCH            "\0"
#ifdef CONFIG_SYS_CPU
    "cpu="        CONFIG_SYS_CPU            "\0"
#endif
#ifdef CONFIG_SYS_BOARD
    "board="    CONFIG_SYS_BOARD        "\0"
    "board_name="    CONFIG_SYS_BOARD        "\0"
#endif
#ifdef CONFIG_SYS_VENDOR
    "vendor="    CONFIG_SYS_VENDOR        "\0"
#endif
#ifdef CONFIG_SYS_SOC
    "soc="        CONFIG_SYS_SOC            "\0"
#endif
#endif
#ifdef    CONFIG_EXTRA_ENV_SETTINGS
    CONFIG_EXTRA_ENV_SETTINGS
#endif
    "\0"
#ifdef DEFAULT_ENV_INSTANCE_EMBEDDED
    }
#endif
};

可以根据宏定义去配置默认的环境变量,如bootargs、bootcmd、bootdelay等。

例如修改Uboot-2017.03/configs/mx6ull_14x14_evk_defconfig 中的CONFIG_BOOTDELAY,便可以设置默认的u-boot延时时间。

3.4 dram_init
#if defined(CONFIG_ARM) || defined(CONFIG_X86) || defined(CONFIG_NDS32) || \
        defined(CONFIG_MICROBLAZE) || defined(CONFIG_AVR32) || \
        defined(CONFIG_SH)
    dram_init,        /* configure available RAM banks */
#endif

调用dram_init接口,初始化系统的DDR。dram_init应该由平台相关的代码实现。

如果DDR在SPL中已经初始化过了,则不需要重新初始化,只需要把DDR信息保存在global data中即可,例如:

gd->ram_size = …
3.5 DRAM空间的分配

DRAM初始化完成后,就可以着手规划u-boot需要使用的部分,如下图:

总结如下:

1)考虑到后续的kernel是在RAM的低端位置解压缩并执行的,为了避免麻烦,u-boot将使用DRAM的顶端地址,即gd->ram_top所代表的位置。其中gd->ram_top是由setup_dest_addr函数配置的。

2)u-boot所使用的DRAM,主要分为三类:各种特殊功能所需的空间,如log buffer、MMU page table、LCD fb buffer、trace buffer、等等;u-boot的代码段、数据段、BSS段所占用的空间(就是u-boot relocate之后的执行空间),由gd->relocaddr标示;堆栈空间,从gd->start_addr_sp处递减。

3)特殊功能以及u-boot所需空间,是由reserve_xxx系列函数保留的,具体可参考source code,这里不再详细分析。

4)reserve空间分配完毕后,堆栈紧随其后,递减即可。

3.6 reloc_fdt

reloc_fdt函数负责将设备树数据搬运到新分配的new_fdt地址中去,如下所示:

static int reloc_fdt(void)
{
#ifndef CONFIG_OF_EMBED    /* 有定义 */
   if (gd->flags & GD_FLG_SKIP_RELOC)
      return 0;
   if (gd->new_fdt) {
      memcpy(gd->new_fdt, gd->fdt_blob, gd->fdt_size); //将老的设备树段拷贝到新的设备树段
      gd->fdt_blob = gd->new_fdt; //将老的设备树地址更新为新的设备树地址
   }
#endif

   return 0;
}
3.7 setup_reloc

计算relocation有关的信息,主要是 gd->reloc_off,计算公式如下:

gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;

其中CONFIG_SYS_TEXT_BASE是u-boot relocation之前在(只读)memory的位置(也是编译时指定的位置),gd->relocaddr是relocation之后的位置,因此gd->reloc_off代表u-boot relocation操作之后的偏移量,后面relocation时会用到。

同时,该函数顺便把global data拷贝到了上图所示的“new global data”处,其实就是global data的relocation。

memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));

4,u-boot的relocation

relocate_code函数会传入一个参数,该参数为gd->relocaddr,也就是uboot重定位的目的地址

接下来,对relocate_code函数进行分析,该函数用于重定位uboot,函数的定义在下面的汇编文件中:

uboot/arch/arm/lib/relocate.S

/*
* void relocate_code(addr_moni)
*
* This function relocates the monitor code.
*
* NOTE:
* To prevent the code below from containing references with an R_ARM_ABS32
* relocation record type, we never refer to linker-defined symbols directly.
* Instead, we declare literals which contain their relative location with
* respect to relocate_code, and at run time, add relocate_code back to them.
*/

ENTRY(relocate_code)
    ldr    r1, =__image_copy_start/* r1 <- SRC &__image_copy_start */ //r1保存源image开始地址
    //r0 = gd->relocaddr,r4 = r0 - r1 = 0x8ff3b000 - 0x87800000 = 0x873b000
    subs    r4, r0, r1 /* r4 <- relocation offset */ //r4 = gd->reloc_off,保存偏移地址
    beq    relocate_done    /* skip relocation */ //如果r0和r1相等,则不需要uboot重定位
    ldr    r2, =__image_copy_end  /* r2 <- SRC &__image_copy_end */  //r2保存源image结束地址

copy_loop:
    ldmia    r1!, {r10-r11}  /* copy from source address [r1] */ //拷贝uboot代码到r10和r11
    stmia    r0!, {r10-r11}  /* copy to  target address [r0] */ //将uboot代码写到目的地址
    cmp    r1, r2     /* until source end address [r2] */ //判断uboot是否拷贝完成
    blo    copy_loop  //循环拷贝

    /*
     * fix .rel.dyn relocations //修复.rel.dyn段
     */
    ldr    r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
    ldr    r3, =__rel_dyn_end    /* r3 <- SRC &__rel_dyn_end */
fixloop:
    ldmia    r2!, {r0-r1}        /* (r0,r1) <- (SRC location,fixup) */
    and    r1, r1, #0xff
    cmp    r1, #23            /* relative fixup? */
    bne    fixnext

    /* relative fix: increase location by offset */
    add    r0, r0, r4
    ldr    r1, [r0]
    add    r1, r1, r4
    str    r1, [r0]
fixnext:
    cmp    r2, r3
    blo    fixloop

relocate_done:

ENDPROC(relocate_code)

函数传入的参数为r0 = 0x8ff3b000,uboot重定位的目的地址,函数进来后,将__image_copy_start的数值赋值给r1,也就是uboot复制开始地址,从表格可以知道r1 = 0x87800000,接下来进行一个减法操作,此时r4将保存着uboot的偏移量,也就是r4 = gd->reloc_off。

接下来会进行一个判断,判断uboot重定位的目的地址是否和uboot源地址是否相等,如果相等的话,表示不需要重定位,跳到relocate_done处,继续运行,如果不相等的话,则是需要进行重定位。

将__image_copy_end的值赋值给r2,也就是uboot复制结束的地址,从表格中可以知道,此时r2 = 0x87868960,接下来,开始进行uboot代码的拷贝,进入到copy_loop循环,从r1,也就是__image_copy_start开始,读取uboot代码到r10和r11寄存器,一次就拷贝两个32位的数据,r1自动更新,保存下一个需要拷贝的地址,然后将r10和r11里面的数据,写到r0保存的地址,也就是uboot重定位的目的地址,r0自动更新。

接下来,比较r1和r2是否相等,也就是判断uboot代码是否拷贝完成,如果没完成的话,跳转到copy_loop处,继续循环,直到uboot拷贝完成。

函数relocate_code()的第二部分是修复.rel.dyn的定位问题,.rel.dyn段存放了.text段中需要重定位地址(也就是搬移了__image_copy_start~__image_copy_end的代码后,需要对其中存的绝对地址进行更新,即绝对地址+偏移地址)的集合,uboot重定位就是uboot将自身拷贝到DRAM的另外一个地址继续运行(DRAM的高地址),一个可执行的bin文件,它的链接地址和运行地址一定要相等,也就是链接到哪个地址,运行之前就需要拷贝到哪个地址中进行运行,在前面的重定位后,运行地址和链接地址就不一样了,这样在进行寻址就会出问题,对.rel.dyn的定位问题进行修复,就是为了解决该问题。

下面,使用一个简单的例子进行分析:

先在新适配的板级文件中添加测试代码,文件如下:

uboot/board/freescale/mx6ul_comp6ul_nand/mx6ul_comp6ul_nand.c

 添加的内容如下:

static int rel_a = 0;

void rel_test(void)
{
    rel_a = 100;
    printf("rel_test\n");
}

/* 在board_init()函数中调用rel_test()函数 */
int board_init(void)
{
        ...
        ...
        rel_test();
        
        return 0;
}

接下来对uboot源码重新编译,并将u-boot文件进行反汇编分析,使用命令如下:

$ cd uboot

$ make

$ arm-linux-gnueabihf-objdump -D -m arm u-boot > u-boot.dis

 然后,打开u-boot.dis文件,并在文件中,找到rel_a变量、rel_test函数和board_init函数相关的汇编代码,如下所示:

/* rel_test寻址分析 */
8780424c <rel_test>:
8780424c:    e59f300c     ldr    r3, [pc, #12]    ; 87804260 <rel_test+0x14>
87804250:    e3a02064     mov    r2, #100    ; 0x64
87804254:    e59f0008     ldr    r0, [pc, #8]    ; 87804264 <rel_test+0x18>
87804258:    e5832000     str    r2, [r3]
8780425c:    ea00f1a9     b    87840908 <printf>
87804260:     87868994             ; <UNDEFINED> instruction: 0x87868994
87804264:    878494ce     strhi    r9, [r4, lr, asr #9]
...
...
878043e4:    e59f2038     ldr    r2, [pc, #56]    ; 87804424 <board_init+0x1bc>
878043e8:    e5923068     ldr    r3, [r2, #104]    ; 0x68
878043ec:    e3833030     orr    r3, r3, #48    ; 0x30
878043f0:    e5823068     str    r3, [r2, #104]    ; 0x68
878043f4:    ebffff94     bl    8780424c <rel_test>
...
...
87868994 <rel_a>:
87868994:    00000000     andeq    r0, r0, r0

 

在0x878043f4处,也就是board_init()函数中调用了rel_test()函数,在汇编代码中,可以看到是使用了bl指令,而bl指令是相对寻址的(pc + offset),因此,可以知道,uboot中的函数调用时与绝对地址无关。

接下来,分析rel_test()函数对全局变量rel_a的调用过程,在0x8780424c处开始,先设置r3的值为pc + 12地址处的值,由于ARM流水线的原因,当前pc的值为当前的地址加8,所以pc = 0x8780424c + 8 = 0x87804254,因此r3 = pc + 12 = ‬0x87804254 + 12 = 0x87804260,从反汇编的代码中可以看到0x87804260处的值为0x87868994,所以,最终r3的值为0x87868994,而且从反汇编代码中可以看到0x87868994就是变量rel_a的地址,rel_test()函数继续执行,将100赋值到r2寄存器,然后通过str指令,将r2的值写到r3的地址处,也就是将0x87868994地址处的值赋值100,就是函数中调用的rel_a = 100;语句。

从上面的分析,可以知道rel_a = 100;的执行过程为:

  • 在函数rel_test()的末尾处有一个地址为0x87804260的内存空间,此内存空间中保存着变量rel_a的地址;

  • 函数rel_test()想要访问变量rel_a,首先需要访问0x87804260内存空间获取变量rel_a的地址,而访问0x87804260是通过偏移来访问的,与位置无关的操作;

  • 通过访问0x87804260获取变量rel_a的地址,然后对变量rel_a进行赋值操作;

  • 函数rel_test()对变量rel_a的访问并没有直接进行,而是通过一个偏移地址0x87804260,找到变量rel_a真正的地址,然后才对其操作,偏移地址的专业术语为Label。

 从分析中,可以知道,该偏移地址就是uboot重定位运行是否会出错的原因,在上面可以知道,uboot重定位的偏移量为0x8ff3b000,将上面给出的反汇编代码进行重定位后,也就是加上地址偏移量0x873b000后,如下:

/* rel_test寻址分析(重定位后) */
8ff3f24c‬ <rel_test>:
8ff3f24c:    e59f300c     ldr    r3, [pc, #12]
8ff3f250:    e3a02064     mov    r2, #100    ; 0x64
8ff3f254:    e59f0008     ldr    r0, [pc, #8]    
8ff3f258:    e5832000     str    r2, [r3]
8ff3f25c:    ea00f1a9     b    87840908 <printf>
8ff3f260:     87868994             ; <UNDEFINED> instruction: 0x87868994
8ff3f264:    878494ce     strhi    r9, [r4, lr, asr #9]
...
...
8ff3f3e4:    e59f2038     ldr    r2, [pc, #56]    
8ff3f3e8:    e5923068     ldr    r3, [r2, #104]    ; 0x68
8ff3f3ec:    e3833030     orr    r3, r3, #48    ; 0x30
8ff3f3f0:    e5823068     str    r3, [r2, #104]    ; 0x68
8ff3f3f4:    ebffff94     bl    8780424c <rel_test>
...
...
8ffa3994 <rel_a>:
8ffa3994:    00000000     andeq    r0, r0, r0

 

函数rel_test()假设调用后对rel_a变量进行访问,分析和上面一样,先通过pc和偏移量找到0x8ff3f260,该地址是重定位后的地址,可此时该地址的值为0x87868994,也就是没有重定位后的rel_a变量的地址,但是从上面可以知道,uboot重定位后,rel_a变量的新地址为0x8ffa3994,因此,我们可以知道,如果uboot重定位后,要想正常访问rel_a变量,必须要将0x8ff3f260地址中的值0x87868994加上偏移量0x873b000,才能保证uboot重定位后,能正常访问到rel_a变量,将.rel.dyn段进行重定位修复,就是为了解决链接地址和运行地址不一致的问题。

在uboot中,对于重定位后链接地址与运行地址不一致的解决办法就是使用位置无关码,在uboot编译使用ld链接的时候使用参数"-pie"可生成与位置无关的可执行程序,使用该参数后,会生成一个.rel.dyn段,uboot则是靠该段去修复重定位后产生的问题的,在uboot的反汇编文件中,有.rel.dyn段代码,如下:

Disassembly of section .rel.dyn:

87868988 <__rel_dyn_end-0x91a0>:

87868988:    87800020     strhi    r0, [r0, r0, lsr #32]

8786898c:    00000017     andeq    r0, r0, r7, lsl r0

87868990:    87800024     strhi    r0, [r0, r4, lsr #32]

87868994:    00000017     andeq    r0, r0, r7, lsl r0

...

...

87868f08:    87804260     strhi    r4, [r0, r0, ror #4]

87868f0c:    00000017     andeq    r0, r0, r7, lsl r0

87868f10:    87804264     strhi    r4, [r0, r4, ror #4]

87868f14:    00000017     andeq    r0, r0, r7, lsl r0

...

...

rel.dyn段的格式如下,类似下面的两行就是一组:

 87868f08:    87804260     strhi    r4, [r0, r0, ror #4]

87868f0c:    00000017     andeq    r0, r0, r7, lsl r0

 

也就是两个4字节数据为一组,其中高4字节是Label地址的标识0x17,低4字节就是Label的地址,首先会判断Label地址标识是否正确,也就是判断高4字节是否为0x17,如果是的话,低4字节就是Label地址,在上面给出的两行代码中就是存放rel_a变量地址的Label,0x87868f0c的值为0x17,说明低4字节是Label地址,也就是0x87804260,需要将0x87804260 + offset处的值改为重定位后的变量rel_a地址。

在对.rel.dyn段以及Label的相关概念有一定的了解后,接下来,我们分析函数relocate_code()的第二部分代码,修复.rel.dyn段重定位问题,代码如下:

/*
     * fix .rel.dyn relocations //修复.rel.dyn段
     */
    ldr    r2, =__rel_dyn_start    /* r2 <- SRC &__rel_dyn_start */
    ldr    r3, =__rel_dyn_end    /* r3 <- SRC &__rel_dyn_end */
fixloop:
    ldmia    r2!, {r0-r1}        /* (r0,r1) <- (SRC location,fixup) */
    and    r1, r1, #0xff
    cmp    r1, #23            /* relative fixup? */
    bne    fixnext

    /* relative fix: increase location by offset */
    add    r0, r0, r4
    ldr    r1, [r0]
    add    r1, r1, r4
    str    r1, [r0]
fixnext:
    cmp    r2, r3
    blo    fixloop

relocate_code()函数第二部分代码调用后,将__rel_dyn_start的值赋给r2,也就是r2中保存着.rel.dyn段的起始地址,然后将__rel_dyn_end的值赋给r3,也就是r3中保存着.rel.dyn段的结束地址。

接下来,进入到fixloop处执行,使用ldmia指令,从.rel.dyn段起始地址开始,每次读取两个4字节数据存放到r0和r1寄存器中,其中r0存放低4字节的数据,也就是Label的地址,r1存放高4字节的数据,也就是Label的标识,然后将r1的值与0xff相与,取r1值的低8位,并将结果保存到r1中,接下来,判断r1中的值是否等于23(0x17),如果r1不等于23的话,也就说明不是描述Label,跳到fixnext处循环执行,直到r2和r3相等,也就是遍历完.rel.dyn段。

如果r1的值等于23(0x17)的话,继续执行,r0保存着Label地址,r4保存着uboot重定位的偏移,因此,r0 + r4就得到了重定位后的Label地址,也就是上面分析的0x87804260 + 0x873b000 = 0x8ff3f260 = r0,此时r0已经保存着重定位后的Label地址,然后使用ldr指令,读取r0中保存的值到r1中,也就是Label地址所保存的rel_a变量的地址,但是此时,该rel_a变量的地址仍然是重定位之前的地址0x87868994,所以,此时r1 = 0x87868994,接下来,使用add指令,将r1中的值加上r4,也就是加上uboot重定位偏移量,所以,此时r1 = 0x87868994 + 0x873b000 = 0x8ffa3994,这不就是前面分析的uboot重定位后的rel_a变量的地址吗?接下来使用str指令,将r1中的值写入到r0保存的地址中,也就是将Label地址中的值设置为0x8ffa3994,就是重定位后rel_a变量的新的地址。

比较r2和r3的值,查看.rel.dyn段重定位修复是否完成,循环直到完成,才能执行完relocate_code()函数。

第二部分执行完成后,就解决了.rel.dyn段的重定位问题,从而解决了uboot重定位后,链接地址和运行地址不一致的问题。

 

二,u-boot启动第二阶段

relocate完成之后,真正的C运行环境才算建立了起来,接下来会执行“后置的板级初始化操作”,即board_init_r函数。board_init_r和board_init_f的设计思路基本一样,也有一个很长的初始化序列----init_sequence_r,该序列中包含如下的初始化函数:

common/board_r.c

1)initr_trace,初始化并使能u-boot的tracing system,涉及的配置项有CONFIG_TRACE。
2)initr_reloc,设置relocation完成的标志。
3)initr_caches,使能dcache、icache等,涉及的配置项有CONFIG_ARM。
4)initr_malloc,malloc有关的初始化。
5)initr_dm,relocate之后,重新初始化DM,涉及的配置项有CONFIG_DM。
6)board_init,具体的板级初始化,需要由board代码根据需要实现,涉及的配置项有CONFIG_ARM。
7)set_cpu_clk_info,Initialize clock framework,涉及的配置项有CONFIG_CLOCKS。
8)initr_serial,重新初始化串口(不太明白什么意思)。
9)initr_announce,宣布已经在RAM中执行,会打印relocate后的地址。
10)board_early_init_r,由板级代码实现,涉及的配置项有CONFIG_BOARD_EARLY_INIT_R。
11)arch_early_init_r,由arch代码实现,涉及的配置项有CONFIG_ARCH_EARLY_INIT_R。
12)power_init_board,板级的power init代码,由板级代码实现,例如hold住power。
13)initr_flash、initr_nand、initr_onenand、initr_mmc、initr_dataflash,各种flash设备的初始化。
14)initr_env,环境变量有关的初始化。
15)initr_secondary_cpu,初始化其它的CPU core。
16)stdio_add_devices,各种输入输出设备的初始化,如LCD driver等。
17)interrupt_init,中断有关的初始化。
18)initr_enable_interrupts,使能系统的中断,涉及的配置项有CONFIG_ARM(ARM平台u-boot实在开中断的情况下运行的)。
19)initr_status_led,状态指示LED的初始化,涉及的配置项有CONFIG_STATUS_LED、STATUS_LED_BOOT。
20)initr_ethaddr,Ethernet的初始化,涉及的配置项有CONFIG_CMD_NET。
21)board_late_init,由板级代码实现,涉及的配置项有CONFIG_BOARD_LATE_INIT。
22)等等…
23)run_main_loop/main_loop,执行到main_loop,开始命令行操作。

三,u-boot加载内核阶段

... ... 

参考链接:

27. Uboot启动流程分析——下 — [野火]嵌入式Linux镜像构建与部署——基于LubanCat-i.MX6ULL开发板 文档

u-boot启动流程分析(2)_板级(board)部分

https://www.cnblogs.com/god-of-death/p/16971688.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1820974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

扩展方块加载动画

效果图: 完整代码: <!DOCTYPE html> <html> <head><meta charset="UTF-8" /><title>扩展方块加载动画</title><style type="text/css">body {background: #ECF0F1;display: flex;justify-content: center;al…

【并集查找】839. 相似字符串组

本文涉及知识点 并集查找&#xff08;并差集) 图论知识汇总 LeetCode839. 相似字符串组 如果交换字符串 X 中的两个不同位置的字母&#xff0c;使得它和字符串 Y 相等&#xff0c;那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的&#xff0c;那它们也是相似的。…

车载网络安全指南 系统层面开发阶段(六)

返回总目录->返回总目录<- 目录 前言 一、统层面产品开发启动 二、系统层面漏洞分析 三、网络安全策略具体化 四、确定网络安全技术需求 五、系统设计 六、系统集成与测试 七、网络安全验证 八、系统层面网络安全评估 九、系统层面产品开发阶段检查 十、产品发…

在vue中循环中调用接口-promise.all();按顺序执行异步处理

&#x1f308;&#x1f308;&#x1f308;目录 场景一 解决 场景二 解决 场景一 数组遍历中每次遍历都需要去请求getStaffCover接口&#xff0c;拿到该接口的结果拼接到数组的每一项&#xff0c;等到数组遍历完之后&#xff0c;拿到拼接好的数组。拼接的数组必须是最终遍历…

自动控制理论---零点和极点、单位脉冲响应

1、实验设备 PC计算机1台&#xff0c;MATLAB软件1套。 2、实验目的 研究四个具有相同极点分布但不同零点分布的二阶系统对单位脉冲响应的影响。绘制各系统的零点和极点分布图。计算并绘制各系统的单位脉冲响应波形。分析零点分布对单位脉冲响应的影响。 3、实验原理说明&am…

BC35 判断字母

BC35 判断字母 废话不多说&#xff0c;直接上题 解析答案&#xff1a; #include<stdio.h> int main() {int ch;while ((ch getchar()) ! EOF)if ((A < ch && Z > ch) || (a < ch && z > ch)){printf("YES");}else{printf("…

短剧APP小程序开发之小程序内存管理挑战:短剧缓存与释放策略探讨(第二篇)

在上一篇帖子中&#xff0c;我们探讨了小程序内存管理的限制以及缓存策略的设计。本篇将进一步探讨释放策略的具体实现以及优化方案&#xff0c;以支持大量短剧内容的加载和播放。 释放策略的具体实现 监听内存警告&#xff1a;小程序提供了监听内存警告的API&#xff0c;开发…

go context 源码刨析(一)

Context 上下文context.Context 是用来设置截止时间、同步信号&#xff0c;传递请求相关值的结构体。 context.Context 定义了四个需要实现的方法&#xff1a; Deadline: 返回 context.Context 被取消的时间。Done: 返回一个 Channel&#xff0c;这个 Channel 会在当前工作完…

【OpenVINO™】使用 OpenVINO™ C++ 异步推理接口部署YOLOv8 ——在Intel IGPU 上实现80+FPS视频推理

​ OpenVINO Runtime支持同步或异步模式下的推理。Async API的主要优点是&#xff0c;当设备忙于推理时&#xff0c;应用程序可以并行执行其他任务&#xff08;例如&#xff0c;填充输入或调度其他请求&#xff09;&#xff0c;而不是等待当前推理首先完成。 当我们使用异步API…

使用pnpm创建vue3项目

https://pnpm.io/zh/ 全局安装&#xff1a; npm install -g pnpm 检查版本&#xff1a; pnpm -v 创建vue3项目&#xff1a; pnpm create vuelatest 项目装包&#xff1a; pnpm install 运行项目&#xff1a; pnpm dev 命令行&#xff1a; https://pnpm.io/zh/pnpm-cli pnpm …

django学习入门系列之第二点《浏览器能识别的标签3》

文章目录 列表表格往期回顾 列表 无序列表 <!-- <ul </ul> 无序列表 --> <ul><li> 内容1 </li><li> 内容2 </li><li> 内容3 </li><li> 内容4 </li> </ul>有序列表 <!-- <ol> &…

Python | Leetcode Python题解之第150题逆波兰表达式求值

题目&#xff1a; 题解&#xff1a; class Solution:def evalRPN(self, tokens: List[str]) -> int:op_to_binary_fn {"": add,"-": sub,"*": mul,"/": lambda x, y: int(x / y), # 需要注意 python 中负数除法的表现与题目不一…

【车载音视频电脑】嵌入式AI分析车载DVR,支持8路1080P

产品特点 采用H.265 & H.264编解码&#xff0c;节约存储空间、传输流量&#xff1b; 高分辨率&#xff1a;支持8路1080P*15FPS/4路1080P*30FPS、720P、D1等编解码&#xff1b; 支持1张SATA硬盘&#xff0c;取用方便&#xff0c;满足大容量存储要求&#xff1b; 支持1个…

对抗攻击论文阅读—AAAI2022—CMUA-Watermark

文章目录 CMUA-Watermark: A Cross-Model Universal Adversarial Watermark for Combating Deepfakes背景1、什么是对抗攻击1.1 主动防御与被动防御 2、整体思路3、方法3.1 整体流程3.2 如何破坏单个面部修改模型 G G G论文中代码 3.3 对抗扰动融合3.4 基于TPE的自动步长调整 4…

【Linux文件篇】软硬链接与动静态库链接的实用指南

W...Y的主页 &#x1f60a; 代码仓库分享&#x1f495; 目录 文件的软硬链接 动静态库 回归动静态库 创建动静态库 生成静态库 生成动态库 库搜索路径 文件的软硬链接 上篇文章中我们讲述了文件系统从硬件到软件&#xff0c;理解了如何创建一个文件的具体流程&#xff…

AI数字人的开源解决方案

目前&#xff0c;国内外已经涌现出一些优秀的数字人开源解决方案&#xff0c;这些解决方案为开发者提供了构建数字人应用的工具和基础设施。以下是一些比较知名的数字人开源解决方案。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1…

java Springboot网上音乐商城(源码+sql+论文)

1.1 研究目的和意义 随着市场经济发展&#xff0c;尤其是我国加入WTO &#xff0c;融入经济全球化潮流&#xff0c;已进入国内外市场经济发展新时期&#xff0c;音乐与市场联系越来越紧密&#xff0c;我国音乐和网上业务也进入新历史发展阶段。为了更好地服务于市场&#xff0…

5.7 Python内置函数

文章目录 1. 内置模块Aabs()all()any()ascii() Bbin()bool()bytearra()bytes() Ccallable()chr()classmethod()compile()complex() Ddelattr()dict()dir()divmod() Eenumerate()eval()exec()execfile() Ffile()filter()float()format()frozenset() Ggetattr()globals() Hhasatt…

【分布式技术专题】「OceanBase深度解析」 探索OceanBase产品矩阵与核心设计

探索OceanBase产品矩阵与核心设计 OceanBase的六大特性高扩展高可用多租户&#xff08;资源隔离&#xff09;OceanBase架构和功能OceanBase广泛的数据源支持 OceanBase的六大特性 OceanBase以其卓越的产品平台整合方案&#xff0c;充分展现了六大核心特性的卓越与全面。这一方…

进击算法工程师深度学习课程

"进击算法工程师深度学习课程"旨在培养学员在深度学习领域的专业技能和实战经验。课程涵盖深度学习基础理论、神经网络架构、模型优化方法等内容&#xff0c;通过项目实践和算法实现&#xff0c;帮助学员掌握深度学习算法原理和应用&#xff0c;提升在算法工程师领域…