paint-brush
펌웨어의 비밀을 통한 여행: BIOS/UEFI에서 OS까지~에 의해@tristejoursoir
730 판독값
730 판독값

펌웨어의 비밀을 통한 여행: BIOS/UEFI에서 OS까지

~에 의해 Aleksandr Goncharov20m2024/08/22
Read on Terminal Reader

너무 오래; 읽다

기존 BIOS에서 최신 UEFI 펌웨어로의 진화를 살펴보고, 부팅 시퀀스가 관리되는 방식을 이해하고, 부팅 서비스와 런타임 서비스의 역할을 알아보세요. OS 부트 로더의 복잡성을 살펴보고 펌웨어가 이제 고급 기능과 애플리케이션을 어떻게 지원하는지 확인하세요.
featured image - 펌웨어의 비밀을 통한 여행: BIOS/UEFI에서 OS까지
Aleksandr Goncharov HackerNoon profile picture
0-item
1-item


컴퓨터의 전원 버튼을 누르는 순간 무슨 일이 일어나는지 궁금한 적이 있나요? 화면이 켜지기 전, 잠깐 멈춘 뒤에는 복잡한 일련의 프로세스가 진행됩니다. 이 글에서는 펌웨어의 매혹적인 세계를 탐구하며 부팅 프로세스 중에 다양한 구성 요소가 어떻게 상호 작용하는지 살펴봅니다.


이러한 연결을 이해하면 시스템을 살아있게 하는 기본 요소에 대한 더 명확한 그림을 얻을 수 있습니다. 우리의 주요 초점은 Intel x86 아키텍처 에 있지만 많은 원칙이 다른 아키텍처에도 적용됩니다.


시리즈의 첫 번째 부분을 놓치셨다면 여기를 클릭하여 따라잡으세요. 이제 펌웨어의 비밀을 밝혀보죠.

목차:

  • 정의
  • 전체 펌웨어 아키텍처
  • 1단계 부트로더(FSBL)
    • BIOS(POST 단계)
    • UEFI 플랫폼 초기화(PI)
    • 코어부트
    • 기타 솔루션
  • 2단계 부트로더(SSBL)
    • 바이오스
    • UEFI
  • OS 부트로더


정의

  • 펌웨어 : 하드웨어에 내장된 특수한 유형의 소프트웨어로, 저수준 제어를 제공하고 하드웨어가 올바르게 작동하고 다른 시스템 구성 요소와 상호 작용할 수 있도록 합니다.


  • 기본 입출력 시스템(BIOS) : 레거시 펌웨어(원래 IBM PC 용으로 만들어짐)로 플랫폼 전원 켜기 후 하드웨어 초기화를 담당합니다. 요즘은 종종 모호하게 펌웨어 전체 세트라고 합니다.


  • 부트로더 : 컴퓨터를 부팅하는 펌웨어의 일반 이름입니다. BIOS 대신 현대적인 개념으로 사용되며, 종종 프로세서와 칩셋을 초기화하는 부트스트랩 코드가 있는 프레임워크를 제공하고, 타사(예: 마더보드 개발자)가 플랫폼별 초기화를 수행할 수 있는 인터페이스를 제공합니다.


  • 페이로드 : 부트로더가 종료될 때 실행되는 소프트웨어. 2단계 부트로더, 운영 체제, BIOS/UEFI 애플리케이션 등이 될 수 있습니다. 일반적으로 펌웨어 설계에 따라 부트스트래핑 흐름을 처리합니다.


  • BIOS부트로더라는 용어의 사용은 의미가 문맥에 따라 달라지기 때문에 혼란스러울 수 있습니다. 그러나 누군가가 펌웨어 , BIOS 또는 부트로더를 언급할 때 일반적으로 운영 체제와 하드웨어 사이에서 실행되는 전체 펌웨어 세트를 말합니다.


  • EFIUEFI : 확장 가능 펌웨어 인터페이스(EFI)는 인텔이 개발한 원래 사양입니다. 통합 확장 가능 펌웨어 인터페이스(UEFI)는 EFI 의 후속 버전으로, UEFI 포럼 에서 원래 사양을 표준화하고 확장하기 위해 만들었습니다. 대부분의 경우 EFIUEFI는 서로 바꿔 사용됩니다.

전체 펌웨어 아키텍처

펌웨어 구성 요소가 어떻게 상호 작용하는지 이해하기 위해 모든 연결된 부분이 있는 전체 아키텍처를 살펴보겠습니다. 아래 다이어그램에 표시된 실행 흐름은 First-Stage Bootloader 의 일부인 재설정 벡터 에서 시작합니다. 거기에서 다양한 펌웨어 단계를 거칩니다.



펌웨어 또는 BIOS는 일반적으로 두 가지 주요 부분으로 나눌 수 있으며, 두 부분 간의 인터페이스는 일반적으로 최소화됩니다.


  1. 하드웨어 초기화 : 시스템의 하드웨어 구성 요소를 초기화하는 역할을 담당합니다.
  2. 운영체제 및 사용자 인터페이스 : 운영체제와 사용자에게 필요한 인터페이스를 제공합니다.


플랫폼 펌웨어의 설계는 하드웨어 초기화와 부팅 기능을 결합한 모놀리식이 거나 모듈식단계적 부팅 흐름을 따를 수 있습니다. 설계 선택은 시스템 요구 사항에 따라 달라지며 특정 장치에 더 적합할 수 있습니다.


다음 다이어그램은 다양한 펌웨어 구성 요소가 어떻게 상호 작용하고 부팅 프로세스를 지원하기 위해 함께 사용될 수 있는지 보여줍니다(화살표는 실행 순서를 나타냄):



지금 이 다이어그램이 복잡해 보인다면 걱정하지 마세요. 이 기사를 읽은 후 다시 검토하면 더 명확해질 겁니다.

1단계 부트로더(FSBL)

이 펌웨어는 최소한의 하드웨어 초기화에 초점을 맞춰 컴퓨터와 임베디드 시스템을 초기화하도록 설계되었습니다. 즉, 절대적으로 필요한 작업만 수행한 다음 제어를 Second-Stage Bootloader 에 전달하여 운영 체제를 부팅합니다. FSBL은 플래시 칩 이외의 저장 매체에서 운영 체제를 로드하지 않습니다. 기본 하드웨어만 초기화하고 하드 드라이브, SSD 또는 USB 플래시 드라이브와 같은 부팅 매체를 처리하지 않으므로 실제로 운영 체제를 부팅하려면 다른 소프트웨어가 필요합니다.


FSBL의 주요 책임 :


  1. CPU : 16비트 실제 모드 에서 32비트 보호 모드 로 전환합니다( 참고 : BIOS의 경우 가상 8086 모드 ).
  2. 캐시 활용 : C 환경 에 대한 Cache-As-RAM을 구성하기 위해 FSP-T를 호출합니다.
  3. 디버그 포트 : 보드별 초기화 메서드를 호출하여 구성된 디버그 포트를 초기화합니다.
  4. 메모리 초기화 : FSP-M을 호출하여 시스템의 주 메모리를 초기화하고 중요한 시스템 메모리 정보를 저장합니다.
  5. GPIO : 외부 장치와의 인터페이스를 위한 범용 입출력(GPIO) 핀 구성.
  6. 실리콘 : 초기 플랫폼 초기화를 수행하고 FSP-S를 사용하여 칩셋, CPU, IO 컨트롤러 초기화를 완료합니다.
  7. PCI 열거 : PCI 장치를 열거하고 메모리 주소, IRQ와 같은 리소스를 할당합니다.
  8. 페이로드 준비 : 페이로드에 전달해야 하는 준비 정보(코어부트 테이블, HOB)를 포함하여 SMBIOSACPI 테이블을 설정합니다.
  9. 로딩 및 핸드오프 : 페이로드에 대한 제어권 로딩 및 이전.

BIOS(POST 단계)

컴퓨팅의 초기 시절에는 오픈소스 소프트웨어가 널리 보급되지 않았고, 대부분의 BIOS 구현은 독점적이었습니다. BIOS POST 소스 코드를 제공하는 오픈 솔루션은 Super PC/Turbo XT BIOSGLaBIOS 와 같이 몇 가지에 불과합니다. 이러한 프로젝트는 IBM 5150/5155/5160 시스템과 대부분의 XT 클론에서 작동하도록 설계되었습니다.


그러나 OpenBIOSSeaBIOS 와 같은 더 잘 알려진 오픈소스 BIOS 구현은 베어 하드웨어에서 실행되도록 의도되지 않았기 때문에 하드웨어 초기화를 수행하지 않습니다. 그러나 이들은 2단계 부트로더 로 널리 사용되고 QEMU 및 Bochs와 같은 가상 환경에서 기본적으로 실행됩니다.


어떤 경우든, 이 초기 BIOS를 직접 다루거나 세부 사항을 깊이 파고들 필요가 있을 가능성은 거의 없습니다. 하지만 탐색하는 데 관심이 있다면 언급된 저장소가 좋은 시작점입니다.


현재 개발 추세를 볼 때, 독점적인 BIOS 솔루션에 대한 지속적인 개발은 없는 것으로 보이며, 이러한 프로젝트는 현대적인 대안에 직면하여 더 이상 의미가 없게 되었습니다.

UEFI 플랫폼 초기화(PI)

부팅 프로세스는 다음 그림에서 왼쪽에서 시작하여 오른쪽으로 이동하는 단계적 흐름을 따릅니다. 플랫폼 부팅 프로세스의 타임라인은 노란색 상자로 표시된 대로 다음 구문으로 나뉩니다.



  • 보안(SEC) : 재설정 벡터 의 첫 번째 단계로, 주요 기능은 임시 RAM(CPU 캐시-램 또는 SRAM)을 설정하는 것입니다.
  • 사전 EFI 초기화(PEI) : 이 단계는 사전 EFI 초기화 모듈(PEIM) 이라고 하는 특수 드라이버를 전송합니다. 이러한 모듈은 CPU 및 칩셋 구성, 주 메모리(DRAM) 설정과 같은 필수 하드웨어 초기화를 처리합니다.
  • 드라이버 실행 환경(DXE) : 이 단계에서는 나머지 시스템 초기화가 수행됩니다. DXE 단계는 UEFI 서비스를 제공하고 시스템 작동에 필요한 다양한 프로토콜과 드라이버를 지원합니다.
  • 부팅 장치 선택(BDS) : 이 단계에서는 플랫폼 부팅 정책을 구현하고 부팅 순서를 결정하며 적절한 부팅 장치/로더를 선택합니다.
  • 일시적 시스템 로드(TSL) : 이 단계에서 시스템은 UEFI 서비스를 사용하여 애플리케이션을 실행하여 OS를 준비합니다. 여기에는 UEFI 환경에서 운영 체제로의 전환이 포함되며 ExitBootServices() 호출로 마무리됩니다.
  • 런타임(RT) : 이 단계에서는 운영체제가 완전히 작동하며 운영체제의 제어 하에 시스템을 관리합니다.
  • After Life(AL) : 이 단계는 하드웨어나 OS가 충돌/종료/재부팅되는 시나리오를 다룹니다. 펌웨어는 복구 작업을 시도할 수 있지만 UEFI PI 사양은 이 단계에 대한 특정 요구 사항이나 동작을 정의하지 않습니다.


이 프로세스와 실행 단계는 UEFI 플랫폼 초기화(PI) 사양 에서 다룹니다. 그러나 이전 문서의 일부가 아니며 UEFI 사양 에서 설명하는 UEFI 인터페이스 (그림에서 굵은 파란색 선으로 표시)도 있습니다. UEFI 의 이름과 빈번한 사용은 혼란스러울 수 있지만, 이 두 문서는 다른 초점을 두고 있습니다.


  • UEFI PI 사양 : 저수준 펌웨어 구성 요소 간의 인터페이스에 초점을 맞추고 이러한 모듈이 플랫폼을 초기화하기 위해 어떻게 상호 작용하는지 자세히 설명합니다.


  • UEFI 사양 : 운영 체제(OS)와 펌웨어 간 상호 작용을 위한 인터페이스를 정의합니다. 이는 Second-Stage Bootloader 의 맥락에서 더 자세히 논의됩니다. UEFI 사양은 PI 사양에 의존한다는 점에 유의하세요.


기본적으로 두 사양 모두 인터페이스에 관한 것이지만, 다른 수준에서입니다. 자세한 내용은 UEFI 포럼 웹사이트 에서 두 사양에 액세스할 수 있습니다.


UEFI PI는 원래 통합 펌웨어 솔루션으로 설계되었으며, 1단계 부트로더와 2단계 부트로더의 구분을 고려하지 않았습니다. 그러나 UEFI를 1단계 부트 로더라고 부를 때는 SEC , PEI , 초기 DXE 단계가 포함됩니다. DXE를 초기 단계후기 단계로 나누는 이유는 초기화 프로세스에서 역할이 다르기 때문입니다.


초기 DXE 단계에서 드라이버는 일반적으로 필수적인 CPU/PCH/보드 초기화를 수행하고 DXE 아키텍처 프로토콜(AP) 도 생성하는데, 이는 DXE 단계를 플랫폼별 하드웨어에서 분리하는 데 도움이 됩니다. AP는 플랫폼에 특정한 세부 정보를 캡슐화하여 후기 DXE 단계가 하드웨어 세부 정보와 독립적으로 작동할 수 있도록 합니다.



코어부트

Coreboot가 어떻게 작동하는지에 대한 자세한 기사가 곧 나올 예정입니다. 저의 소셜 미디어를 팔로우하세요. 곧 게시될 예정입니다!

기타 솔루션

  • Intel Slim Bootloader(SBL) : 코어 하드웨어 구성 요소 초기화만 제공하고, 그 다음에 페이로드를 로드하는 순수한 1단계 부트로더입니다. 그러나 Intel x86 플랫폼에서만 작동하며 AMD x86 또는 다른 아키텍처는 지원하지 않습니다.
  • Das U-Boot : 1단계와 2단계 부트로더입니다. 그러나 플랫폼의 x86 리셋 벡터 에서 직접 부팅하는 지원(베어 모드라고 함)은 다른 펌웨어에 비해 제한적입니다. 임베디드 시스템과 ARM 기반 장치에서 더 인기가 있습니다. 2단계 부트로더인 U-Boot은 UEFI의 하위 집합 을 구현하지만 임베디드 시스템에 초점을 맞춥니다.

2단계 부트로더(SSBL)

초기 하드웨어 설정이 완료되면 두 번째 단계가 시작됩니다. 주요 역할은 운영 체제와 플랫폼 펌웨어 간에 소프트웨어 인터페이스를 설정하여 OS가 시스템 리소스를 관리하고 하드웨어 구성 요소와 상호 작용할 수 있도록 하는 것입니다.


SSBL은 하드웨어 변형을 최대한 숨기고 , 대부분의 하드웨어 수준 인터페이스를 처리하여 OS 및 애플리케이션 개발을 간소화하는 것을 목표로 합니다. 이 추상화를 통해 개발자는 기본 하드웨어 차이에 대해 걱정하지 않고 상위 수준 기능에 집중할 수 있습니다.


SSBL의 주요 책임 :


  1. 플랫폼 정보 검색 : 메모리 매핑, SMBIOS, ACPI 테이블, SPI 플래시 등을 포함하여 1단계 부트로더 에서 플랫폼별 정보를 가져옵니다.


  2. 플랫폼 독립 드라이버 실행 : SMM, SPI, PCI, SCSI/ATA/IDE/DISK, USB, ACPI, 네트워크 인터페이스 등을 위한 드라이버가 포함되어 있습니다.


  3. 서비스 구현(일명 인터페이스) : 운영 체제와 하드웨어 구성 요소 간의 통신을 용이하게 하는 서비스 세트를 제공합니다.


  4. 설정 메뉴 : 시스템 구성을 위한 설정 메뉴를 제공하여 사용자가 부팅 순서, 하드웨어 기본 설정 및 기타 시스템 매개변수와 관련된 설정을 조정할 수 있습니다.


  5. 부트 로직 : 사용 가능한 부트 미디어에서 페이로드(아마도 운영 체제)를 찾아 로드하는 메커니즘입니다.

바이오스

BIOS의 인터페이스는 BIOS 서비스/함수/인터럽트 호출 이라고 합니다. 이러한 함수는 하드웨어 액세스를 위한 일련의 루틴을 제공하지만, 시스템의 특정 하드웨어에서 실행되는 구체적인 세부 사항은 사용자에게 숨겨져 있습니다.


16비트 Real Mode 에서는 INT x86 어셈블리 언어 명령어를 통해 소프트웨어 인터럽트를 호출하여 쉽게 액세스할 수 있습니다. 32비트 Protected Mode 에서는 세그먼트 값을 처리하는 방식이 다르기 때문에 거의 모든 BIOS 서비스를 사용할 수 없습니다.




예를 들어, 실린더-헤드-섹터(CHS) 주소 지정을 사용하여 섹터 기반 하드 디스크 및 플로피 디스크 읽기 및 쓰기 서비스를 제공하는 Disk Services ( INT 13h )를 살펴보겠습니다. 이 인터페이스를 사용하는 방법의 예입니다. 2개의 섹터(1024바이트)를 읽고 메모리 주소 0x9020 에 로드하려고 한다고 가정하면 다음 코드를 실행할 수 있습니다.


 mov $0x02, %ah # Set BIOS read sector routine mov $0x00, %ch # Select cylinder 0 mov $0x00, %dh # Select head 0 [has a base of 0] mov $0x02, %cl # Select sector 2 (next after the # boot sector) [has a base of 1] mov $0x02, %al # Read 2 sectors mov $0x00, %bx # Set BX general register to 0 mov %bx, %es # Set ES segment register to 0 mov $0x9020, %bx # Load sectors to ES:BX (0:0x9020) int $0x13 # Start reading from drive jmp $0x9020 # Jump to loaded code


SeaBios에서 이 서비스가 어떻게 작성되었는지 궁금하다면 src/disk.c를 살펴보세요.

부트 단계

  • 부팅 가능한 장치 (하드 드라이브, CD-ROM, 플로피 디스크 등)를 검색하기 위해 장치의 첫 번째 512바이트 섹터 (섹터 0)를 읽습니다.


  • BIOS의 부트스트랩 시퀀스는 발견된 첫 번째 유효한 마스터 부트 레코드(MBR) 를 컴퓨터의 물리적 메모리의 물리적 주소 0x7C00 에 로드합니다(힌트: 0x0000:0x7c000x7c0:0x0000은 동일한 물리적 주소를 나타냅니다).


  • BIOS는 제어를 페이로드의 처음 512바이트 로 넘깁니다. 전체 페이로드 코드를 담기에는 너무 작은 이 로드된 섹터는 부팅 가능한 장치 에서 나머지 페이로드를 로드하는 목적을 갖습니다. 이 시점에서 페이로드는 BIOS에서 노출된 인터페이스를 사용할 수 있습니다.


BIOS 사양이 초기에는 존재하지 않았다는 점은 주목할 만합니다. BIOS는 사실상의 표준 입니다. 1980년대에 실제 IBM PC에서 작동했던 방식대로 작동합니다. 나머지 제조업체는 단지 역엔지니어링을 해서 IBM 호환 BIOS를 만들었습니다. 그 결과 BIOS 제조업체가 새로운 BIOS 기능을 발명하거나 중복되는 기능을 갖는 것을 방지하는 규정이 없었습니다.

통합 확장 가능 펌웨어 인터페이스(UEFI)

앞서 언급했듯이 UEFI 자체는 단지 사양일 뿐이며 많은 구현이 있습니다. 가장 널리 사용되는 것은 UEFI 및 PI 사양의 오픈 소스 참조 구현인 TianoCore EDK II 입니다. EDKII만으로는 완벽하게 작동하는 부팅 펌웨어를 만들기에 충분하지 않지만 대부분의 상용 솔루션에 견고한 기반을 제공합니다.


다양한 First-Stage 부트로더를 지원하고 UEFI 인터페이스를 제공하기 위해 UEFI Payload 프로젝트가 사용됩니다. UEFI Payload 프로젝트는 부팅 펌웨어에서 제공하는 초기 설정 및 플랫폼 정보를 사용하여 시스템을 UEFI 환경에 준비합니다.


UEFI Payload는 플랫폼에 독립적으로 설계된 DXEBDS 단계를 사용합니다. 다양한 플랫폼에 적응할 수 있는 일반 페이로드를 제공합니다. 대부분의 경우 사용자 정의나 플랫폼별 조정이 필요하지 않으며 First-Stage Bootloader 에서 플랫폼 정보를 사용하여 그대로 사용할 수 있습니다.


UEFI 페이로드의 변형 :


  1. 레거시 UEFI 페이로드 : 필요한 구현별 플랫폼 정보를 추출하기 위해 파스 라이브러리가 필요합니다. 부트로더가 API를 업데이트하면 페이로드도 업데이트해야 합니다.



  2. Universal UEFI Payload : Universal Scalable Firmware(USF) 사양을 따르며, Executable and Linkable Format(ELF) 또는 Flat Image Tree(FIT)를 공통 이미지 형식으로 사용합니다. 직접 파싱하는 대신, 페이로드 항목에서 Hand Off Blocks(HOB)를 수신할 것으로 예상합니다.


Legacy UEFI Payload가 잘 작동하는 동안 EDK2 커뮤니티는 업계를 Universal UEFI Payload 로 전환하려고 노력하고 있습니다. 페이로드 간의 선택은 펌웨어 구성 요소에 따라 달라집니다. 예를 들어, 내 패치 없이 Slim Bootloader 에서 SMM 지원으로 Legacy Payload를 실행할 수 없습니다. 반면, coreboot와 함께 Universal Payload를 사용하려면 coreboot 테이블을 HOB 로 변환하는 shim 계층이 필요한데, 이 기능은 StarLabs EDK2 포크 에서만 사용할 수 있습니다.

인터페이스

모든 UEFI 호환 시스템은 UEFI 환경에서 실행되는 모든 코드(드라이버, 애플리케이션, OS 로더)에 전달되는 시스템 테이블을 제공합니다. 이 데이터 구조를 통해 UEFI 실행 파일은 ACPI , SMBIOSUEFI 서비스 모음과 같은 시스템 구성 테이블 에 액세스할 수 있습니다.



테이블 구조는 MdePkg/Include/Uefi/UefiSpec.h 에 설명되어 있습니다.


 typedef struct { EFI_TABLE_HEADER Hdr; CHAR16 *FirmwareVendor; UINT32 FirmwareRevision; EFI_HANDLE ConsoleInHandle; EFI_SIMPLE_TEXT_INPUT_PROTOCOL *ConIn; EFI_HANDLE ConsoleOutHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut; EFI_HANDLE StandardErrorHandle; EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *StdErr; // // A pointer to the EFI Runtime Services Table. // EFI_RUNTIME_SERVICES *RuntimeServices; // // A pointer to the EFI Boot Services Table. // EFI_BOOT_SERVICES *BootServices; UINTN NumberOfTableEntries; EFI_CONFIGURATION_TABLE *ConfigurationTable; } EFI_SYSTEM_TABLE;


서비스에는 다음 유형이 포함됩니다. 부팅 서비스 , 런타임 서비스프로토콜에서 제공하는 서비스 .


UEFI는 UEFI 프로토콜을 설정하여 장치에 대한 액세스를 추상화합니다. 이러한 프로토콜은 함수 포인터를 포함하는 데이터 구조 이며 다른 모듈이 이를 찾아 사용할 수 있도록 하는 GUID(Globally Unique IDentifier) 로 식별됩니다. 부트 서비스를 통해 발견할 수 있습니다.


UEFI 드라이버는 이러한 프로토콜을 생성하고 실제 함수(포인터가 아님!)는 드라이버 자체에 포함되어 있습니다. 이 메커니즘은 UEFI 환경 내의 다양한 구성 요소가 서로 통신할 수 있도록 하며 OS가 자체 드라이버를 로드하기 전에 장치와 상호 작용할 수 있도록 보장합니다.



일부 프로토콜은 UEFI 사양에 미리 정의되어 설명되어 있지만, 펌웨어 공급업체는 플랫폼의 기능을 확장하기 위해 자체 사용자 정의 프로토콜을 만들 수도 있습니다.


부트 서비스

부팅 시간 에만 사용할 수 있는 기능을 제공합니다. 이러한 서비스는 EFI_BOOT_SERVICES.ExitBootServices() 함수가 호출될 때까지 사용 가능합니다( MdeModulePkg/Core/Dxe/DxeMain/DxeMain.c ).


모든 부팅 서비스에 대한 포인터는 부팅 서비스 테이블 ( MdePkg/Include/Uefi/UefiSpec.h )에 저장됩니다.


 typedef struct { EFI_TABLE_HEADER Hdr; ... EFI_GET_MEMORY_MAP GetMemoryMap; EFI_ALLOCATE_POOL AllocatePool; EFI_FREE_POOL FreePool; ... EFI_HANDLE_PROTOCOL HandleProtocol; ... EFI_EXIT_BOOT_SERVICES ExitBootServices; ... } EFI_BOOT_SERVICES;


런타임 서비스

최소한의 서비스 세트는 운영 체제가 실행되는 동안에도 여전히 액세스할 수 있습니다. 부트 서비스와 달리 이러한 서비스는 모든 페이로드(예: OS 부트로더)가 EFI_BOOT_SERVICES.ExitBootServices() 를 호출하여 플랫폼을 제어한 후에도 여전히 유효합니다.


모든 런타임 서비스에 대한 포인터는 런타임 서비스 테이블 ( MdePkg/Include/Uefi/UefiSpec.h )에 저장됩니다.


 typedef struct { EFI_TABLE_HEADER Hdr; ... EFI_GET_TIME GetTime; EFI_SET_TIME SetTime; ... EFI_GET_VARIABLE GetVariable; EFI_GET_NEXT_VARIABLE_NAME GetNextVariableName; EFI_SET_VARIABLE SetVariable; ... EFI_GET_NEXT_HIGH_MONO_COUNT GetNextHighMonotonicCount; EFI_RESET_SYSTEM ResetSystem; ... } EFI_RUNTIME_SERVICES;


아래 그림은 부팅 및 런타임 서비스의 타임라인을 보여주므로 각 서비스가 언제 활성화되는지 정확히 확인할 수 있습니다.



부팅 장치 선택(BDS) 단계

UEFI 사양은 UEFI 부트 관리자 라는 부트 정책 엔진을 정의합니다. 이 엔진은 특정 순서로 UEFI 애플리케이션을 로드하려고 시도합니다. 이 순서와 기타 설정은 글로벌 NVRAM(비휘발성 랜덤 액세스 메모리) 변수를 수정하여 구성할 수 있습니다. 가장 중요한 변수에 대해 논의해 보겠습니다.


  • Boot#### ( #### 고유한 16진수 값으로 대체됨) — 부팅/로드 옵션입니다.
  • BootCurrent — 현재 실행 중인 시스템을 시작하는 데 사용되는 부팅 옵션입니다.
  • BootNext — 다음 부팅만을 위한 부팅 옵션입니다. 이것은 한 부팅만을 위한 BootOrder 대체하며 처음 사용 후 부트 관리자에 의해 삭제됩니다. 이를 통해 BootOrder 변경하지 않고도 다음 부팅 동작을 변경할 수 있습니다.
  • BootOrder — 정렬된 부팅 옵션 로드 목록. 부트 관리자는 이 목록에서 첫 번째 활성 옵션을 부팅하려고 시도합니다. 실패하면 다음 옵션을 시도하고, 이런 식으로 계속됩니다.
  • BootOptionSupport — 부팅 관리자가 지원하는 부팅 옵션 유형입니다.
  • Timeout - BootNext 또는 BootOrder 에서 자동으로 시작 값을 선택하기 전에 펌웨어의 부트 관리자가 시간 초과(초)를 발생시킵니다.


이러한 변수는 efibootmgr(8)를 사용하여 Linux에서 쉽게 얻을 수 있습니다.


 [root@localhost ~]# efibootmgr BootCurrent: 0000 Timeout: 5 seconds BootOrder: 0000,0001,2001,2002,2003 Boot0000* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi) Boot0001* Windows Boot Manager HD(1,GPT,6f185443-09fc-4f15-afdf-01c523565e52,0x800,0x32000)/File(\EFI\Microsoft\Boot\bootmgfw.efi)57a94e544f5753000100000088900100780000004200430044039f0a42004a004500430054003d007b00390064006500610038003600320063002d1139006300640064002d0034006500370030102d0061006300630031002d006600330032006200330034003400640034003700390035007d00000033000300000710000000040000007fff0400 Boot0002* ARCHLINUX HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000) Boot2001* EFI USB Device RC Boot2002* EFI DVD/CDROM RC Boot2003* EFI Network RC


위의 코드 조각을 사용하여 부팅을 살펴보겠습니다. UEFI는 BootOrder 목록을 반복하기 시작합니다. 목록의 각 항목에 대해 해당 Boot#### 변수를 찾습니다. 0000의 경우 Boot0000 , 2003의 경우 Boot2003 등입니다. 변수가 없으면 다음 항목으로 계속합니다. 변수가 있으면 변수의 내용을 읽습니다. 각 부팅 옵션 변수에는 가변 길이 필드의 바이트 압축 버퍼인 EFI_LOAD_OPTION 설명자가 포함됩니다(단지 데이터 구조일 뿐입니다).


데이터 구조는 [MdePkg/Include/Uefi/UefiSpec.h][ https://github.com/tianocore/edk2/blob/edk2-stable202405/MdePkg/Include/Uefi/UefiSpec.h#L2122 )에 설명되어 있습니다.


 typedef struct _EFI_LOAD_OPTION { /// The attributes for this load option entry. UINT32 Attributes; /// Length in bytes of the FilePathList. UINT16 FilePathListLength; /// The user readable description for the load option. /// Example: 'ARCHLINUX' / 'Windows Boot Manager' / `EFI USB Device` // CHAR16 Description[]; /// A packed array of UEFI device paths. /// Example: 'HD(5,GPT,d03ca3cf-1511-d94e-8400-c7a125866442,0x40164000,0x100000)/File(\EFI\ARCHLINUX\grubx64.efi)' // EFI_DEVICE_PATH_PROTOCOL FilePathList[]; /// The remaining bytes in the load option descriptor are a binary data buffer that is passed to the loaded image. /// Example: '57a9...0400' in Boot0001 variable // UINT8 OptionalData[]; } EFI_LOAD_OPTION;


이 시점에서 펌웨어는 장치 경로 ( EFI_DEVICE_PATH_PROTOCOL )를 검사합니다. 대부분의 경우, 컴퓨터는 저장 장치(하드 드라이브/SSD/NVMe/등)에서 부팅됩니다. 따라서 장치 경로에는 HD(Partition Number, Type, Signature, Start sector, Size in sectors) 노드가 포함됩니다.


  • 유형MBR (1) 또는 GPT (2) 키워드를 사용하여 분할 방식에 사용되는 형식을 나타냅니다.
  • 서명 - 유형이 MBR 인 경우 4바이트 MBR 서명이거나 유형이 GPT 인 경우 16바이트 UUID 입니다.


참고 : 다른 경로를 변환하는 방법에 관심이 있으면 UEFI 사양 v2.10, 10.6.1.6 텍스트 장치 노드 참조를 읽어보세요.


UEFI는 디스크를 살펴보고 노드와 일치하는 파티션이 있는지 확인합니다. 존재하는 경우 EFI 시스템 파티션(ESP) 으로 표시하는 특정 전역 고유 식별자(GUID) 로 레이블이 지정되어야 합니다. 이 파티션은 FAT 파일 시스템 의 특정 버전을 기반으로 하는 사양을 가진 파일 시스템으로 포맷되었으며 EFI 파일 시스템 이라는 이름이 지정되었습니다. 실제로는 일반 FAT12/16/32 입니다.


  • 네이티브 부팅 : 장치 경로에 파일 File(\Path\To\The\File.efi) 에 대한 명시적 경로가 포함되어 있는 경우 UEFI는 해당 특정 파일을 찾습니다. 예를 들어, Boot0000 옵션에는 File(\EFI\ARCHLINUX\grubx64.efi) 가 포함되어 있습니다.
  • 폴백 부팅 : 장치 경로가 단순히 디스크를 가리키는 경우, 이러한 상황에서 펌웨어는 아키텍처에 기반한 폴백 부팅 경로를 사용합니다 — \EFI\BOOT\BOOT{arch}.EFI ( amd64 의 경우 BOOTx64.EFI 또는 i386 / IA32 의 경우 BOOTia32.EFI ). 이 메커니즘은 부팅 가능한 이동식 미디어 (예: USB 드라이브)가 UEFI에서 작동할 수 있도록 합니다. 폴백 부팅 경로 만 사용합니다. 예를 들어, Boot0002 옵션은 이 메커니즘을 사용합니다.


참고: 위에 언급된 모든 Boot#### 옵션은 efibootmgr 의 예시 출력에 표시된 부팅 옵션을 참조합니다.


두 경우 모두 UEFI 부트 관리자는 UEFI 애플리케이션 ( OS 부트로더 , UEFI 셸, 유틸리티 소프트웨어, 시스템 설정 등)을 메모리에 로드합니다. 이때 제어권은 UEFI 애플리케이션 의 진입점으로 이전됩니다. BIOS 와 달리 UEFI 애플리케이션은 제어권을 펌웨어로 반환할 수 있습니다(애플리케이션이 시스템 제어권을 인계하는 상황 외에도). 이런 일이 발생하거나 문제가 발생하면 부트 관리자는 다음 Boot#### 항목으로 이동하여 정확히 동일한 프로세스를 따릅니다.


사양에서는 부트 관리자가 데이터베이스 변수를 자동으로 유지할 수 있다고 언급합니다. 여기에는 참조되지 않거나 구문 분석할 수 없는 로드 옵션 변수를 제거하는 것이 포함됩니다. 또한, 해당 로드 옵션 변수가 없는 모든 로드 옵션을 제거하기 위해 정렬된 목록을 다시 쓸 수 있습니다.


위의 텍스트는 UEFI 부팅을 설명합니다. 또한 UEFI 펌웨어는 BIOS를 에뮬레이트하는 호환성 지원 모듈(CSM) 모드에서 실행할 수 있습니다.

OS 부트로더

펌웨어(일반적으로 Second-Stage Bootloader )에서 시작하여 해당 인터페이스를 사용하여 OS 커널을 로드하는 소프트웨어입니다. OS만큼 복잡할 수 있으며 다음과 같은 기능을 제공합니다.


  • 다양한 파일 시스템(HFS+, ext4, XFS 등)에서 읽기
  • 네트워크를 통한 상호작용(예: TFTP, HTTP)
  • Multiboot 호환 커널 부팅
  • 체인 로딩
  • 초기 램디스크 로딩( initrd )
  • 그리고 더 많은 것들!


이러한 프로그램의 일반적인 디자인은 이 기사의 범위를 벗어납니다. 인기 있는 OS 부트로더에 대한 자세한 비교는 ArchLinux 위키Wikipedia 기사를 참조할 수 있습니다.


Windows 시스템은 BOOTMGR(Windows Boot Manager) 이라는 독점적인 OS 부트로더를 사용합니다.


펌웨어는 더 이상 작고 복잡한 코드 조각이 아닙니다. 그것은 엄청난 양의 복잡한 코드가 되었고, 현재 추세는 이것에 기여할 뿐입니다. 우리는 Doom , Twitter 및 기타 많은 흥미로운 애플리케이션을 실행할 수 있습니다.


전반적인 아키텍처를 이해하면 이러한 구성 요소를 마음속으로 정리하는 데 도움이 됩니다. 기존 펌웨어의 디자인을 검토하면 컴퓨터가 켜질 때마다 전개되는 매혹적인 프로세스에 대한 통찰력을 얻을 수 있습니다. 이러한 상향식 관점은 각 부분의 역할을 명확히 할 뿐만 아니라 현대 펌웨어 시스템의 정교하고 진화하는 본질을 강조합니다.

자원