U-boot(2) : U-boot 동작

황승우·2023년 5월 20일
0

디렉토리 구조 설명

  • arch
    • 주로 아키텍처(powerpc, x86, mips, arm 등)에 의존적인 코드와 라이브러리, U-BOOT에서 제일 처음 시작하는 start.S
  • board 보드와 관련된 코드. 보통 보드 제조사가 자사의 보드에 대한 코드들을 만들어 올림.
  • commom 아키텍처에 독립적인 코드들과 각종 명령어들 존재.
  • disk 디스크드라이버와 파티션 관련 코드.
  • doc U-BOOT 관련 문서
  • drivers gpio, i2c, pci, serial, sound, usb와 같은 외부 장치의 드라이버
  • fs fat, yaffs2와 같이 U-BOOT에서 지원하는 파일 시스템 관련 코드
  • include 헤더 파일
  • lib 모든 아키텍처와 관련된 라이브러리
  • net 네트워크 관련 코드
  • post Power On Self Test
  • tools U-BOOT 이미지나 S-Record(부트로더를 직접적으로 플래시에 저장하기 위한 이미지) 이미지를 생성하기 위한 툴
  • 부록 : U-Boot의 파일이 다양한 이유
    • 부트로더의 기본 기능만 제공한다면, 어떤 종류의 부트로더를 사용해도 무방함.
    • U-boot는 가장 활발하게, 개발되고 다양하게 사용되는 공개 부트로더.
    • 임베디드 리눅스 기반의 시스템에서 많이 사용됨.
    • 즉, 각자 다른 디바이스(아키텍쳐)에서 부팅을 하기 위한 동작을 위해서는 다른 코드가 필요함.
    • Cisc vs Risc
      • CISC (Complex Instruction Set Computer)
        • 연산에 처리되는 복잡한 명령어 집합을 수백 개 이상 탑재하고 있는 프로세서
        • 인텔 계열의 모든 프로세서는 CISC 프로세서
          1. 복잡하고 기능이 많은 명령어로 구성된 프로세서
          2. 복합 명령을 가짐으로써 하위 호환성을 확보
          3. 트랜지스터 집적에 있어 효율성이 떨어짐
          4. 전력 소모가 큼
          5. 속도가 느리고 가격이 비쌈
          6. 호환성이 절대적으로 필요한 PC 환경에 사용
          7. 명령어 해석에 필요한 회로가 복잡해 병렬 처리가 쉽지 않음
      • RISC (Reduced Instruction Set Computer)
        • 적은 수의 명령어를 수행하도록 설계된 마이크로프로세서
        • 복잡한 명령어를 제거하여 사용빈도가 높은 명령어 위주로 처리속도를 향상한 프로세서
        • ARM 계열의 프로세서가 RISC 프로세서
          1. CPU의 명령어를 최소화하여 단순하게 제작된 프로세서
          2. 효율적이고 특화된 CPU 구조
          3. 하드웨어가 간단한 대신 소프트웨어가 복잡하고 크기가 커짐(컴파일러의 최적화가 요구됨)
          4. 하위 호환을 위해 에뮬레이션 방식을 채택, 호환성 부족
          5. 전력 소모가 적음
          6. 속도가 빠르고 가격이 저렴
          7. 용도에 최적화가 요구되는 환경에 사용
          8. 명령어의 길이가 같기 때문에 병렬 처리가 용이함

U-boot 빌드 과정

# 이파일에 선언된 모든 변수를 서브 메이크화일에서 사용할 수 있게한다.
.EXPORT_ALL_VARIABLES:

# 컴파일 아키텍쳐
MCU     = au1250

BOARD   = BOARD_EZ_AU1250

VERSION = 1.2.9.4

# 컴파일 대상 디렉토리
DIRS    = boot bin
TOPDIR  := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)
INCLUDES = -I$(TOPDIR)/include
INCLUDES += -I.

# 컴파일러 지정
CROSS   = mipsel-linux-

 ![](https://velog.velcdn.com/images/simodda/post/b74857b7-1054-4953-9874-a5af46ee244e/image.png)


# Compiler
CC      = $(CROSS)gcc

# Linker
LD      = $(CROSS)ld

# Object copy and translate
OC      = $(CROSS)objcopy

# 컴파일 옵션 및 Object
include ./bin/makefile_config

# 실행이미지 생성
BIN_IMAGE = ./bin/$(MCU)_booter_$(VERSION)
  1. 첫번째, 컴파일러에 의해서 소스파일을 목적파일로 컴파일한다.
    • 임베디드 보드의 아키텍쳐가 MIPS 이므로 mips 크로스 컴파일러(mipsel-linux-gcc)가 C언어 혹은 MIPS 어셈블리어로된 소스파일들을 각각의 코드(.o) 파일들로 컴파일한다.
      • 크로스 컴파일러 : 다른 플렛폼에서(windows, Mac) 다른 플렛폼의(Android, IOS, Embedded) 실행 파일을 만들어 주는 컴파일러

  2. 두번째, 링커(Linker: mipsel-linux-ld)에 의해서 각각의 코드(.o) 파일들이 하나의 파일로 연결(link)된다.
    • 링커란?
      • 여러 개의 코드와 데이터를 모아서 연결하여 메모리에 로드될 수 있고 실행될 수 있는 한 개의 파일로 만드는 작업을 담당하는 프로그램
  3. 세번째, 연결(link)된 파일을 목적코드(object)로 복사하여 변환한다.
  4. 최종적으로 파일복사 프로그램에 의해서 목적코드(object) 파일이 실행 가능한 이미지 파일(.iso)로 생성된다.
    • iso란?
      • 폴더 및 파일 계층을 포함하여 광 디스크의 동일한 내용을 포함하는 단일 전자 파일
      • ISO 파일에는 디스크에 저장된 데이터와 파일 시스템 정보가 포함
        • Ex) 디렉터리 구조, 파일 속성, 부트코드 등

U-boot 동작 과정

  • Instruction
    - 컴파일 된 소스 코드로 실행 파일 한 줄 한 줄의 명령어
    - 프로세서마다 조금씩 다른 instruction을 가짐

  • 레지스터(Register)
    • 레지스터는 CPU(Central Processing Unit)가 요청을 처리하는 데 필요한 데이터를 일시적으로 저장하는 기억장치
    • Arm Registers
      • R4 - R12 : 범용 레지스터
      • R11 : 스택 프레임 포인터
      • R0 : 함수 리턴 값 저장
      • R0 - R3 : 함수 호출 인자 전달
      • R13 - R15 : 특수 레지스터
        • R13(SP)
          • 스택 포인터: 스택의 맨 위 주소 저장
        • R14(LR)
          • 링크 레지스터 : 서브루틴 후에 돌아갈 리턴 주소 저장
        • R15(PC)
          • 프로그램 카운터 : 현재 fetch되고 있는 명령어의 주소, 즉 현재 실행되는 명령어의 다다음 주소
      • CPSR : 현재 프로그램 상태 레지스터

실제 동작 과정

    1. Stat.S

      arch\arm\cpu\armv7\start.S
      
      #if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
      	/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
      	mrc	p15, 0, r0, c1, c0, 0	@ Read CP15 SCTLR Register
      	bic	r0, #CR_V		@ V = 0
      	mcr	p15, 0, r0, c1, c0, 0	@ Write CP15 SCTLR Register
      
      #ifdef CONFIG_HAS_VBAR
      	/* Set vector address in CP15 VBAR register */
      	ldr	r0, =_start
      	mcr	p15, 0, r0, c12, c0, 0	@Set VBAR
      #endif
      #endif
      
      	/* the mask ROM code should have PLL and others stable */
      #ifndef CONFIG_SKIP_LOWLEVEL_INIT
      #ifdef CONFIG_CPU_V7A
      	bl	cpu_init_cp15
      #endif
      #ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
      	bl	cpu_init_crit
      #endif
      #endif
      
      ...
      
      ENTRY(cpu_init_crit)
      	/*
      	 * Jump to board specific initialization...
      	 * The Mask ROM will have already initialized
      	 * basic memory. Go here to bump up clock rate and handle
      	 * wake up conditions.
      	 */
      	b	lowlevel_init		@ go setup pll,mux,memory
      ENDPROC(cpu_init_crit)
    • u-boot의 entry point는 _start

    • 가장 먼저 Vector table을 세팅

      • Vector table이란?

        • 프로그램이이 동작중에 문제가 생기거나 인터럽트가 걸리게 되었을 때 Jump되는 곳이며, 프로그램에 맨 앞에 위치

          1. RESET - RESET이 걸리는 경우 (하드웨어나 소프트웨어적으로)
          2. Prefetch Abort/Data Abort - 실제로 존재하지 않는 주소를 읽거나 쓸때 발생. (Cashe에서 code나 data를 빨리 읽어오거나 Signal을 빨리 처리하기 위해 정상적인 경우에도 호출하는 경우도 있음)
          3. undefined Instruction - 이 경우 CPU가 명령어를 해석하지 못하는 경우 (CODE 영역에서 READ ONLY이지만 WRITE를 하는 경우, Device를 떨어뜨려 메모리에 금이 가는경우 발생)
          4. SWI(Software Interrupt) - 프로그램에서 의도적으로 Interrupt를 발생해서 처리하려고 할 떄 사용
          5. IRQ(Interrupt Requeset) - H/W Interrupt에 의해 호출, Interrupt Controller에서 Interrupt가 발생되며 IRQ를 호출하게 됩니다. 다음 누가 Interrupt를 호출했는지 검사
          6. FIQ(Fast Interrupt Request) - Interrupt를 더 빠르게 처리하기 위해 사용, FIQ 처리시 레지스터를 적게 백업하고 FIQ를 호출하면 JUMP를 하지 않고 처리하기 떄문에 인터럽트를 빠르게 처리
    • cpu_init_cp15를 호출한다

      • cache, MMU, TLBs init하는 동작
        • MMU(memory management unit) ⇒ CPU 코어 안에 탑재되어 있는 가상 주소를 실제 메모리 주소로 변환해주는 하드웨어 장치
        • TLB(Translation-Lookaside Buffer) ⇒ 하드웨어적으로 지원하여 page table의 임시저장 cache 역할
    • CPU_init_cirt는 lowlevel_init.S에 있는 lowlevel_init procdure를 호출

    1. lowlevel_init.S

      board\samsung\smdk5250\lowlevel_init.S
      
      lowlevel_init:
      
      	/* use iRAM stack in bl2 */
      	ldr	sp, =CONFIG_IRAM_STACK
      	stmdb	r13!, {ip,lr}
      
      	/* check reset status */
      	ldr	r0, =(EXYNOS5_POWER_BASE + INFORM1_OFFSET)
      	ldr	r1, [r0]
      
      	/* AFTR wakeup reset */
      	ldr	r2, =S5P_CHECK_DIDLE
      	cmp	r1, r2
      	beq	exit_wakeup
      
      	/* LPA wakeup reset */
      	ldr	r2, =S5P_CHECK_LPA
      	cmp	r1, r2
      	beq	exit_wakeup
      
      	/* Sleep wakeup reset */
      	ldr	r2, =S5P_CHECK_SLEEP
      	cmp	r1, r2
      	beq	wakeup_reset
      
      	/*
      	 * If U-boot is already running in RAM, no need to relocate U-Boot.
      	 * Memory controller must be configured before relocating U-Boot
      	 * in ram.
      	 */
      	ldr	r0, =0x0ffffff		/* r0 <- Mask Bits*/
      	bic	r1, pc, r0		/* pc <- current addr of code */
      					/* r1 <- unmasked bits of pc */
      	ldr	r2, _TEXT_BASE		/* r2 <- original base addr in ram */
      	bic	r2, r2, r0		/* r2 <- unmasked bits of r2*/
      	cmp	r1, r2			/* compare r1, r2 */
      	beq	1f			/* r0 == r1 then skip sdram init */
      
      	/* init system clock */
      	bl	system_clock_init
      
      	/* Memory initialize */
      	bl	mem_ctrl_init
      
      1:
      	bl	arch_cpu_init
      	bl	tzpc_init
      	ldmia	r13!, {ip,pc}
      
      wakeup_reset:
      	bl	system_clock_init
      	bl	mem_ctrl_init
      	bl	arch_cpu_init
      	bl	tzpc_init
    • mem, system_clock 등 초기화
    1. board_init_f

      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	x0, =(CONFIG_TPL_STACK)
      #elif defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK)
      	ldr	x0, =(CONFIG_SPL_STACK)
      #elif defined(CONFIG_SYS_INIT_SP_BSS_OFFSET)
      	adr	x0, __bss_start
      	add	x0, x0, #CONFIG_SYS_INIT_SP_BSS_OFFSET
      #else
      	ldr	x0, =(CONFIG_SYS_INIT_SP_ADDR)
      #endif
      	bic	sp, x0, #0xf	/* 16-byte alignment for ABI compliance */
      	mov	x0, sp
      	bl	board_init_f_alloc_reserve
      	mov	sp, x0
      	/* set up gd here, outside any C code */
      	mov	x18, x0
      	bl	board_init_f_init_reserve
      
      	mov	x0, #0
      	bl	board_init_f
    • DRAM bank사이즈 초기화, dram config 설정 등을 수행.
    • start.S의 relocate_code함수 호출.
    1. relocate_code()

      relocate_code:
      	mov	sp, r4		/* Set the new sp */
      	mov	r4, r5
      
      	/*
      	 * ZERO BSS/SBSS -- bss and sbss are assumed to be adjacent
      	 * and between __bss_start and __bss_end.
      	 */
      	movhi	r5, %hi(__bss_start)
      	ori	r5, r5, %lo(__bss_start)
      	movhi	r6, %hi(__bss_end)
      	ori	r6, r6, %lo(__bss_end)
      	beq	r5, r6, 5f
      
      4:	stw	r0, 0(r5)
      	addi	r5, r5, 4
      	bne	r5, r6, 4b
      5:
      
      	movhi	r8, %hi(board_init_r@h)
      	ori	r8, r8, %lo(board_init_r@h)
      	callr	r8
      	ret
    • U-boot을 ram으로 옮긴 후 마지막에 arch\arm\lib\board.c의 board_init_r 을 호출한다
    1. Board_init_r

      common/board_r.c
      
      void board_init_r(gd_t *new_gd, ulong dest_addr)
      {
      #if defined(CONFIG_ARM) || defined(CONFIG_RISCV)
      	initr_caches,
      	/* Note: For Freescale LS2 SoCs, new MMU table is created in DDR.
      	 *	 A temporary mapping of IFC high region is since removed,
      	 *	 so environmental variables in NOR flash is not available
      	 *	 until board_init() is called below to remap IFC to high
      	 *	 region.
      	 */
      #endif
      	initr_reloc_global_data,
      #if IS_ENABLED(CONFIG_NEEDS_MANUAL_RELOC) && CONFIG_IS_ENABLED(EVENT)
      	event_manual_reloc,
      #endif
      #if defined(CONFIG_SYS_INIT_RAM_LOCK) && defined(CONFIG_E500)
      	initr_unlock_ram_in_cache,
      #endif
      	initr_barrier,
      	initr_malloc,
      	log_init,
      	initr_bootstage,	/* Needs malloc() but has its own timer */
      #if defined(CONFIG_CONSOLE_RECORD)
      	console_record_init,
      #endif
      #ifdef CONFIG_SYS_NONCACHED_MEMORY
      	noncached_init,
      #endif
      	initr_of_live,
      #ifdef CONFIG_DM
      	initr_dm,
      #endif
      #ifdef CONFIG_ADDR_MAP
      	init_addr_map,
      #endif
      #if defined(CONFIG_ARM) || defined(CONFIG_RISCV) || defined(CONFIG_SANDBOX)
      	board_init,	/* Setup chipselects */
      #endif
      	/*
      	 * TODO: printing of the clock inforamtion of the board is now
      	 * implemented as part of bdinfo command. Currently only support for
      	 * davinci SOC's is added. Remove this check once all the board
      	 * implement this.
      	 */
      #ifdef CONFIG_CLOCKS
      	set_cpu_clk_info, /* Setup clock information */
      #endif
      #ifdef CONFIG_EFI_LOADER
      	efi_memory_init,
      #endif
      	initr_binman,
      #ifdef CONFIG_FSP_VERSION2
      	arch_fsp_init_r,
      #endif
      	initr_dm_devices,
      	stdio_init_tables,
      	serial_initialize,
      	initr_announce,
      	dm_announce,
      #if CONFIG_IS_ENABLED(WDT)
      	initr_watchdog,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #if defined(CONFIG_NEEDS_MANUAL_RELOC) && defined(CONFIG_BLOCK_CACHE)
      	blkcache_init,
      #endif
      #ifdef CONFIG_NEEDS_MANUAL_RELOC
      	initr_manual_reloc_cmdtable,
      #endif
      	arch_initr_trap,
      #if defined(CONFIG_BOARD_EARLY_INIT_R)
      	board_early_init_r,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #ifdef CONFIG_POST
      	post_output_backlog,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #if defined(CONFIG_PCI_INIT_R) && defined(CONFIG_SYS_EARLY_PCI_INIT)
      	/*
      	 * Do early PCI configuration _before_ the flash gets initialised,
      	 * because PCU resources are crucial for flash access on some boards.
      	 */
      	pci_init,
      #endif
      #ifdef CONFIG_ARCH_EARLY_INIT_R
      	arch_early_init_r,
      #endif
      	power_init_board,
      #ifdef CONFIG_MTD_NOR_FLASH
      	initr_flash,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #if defined(CONFIG_PPC) || defined(CONFIG_M68K) || defined(CONFIG_X86)
      	/* initialize higher level parts of CPU like time base and timers */
      	cpu_init_r,
      #endif
      #ifdef CONFIG_EFI_LOADER
      	efi_init_early,
      #endif
      #ifdef CONFIG_CMD_NAND
      	initr_nand,
      #endif
      #ifdef CONFIG_CMD_ONENAND
      	initr_onenand,
      #endif
      #ifdef CONFIG_MMC
      	initr_mmc,
      #endif
      #ifdef CONFIG_XEN
      	xen_init,
      #endif
      #ifdef CONFIG_PVBLOCK
      	initr_pvblock,
      #endif
      	initr_env,
      #ifdef CONFIG_SYS_MALLOC_BOOTPARAMS
      	initr_malloc_bootparams,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      	cpu_secondary_init_r,
      #if defined(CONFIG_ID_EEPROM)
      	mac_read_from_eeprom,
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #if defined(CONFIG_PCI_INIT_R) && !defined(CONFIG_SYS_EARLY_PCI_INIT)
      	/*
      	 * Do pci configuration
      	 */
      	pci_init,
      #endif
      	stdio_add_devices,
      	jumptable_init,
      #ifdef CONFIG_API
      	api_init,
      #endif
      	console_init_r,		/* fully init console as a device */
      #ifdef CONFIG_DISPLAY_BOARDINFO_LATE
      	console_announce_r,
      	show_board_info,
      #endif
      #ifdef CONFIG_ARCH_MISC_INIT
      	arch_misc_init,		/* miscellaneous arch-dependent init */
      #endif
      #ifdef CONFIG_MISC_INIT_R
      	misc_init_r,		/* miscellaneous platform-dependent init */
      #endif
      	INIT_FUNC_WATCHDOG_RESET
      #ifdef CONFIG_CMD_KGDB
      	kgdb_init,
      #endif
      	interrupt_init,
      #if defined(CONFIG_MICROBLAZE) || defined(CONFIG_M68K)
      	timer_init,		/* initialize timer */
      #endif
      #if defined(CONFIG_LED_STATUS)
      	initr_status_led,
      #endif
      	/* PPC has a udelay(20) here dating from 2002. Why? */
      #ifdef CONFIG_BOARD_LATE_INIT
      	board_late_init,
      #endif
      #if defined(CONFIG_SCSI) && !defined(CONFIG_DM_SCSI)
      	INIT_FUNC_WATCHDOG_RESET
      	initr_scsi,
      #endif
      #ifdef CONFIG_BITBANGMII
      	bb_miiphy_init,
      #endif
      #ifdef CONFIG_PCI_ENDPOINT
      	pci_ep_init,
      #endif
      #ifdef CONFIG_CMD_NET
      	INIT_FUNC_WATCHDOG_RESET
      	initr_net,
      #endif
      #ifdef CONFIG_POST
      	initr_post,
      #endif
      #if defined(CONFIG_IDE) && !defined(CONFIG_BLK)
      	initr_ide,
      #endif
      #ifdef CONFIG_LAST_STAGE_INIT
      	INIT_FUNC_WATCHDOG_RESET
      	/*
      	 * Some parts can be only initialized if all others (like
      	 * Interrupts) are up and running (i.e. the PC-style ISA
      	 * keyboard).
      	 */
      	last_stage_init,
      #endif
      #if defined(CFG_PRAM)
      	initr_mem,
      #endif
      	run_main_loop,
      };
      
    • board, ethernet, serial, mmc 등을 초기화하는 작업.
    1. Main_loop()
    • 하드웨어 초기화는 모두 완료
    • main loop는 사용자의 입력을 parsing하고, 해당 명령어를 수행한다.
    • 최종적으로 kernel을 로딩

참고

https://m.blog.naver.com/neos_rtos/220152283698

https://julrams.tistory.com/8

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=kangyunmoon&logNo=220735281091

https://julrams.tistory.com/10

https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=blueiceberg&logNo=50101044369

https://velog.io/@audgus47/벡터-테이블Vector-Table

https://github.com/Digilent/u-boot-Digilent-Dev/blob/master/board/samsung/smdk5250/lowlevel_init.S

https://github.com/ARM-software/u-boot/blob/master/arch/arm/cpu/armv7/start.S

https://code-lab1.tistory.com/174

https://chunggaeguri.tistory.com/entry/System-ISO-파일이란

https://live-everyday.tistory.com/67

https://dreamlog.tistory.com/221

https://johyungen.tistory.com/506

https://wpaud16.tistory.com/286

profile
백엔드 개발자

0개의 댓글