프로젝트 빌드 환경설정

프로젝트 빌드 환경설정

2024년 12월 12일

목표 #

MikanOS 의 빌드는 ansible을 사용해서 ubuntu 에서 동작하도록 구현되어 있다. (apt, mount 방식 등)

WSL으로 세팅하는 방법을 먼저 알아보고, arm 맥북에서도 개발하고 테스트하기 위해 필요한 툴들을 설치하고 쉽게 빌드환경 세팅이 가능하도록 스크립트를 작성하는 것이 목표이다.

ansible은 mac에서 사용할 수 있도록 homebrew 모듈을 지원하고, x86-64 크로스 플랫폼으로 OS를 빌드하고 qemu를 사용해서 gui 에뮬레이팅하는 환경만 구성하면 어렵지 않게 세팅할 수 있을것이다.


WSL 에서의 환경설정 #

개발환경 세팅 #

일단 ubuntu를 타겟으로 구현해뒀기 때문에 WSL에서의 환경 설정은 어렵지 않다.

  • Windows 11
  • WSL2 - ubuntu 22.04

https://github.com/uchan-nos/mikanos-build.git 이 깃허브의 README.md 대로 따라가면 된다.

1sudo apt update
2sudo apt install git
3cd $HOME
4git clone https://github.com/uchan-nos/mikanos-build.git osbook
5
6sudo apt install ansible
7cd $HOME/osbook/devenv
8ansible-playbook -K -i ansible_inventory ansible_provision.yml     # input sudo password

세팅을 하고나면 이런 폴더 트리를 볼 수 있다. 94743370-d261-4718-9afb-b9ec31be651a


QEMU X-Server #

WSL에서는 GUI를 지원하지 않기 때문에 qemu의 화면을 표시 해줄 수 없다. 이걸 해결하려면 디스플레이 서버인 X-Server가 필요하다.

MikanOS 책에서는 VcXsrv 를 사용하고 있고, https://sourceforge.net/projects/vcxsrv/ 에서 설치하고 나면 XLaunch 를 이용해 환경을 세팅할 수 있다.

Extra settings에서 “Disable access control” 설정을 체크해서 모든 클라이언트로부터 연결 요청을 받게 설정하면 편하다.

만약 이후에도 제대로 연결이 안된다면 WSL이 퍼블릭 네트워크이기 때문에 방화벽을 잘 살펴보고 VcXsrv 앱의 설정을 퍼블릭 네트워크에서 통신을 허용하도록 변경해주면 된다. 1d5f981d-716e-49a6-b860-78b074bba0d5


실행 #

1cd $HOME/osbook/devenv/
2./run_qemu.sh ../day01/bin/hello.efi

명령을 실행하고 나면 xserver 화면에 Hello, wrold! 가 출력된 것을 확인할 수 있다.

f951ba2f-6107-40a8-aa8e-b864a392aa4e


세팅 스크립트 분석 #

Ansible? #

WSL 에서의 환경 세팅을 보면 ansible을 사용해서 환경을 세팅하는데, ansible은 여러 서버를 한꺼번에 세팅하기 위한 환경구성 도구이다.

  • 컨트롤 노드 : Ansible이 설치된 호스트이며, Python이 설치되어 있어야 한다.
  • 관리 노드 : Ansible의 관리 대상이 되는 노드이다.
  • 인벤토리 : 관리 노드들이 적힌 목록 파일이다. 호스트명이나 IP 주소로 관리노드를 지정하고 그룹화할수도 있다.
  • 작업(Task) : Ansible의 실행 단위이며, 하나의 모듈을 지정해서 하나의 작업을 실행한다.
  • 모듈 : Ansible로 여러가지 작업을 하기 위한 python 코드 단위이다.
  • 플레이북 : 관리노드에서 실행할 작업(모듈)을 여러 파라미터와 같이 정의한 파일이다. 컨트롤 노드에서 ansible-playbook 명령으로 실행해서 관리노드에 적용한다.

ansible_inventory #

어떤 서버를 대상으로 작업할건지 지정하는 것 이지만, 여기에서는 로컬호스트 한대에 세팅할거라고 지정했다. 사실 이정도는 쉘스크립트 만으로도 목표를 달성할 수 있다.

1kdh@DESKTOP-MHEA7GE:~/osbook/devenv$ cat ansible_inventory
2[default]
3localhost  ansible_connection=local

ansible_provision.yml #

어떤 작업을 할지 하나씩 명시한 파일이다. 태스크마다 이름이 정해져 있고, 어떤 명령들이 실행되는지 확인해보면 좋을것같다.

1. 전체 호스트(로컬호스트)를 대상으로 파일을 패키지를 설치한다. #

 1- hosts: all
 2  tasks:
 3    - name: ensure development tools are at the latest version
 4      become: yes                 # sudo 권한이 필요한 모듈
 5      apt:
 6        name:
 7          - build-essential
 8          - llvm-14-dev
 9          - lld-14
10          - clang-14
11          - nasm
12          - acpica-tools
13          - uuid-dev
14          - qemu-system-x86
15          - qemu-utils
16          - xauth
17          - unzip
18          - python3-distutils
19          - ca-certificates
20        state: latest             # 무조건 최신버전으로 설치 
21        install_recommends: no    # 패키지 설치중 추천으로 표시된 것은 설치하지 않음

2. 패키지 리스트에 qemu-system-gui가 있는지 체크하고 없으면 설치한다. #

command로 qemu-system-gui 패키지를 설치할 수 있는지 확인하고 qemu_gui_result 에 결과를 저장한다.

만약 qemu_gui_result.rc(리턴코드) 가 0 이라면 위 태스크의 커맨드로 결과가 정상적으로 나왔다는거(qemu-system-gui 패키지가 검색됨)고 설치할 수 있는 상태이기 때문에 apt로 설치한다.

 1    - name: check whether qemu-system-gui exists
 2      command: dpkg-query --show qemu-system-gui
 3      failed_when: False            # 이 작업이 에러코드를 반환해도 실패하지 않음
 4      changed_when: False           # 이 작업은 상태를 변경하지 않음
 5      register: qemu_gui_result     # 태스크 결과를 이 변수에 저장한다. 
 6
 7    - name: install qemu gui package if exists
 8      become: yes
 9      apt: name=qemu-system-gui state=latest install_recommends=no
10      when: qemu_gui_result.rc == 0

확인 명령어를 실행해봄 f6d70a80-aeea-4e7d-91ef-db7ff69f0baa


3. llvm, ld, clang 등의 아이템들을 기본 값 14버전으로 실행되도록 한다. #

alternatives 모듈로 with_items에 있는 것들을 -14 버전으로 연결한다.
14버전을 사용하지 않으면 버전이 올라가면서 추가된 에러들로 인해 빌드가 실패할 수 있다.
/usr/bin/clang -> /usr/bin/clang-14

 1    - name: set llvm 14 as default
 2      become: yes
 3      alternatives:
 4        name: "{{ item }}"
 5        link: "/usr/bin/{{ item }}"
 6        path: "/usr/bin/{{ item }}-14"
 7      with_items:
 8        - llvm-PerfectShuffle
 9        - llvm-ar
10        - llvm-as
11        - llvm-bcanalyzer
12          ...
13        - ld.lld
14        - lld-link
15        - clang
16        - clang++
17        - clang-cpp

4. EDK II(크로스플랫폼 UEFI 펌웨어 개발 환경) 레포 클론하고 빌드 #

1    - name: clone EDK II repository
2      git:
3        repo: "https://github.com/tianocore/edk2.git"
4        dest: "{{ ansible_env.HOME }}/edk2"   # ~/edk2
5        version: "edk2-stable202208"          # git tag
6
7    - name: build EDK II base tools           # make -C {chdir}
8      make: 
9        chdir: "{{ ansible_env.HOME }}/edk2/BaseTools/Source/C"

5. 개발에 사용할 스탠다드 라이브러리 다운로드 #

원격 url에서 라이브러리를 다운로드하고

1    - name: download standard libraries
2      unarchive:
3        src: "https://github.com/uchan-nos/mikanos-build/releases/download/v2.0/x86_64-elf.tar.gz"
4        dest: "{{ ansible_env.HOME }}/osbook/devenv"
5        remote_src: yes     # src가 원격저장소이다. 

6. WSL의 qemu x-server 통신을 위한 설정 #

 1    - name: configure display variable if WSL1
 2      lineinfile:
 3        path: "{{ ansible_env.HOME }}/.profile"
 4        regexp: "^export DISPLAY="
 5        line: "export DISPLAY=:0"
 6      when: "'DISPLAY' not in ansible_env and 'WSLENV' in ansible_env and 'WSL_INTEROP' not in ansible_env"
 7
 8    - name: configure display variable if WSL2
 9      lineinfile:
10        path: "{{ ansible_env.HOME }}/.profile"
11        regexp: "^export DISPLAY="
12        line: "export DISPLAY=$(awk '/nameserver / {print $2; exit}' /etc/resolv.conf 2>/dev/null):0"
13      when: "'DISPLAY' not in ansible_env and 'WSL_INTEROP' in ansible_env"

쉘 스크립트 #

run_qemu.sh #

make_image.sh와 run_image.sh 를 실행시켜주는 스크립트이다.

 1#!/bin/sh -ex
 2
 3if [ $# -lt 1 ]
 4then
 5    echo "Usage: $0 <.efi file> [another file]"
 6    exit 1
 7fi
 8
 9DEVENV_DIR=$(dirname "$0")
10EFI_FILE=$1
11ANOTHER_FILE=$2
12DISK_IMG=./disk.img
13MOUNT_POINT=./mnt
14
15$DEVENV_DIR/make_image.sh $DISK_IMG $MOUNT_POINT $EFI_FILE $ANOTHER_FILE
16$DEVENV_DIR/run_image.sh $DISK_IMG

make_image.sh #

 1#!/bin/sh -ex
 2
 3if [ $# -lt 3 ]
 4then
 5    echo "Usage: $0 <image name> <mount point> <.efi file> [another file]"
 6    exit 1
 7fi
 8
 9DEVENV_DIR=$(dirname "$0")
10DISK_IMG=$1
11MOUNT_POINT=$2
12EFI_FILE=$3
13ANOTHER_FILE=$4
14
15if [ ! -f $EFI_FILE ]
16then
17    echo "No such file: $EFI_FILE"
18    exit 1
19fi
20
21rm -f $DISK_IMG
22# 200M 크기의 이미지를 만들고 fat 형식으로 포맷한다.
23qemu-img create -f raw $DISK_IMG 200M
24mkfs.fat -n 'MIKAN OS' -s 2 -f 2 -R 32 -F 32 $DISK_IMG
25
26# 만든 이미지를 마운트 포인트에 마운트한 후 
27# EFI_FILE을 이미지/EFI/BOOT/BOOTX64.EFI 위치에 복사한다. 
28$DEVENV_DIR/mount_image.sh $DISK_IMG $MOUNT_POINT
29# mkdir -p $MOUNT_POINT
30# sudo mount -o loop $DISK_IMG $MOUNT_POINT
31
32sudo mkdir -p $MOUNT_POINT/EFI/BOOT
33sudo cp $EFI_FILE $MOUNT_POINT/EFI/BOOT/BOOTX64.EFI
34if [ "$ANOTHER_FILE" != "" ]
35then
36    sudo cp $ANOTHER_FILE $MOUNT_POINT/
37fi
38sleep 0.5
39sudo umount $MOUNT_POINT

run_image.sh #

 1#!/bin/sh -ex
 2
 3if [ $# -lt 1 ]
 4then
 5    echo "Usage: $0 <image name>"
 6    exit 1
 7  4fi
 8
 9DEVENV_DIR=$(dirname "$0")
10DISK_IMG=$1
11
12if [ ! -f $DISK_IMG ]
13then
14    echo "No such file: $DISK_IMG"
15    exit 1
16fi
17
18# qemu의 하드웨어를 설정하고, UEFI 모드로 구동해서 EFI(UEFI 실행파일)를 실행시킨다.
19qemu-system-x86_64 \
20    -m 1G \
21# UEFI 펌웨어 코드를 pflash(플래시메모리)에 로드하고 qemu 시스템 부팅시 가장먼저 실행되는 코드가 된다. 
22    -drive if=pflash,format=raw,readonly,file=$DEVENV_DIR/OVMF_CODE.fd \
23# UEFI 환경변수 파일. 부팅순서나 시큐어부트 설정이 저장됨
24    -drive if=pflash,format=raw,file=$DEVENV_DIR/OVMF_VARS.fd \  
25# 디스크 이미지를 ide 첫번째 슬롯(0)에 마운트.
26    -drive if=ide,index=0,media=disk,format=raw,file=$DISK_IMG \
27    -device nec-usb-xhci,id=xhci \         # usb3.0(xHCI) 컨트롤러 추가
28    -device usb-mouse -device usb-kbd \    # usb 마우스 키보드 추가
29    -monitor stdio \                       # 모니터콘솔을 더미널에서 사용
30    $QEMU_OPTS

build_edk2.sh #

 1#!/bin/bash -ex
 2
 3if [ ! -d edk2 ]
 4then
 5  git clone https://github.com/tianocore/edk2.git
 6fi
 7
 8cd edk2
 9
10make -C ./BaseTools/Source/C
11
12source ./edksetup.sh
13
14# ACTIVE_PLATFORM 을 검색 후 
15# 그 줄에서 = 이후 모든 값을 = OvmfPkg/OvmfPkgX64.dsc 로 치환
16sed -i '/ACTIVE_PLATFORM/ s:= .*$:= OvmfPkg/OvmfPkgX64.dsc:' Conf/target.txt
17sed -i '/TARGET_ARCH/ s:= .*$:= X64:' Conf/target.txt
18sed -i '/TOOL_CHAIN_TAG/ s:= .*$:= CLANG38:' Conf/target.txt
19
20sed -i '/CLANG38/ s/-flto//' Conf/tools_def.txt
21
22build

전반적인 개발 흐름 #

  1. 환경세팅 ansible 스크립트로 빌드에 필요한 모든 환경을 세팅한다.

  2. 부트로더 코드를 작성해 개발 후 빌드해둔다.

  3. make_image.sh 에서 빈 이미지를 생성해서 전체를 FAT 형식으로 포맷한다.

  4. 디스크 이미지를 폴더에 마운트한 뒤 빌드한 부트로더 bootx64.efiEFI/BOOT/BOOTX64.EFI 경로에 저장해둔다.

  5. 부트로더를 넣었다면 마운트를 해제한다.

  6. qemu-system-x86_64 를 가상환경용 UEFI 펌웨어(OVMF.fd)를 넣고 실행하고 디스크는 부트이미지로 설정한다.


MikanLoaderPkg 분석 #

MikanLoaderPkg 폴더 내부를 보면 dec, dcs, inf, 소스 파일 등이 있고, 이걸 EDK2로 빌드해서 COFF 바이너리를 만드는 것이다.

1MikanLoaderPkg/
2├── MikanLoaderPkg.dec   ← 패키지 설명 파일 (Package Declaration)
3├── MikanLoaderPkg.dsc   ← 플랫폼 빌드 구성 파일 (Platform Description)
4├── Loader.inf           ← 모듈 구성 파일 (Module Information)
5└── Main.c               ← 실제 코드

inf 파일 #

모듈(UEFI 어플리케이션, 드라이버, 라이브러리)의 메타데이터와 구성정보를 정의한다.
소스코드, 의존라이브러리, GUID, 프로토콜, EntryPoint 등을 지정한다.

 1[Defines]
 2  INF_VERSION    = 0x00010006
 3  BASE_NAME      = Loader
 4  FILE_GUID      = c9d0d202-71e9-11e8-9e52-cfbfd0063fbf
 5  MODULE_TYPE    = UEFI_APPLICATION     # 타입을 정의. UEFI_APPLICATION, DXE_DRIVER
 6  ENTRY_POINT    = UefiMain             # 이름을 마음대로 지정할 수 있다. 
 7
 8[Sources]
 9  Main.c
10
11[Packages]
12  MdePkg/MdePkg.dec                    # 필요한 라이브러리 지정
13
14[LibraryClasses]
15  UefiLib                              # 필요한 라이브러리 클래스 (추상인터페이스)

dec 파일 #

MikanLoaderPkg 라는 패키지를 외부에 알리는 역할을 한다. 외부 모듈이나 패키지가 이 패키지를 참조할때 필요한 정보를 제공한다.
LibraryClasses는 이 패키지에서 export 해줄 라이브러리 클래스를 지정하는 것이기 때문에 dsc에서 가져왔다고 하더라도 내부에서만 사용하면 노출시킬 필요는 없다.

1[Defines]
2  DEC_SPECIFICATION              = 0x00010005
3  PACKAGE_NAME                   = MikanLoaderPkg
4  PACKAGE_GUID                   = 452eae8e-71e9-11e8-a243-df3f1ffdebe1
5  PACKAGE_VERSION                = 0.1
6
7[LibraryClasses]     # 이 패키지에서 외부에 제공할 라이브러리 리스트

dsc 파일 #

프로젝트 전체의 빌드 구성을 정의한다. 이 프로젝트를 빌드할때 임포트할 라이브러리 클래스 등에 대한 정보를 포함한다.

EDK2 에서는 빌드하면 정적 라이브러리를 사용하는 것처럼 하나의 파일에 모든 구현을 담아넣어야 하기 때문에 LibraryClasses 에 지정하게 되고, inf에서 지정한건 인터페이스의 이름을 말하는거고 그걸 실제로 구현(implement)한 코드가 포함된 콘트리트클래스의 경로를 지정하는 것이다.

예를들어 DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf 는 DebugLib 을 BaseDebugLibNull을 사용한다는 거고 이렇게 지정하면 디버그로그가 출력되지 않는다.
DebugLib|MdePkg/Library/BaseDebugLibSerialPort/BaseDebugLibSerialPort.inf 이렇게 지정하면 디버그로그가 시리얼포트로 출력된다.

 1#@range_begin(defines)
 2[Defines]
 3  PLATFORM_NAME                  = MikanLoaderPkg
 4  PLATFORM_GUID                  = d3f11f4e-71e9-11e8-a7e1-33fd4f7d5a3e
 5  PLATFORM_VERSION               = 0.1
 6  DSC_SPECIFICATION              = 0x00010005
 7  OUTPUT_DIRECTORY               = Build/MikanLoader$(ARCH)
 8  SUPPORTED_ARCHITECTURES        = X64
 9  BUILD_TARGETS                  = DEBUG|RELEASE|NOOPT
10#@range_end(defines)
11
12#@range_begin(library_classes)
13[LibraryClasses]                # 이 패키지를 빌드할 때 필수로 구현해야되는 라이브러리의 실제 구현 매핑
14  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
15  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
16#@range_end(library_classes)
17
18  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
19  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
20  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
21  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
22  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
23  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
24  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
25  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
26  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
27
28#@range_begin(components)
29[Components]                      # 빌드할 모듈(.inf) 지정
30  MikanLoaderPkg/Loader.inf

Mac 에서의 환경설정 #

ansible-playbook #

ansible-playbook -K -e "devenv_dir=${DEVENV_DIR}" -i "ansible_inventory" "ansible_provision.yml" 명령으로 DEVENV_DIR 위치를 ansible에 알려주고 실행한다.

빌드할때 llvm.ar이나 lld 같은 명령이 필요한데, llvm 최신 버전에서는 없기 때문에 llvm@14 버전을 설치해서 사용해야한다.

 1# ─── macOS (ARM Mac) 전용 작업 ──────────────────────────────
 2- name: Install required packages via Homebrew (macOS)
 3  when: ansible_os_family == "Darwin"
 4  homebrew:
 5    name:
 6      - nasm
 7      - llvm@14    # 꼭 필요함 
 8      - qemu       # qemu (macOS에서는 별도 qemu-system-gui 없음)
 9      - unzip
10      - gnu-tar
11      - dosfstools
12      - acpica
13    state: present
14
15    # llvm@14 를 기본적으로 사용하도록 설정한다.
16    - name: Configure PATH to use Homebrew LLVM (macOS)
17      when: ansible_os_family == "Darwin"
18      lineinfile:
19        path: "{{ ansible_env.HOME }}/.zprofile"
20        regexp: '^export PATH=.*llvm@14/bin'
21        line: 'export PATH="/opt/homebrew/opt/llvm@14/bin:$PATH"'

실행 스크립트 #

mount_image.sh #

운영체제 입장에서는 disk.img($DISK_IMG)이 그냥 파일일 뿐인데, 블록 디바이스만 폴더에 마운트 할 수 있다.

리눅스에서는 -o loop 를 이용해서 파일을 블록 디바이스로 인식하게 할 수 있지만, 맥에서는 그런 옵션이 지원되지 않아서 hdiutil로 파일을 블록디바이스로 변경한 후 마운트를 진행해야 한다.

 1OS_TYPE=$(uname)
 2
 3if [ "$OS_TYPE" = "Darwin" ]; then
 4    DEVICE=$(hdiutil attach -nomount "$DISK_IMG" | tail -n1 | awk '{print $1}')
 5    if [ -z "$DEVICE" ]; then
 6        echo "Failed to attach disk image via hdiutil."
 7        exit 1
 8    fi
 9    sudo mount -t msdos "$DEVICE" "$MOUNT_POINT"
10    echo "$DEVICE" > "$DEVENV_DIR/.device_name.tmp"
11    # diskutil list 로 잘 마운트됐는지 확인가능 
12else
13    sudo mount -o loop "$DISK_IMG" "$MOUNT_POINT"
14fi

make_image.sh #

마운트한 폴더에 부트로더 이미지를 넣어서 disk.img 파일을 부팅 이미지로 만드는 작업을 하고난 뒤 마운트를 해제해줘야 하는데,
hdiutil을 사용하면 disk.img가 시스템이 블록디바이스로 사용하고 있는 것이기 때문에 lock이 걸리게 되어 qemu에서 사용할 수 없게 된다.

물론 qemu에서 옵션을 줘서 이미지를 변경하지 않도록 설정하고 실행할 수 있지만, 사용하지 않는 리소스를 해제하는게 정석적인 방법일 것이다.

1if [ -f "$DEVENV_DIR/.device_name.tmp" ]; then
2    DEVICE=$(cat "$DEVENV_DIR/.device_name.tmp")
3    sudo hdiutil detach "$DEVICE"
4    rm -f "$DEVENV_DIR/.device_name.tmp"
5fi

패키지 빌드 #

맥에서 UEFI 펌웨어나 어플리케이션, 드라이버를 빌드하기 위해 맞춰줘야 할 것들이 있다.

UEFI 어플리케이션은 -t CLANGDWARF을 지정해야하는데 크로스플랫폼 기반의 clang 컴파일러 + dwarf 디버깅 심볼 환경을 사용해서 빌드하겠다는 의미이다.

UEFI 펌웨어는 초기 부트 단계에서 사용하는 라이브러리(SecMain.inf, CpuExceptionHandlerLib)는 절대주소를 종종 사용하는데 EDK2는 XCODE5로 빌드할때 절대주소기반 COFF를 만드는 설정을 해뒀기 때문에 -t XCODE5 로 지정해서 빌드한다.

  • MdePkg/MdePkg.dsc : UEFI에서 사용하는 각종 라이브러리가 포함되어 있다.
  • OvmfPkg/OvmfPkgX64.dsc : UEFI 펌웨어의 구현체인 OVMF 패키지이다. X64 타겟으로 만들어진 펌웨어를 사용해야 한다.
 1cd edk2 
 2source ./edksetup.sh
 3ln -s ~/Project/mikanos/MikanLoaderPkg ./
 4build -p MikanLoaderPkg/MikanLoaderPkg.dsc -a X64 -t CLANGDWARF -b RELEASE   # 의존성은 같이 자동으로 빌드된다. 
 5cp Build/MikanLoaderX64/RELEASE_CLANGDWARF/X64/Loader.efi ~/Project/
 6
 7build -p OvmfPkg/OvmfPkgX64.dsc -a X64 -t XCODE5 -b RELEASE     # OVMF는 XCODE5로 빌드하면 된다. 
 8cp Build/OvmfX64/RELEASE_XCODE5/FV/OVMF_* ../
 9
10cd ~/Project/edk2
11./run_qemu.sh ../../Loader.efi

라이브러리 클래스 지정 #

빌드할때 dsc에 연결되지 않은 라이브러리 클래스 구현체(RegisterFilterLib)들이 있어서 에러가 발생한다.
dsc의 LibraryClasses 에 실제 구현체를 추가해줘야 한다.

1(...): error 4000: Instance of library class [RegisterFilterLib] is not found
2
3$ find . -name "RegisterFilterLib*.inf"
4./MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf
5
6$ vi MikanLoaderPkg/MikanLoaderPkg.dsc
7[LibraryClasses]
8  RegisterFilterLib|MdePkg/Library/RegisterFilterLibNull/RegisterFilterLibNull.inf

mtoc (XCODE5) #

이 도구도 CLANGDWARF를 사용하면 어차피 크로스플랫폼을 대상으로 만들어진 툴체인이기 때문에 필요없어진다.

Mach-O → COFF 변환을 해주는 도구이며, 크로스플랫폼 빌드를 위해 사용되는데 edk2의 설정에서는 mtoc를 /usr/local/bin에서 찾지만 실리콘 맥에서는 homebrew에서 설치하면 /opt/homebrew/bin 폴더에 설치된다.

1$ which mtoc
2/opt/homebrew/bin/mtoc
3
4$ vi Conf/tools_def.txt
5
6# *_XCODE5_*_MTOC_PATH      = /usr/local/bin/mtoc
7*_XCODE5_*_MTOC_PATH      = /opt/homebrew/bin/mtoc

빌드 에러 예외처리 (XCODE5) #

XCODE5를 사용할때만 에러가 발생하는 것으로 보이고, 최신버전 + CLANGDWARF 에서는 발생하지 않기 때문에 신경쓰지 않아도된다.

clang-14 에서는 발생하지 않는 에러들이 clang-16 오면서 에러로 인지되는 것들이 있는 것 같다. edk2는 -Werror 옵션으로 빌드하기 때문에 warning을 error로 처리해서 빌드가 실패되는 문제가 발생한다.

XCODE5_X64_CC_FLAGS 에 unused-but-set 시리즈의 에러를 무시하도록 옵션을 추가해야한다. (-Werror 는 남겨두도록 하자)

1$ vi Conf/tools_def.txt
2
3DEBUG_XCODE5_X64_CC_FLAGS   = ... -Wno-unused-but-set-variable -Wno-unused-but-set-parameter
4RELEASE_XCODE5_X64_CC_FLAGS   = ... -Wno-unused-but-set-variable -Wno-unused-but-set-parameter

Openssl Availability.h 문제 (XCODE5) #

https://edk2.groups.io/g/devel/topic/90113674
edk2 커뮤니티에서도 동일한 문제로 질문이 올라온게 있었다. OpenSSL을 컴파일할때 Apple에서 제공하는 랜덤값 생성 API를 사용하지 않고 다른 난수생성 API를 사용하도록 지정하는 옵션을 추가해야한다.

1$ vi CryptoPkg/Library/OpensslLib/OpensslLibCrypto.inf
2
3XCODE:*_*_X64_CC_FLAGS    = ... -DOPENSSL_NO_APPLE_CRYPTO_RANDOM

실행 테스트 #

이정도 수정 했으면, 다시 맨위의 명령을 사용해서 DEBUG, RELEASE 빌드 둘다 성공하는 것을 볼 수 있다.

1$ build -p OvmfPkg/OvmfPkgX64.dsc -a X64 -t XCODE5 -b DEBUG
2$ cd ../
3$ cp edk2/Build/OvmfX64/DEBUG_XCODE5/FV/OVMF_* .
4$ ./run_qemu.sh ../../hello.efi

여러 부팅 디스크를 찾다가 HARDDISK (Boot0002) 에서 부트로더를 찾고 실행해서 Hello World! 문자열이 표시된 것을 확인할 수 있다.

6a3eb9a1-b5ae-49d4-a2e1-009b789a35d0

OVMF_VARS.fd 를 수정해서 화면 크기나 최초 부팅 위치를 변경할 수 있지만 그건 나중에 알아보자..


추가 환경세팅 #

WSL에서 usb 연결하기 -> 잘안됨 #

  1. usbipd 설치 github에서 msi 파일을 설치할 수 있다.
    https://github.com/dorssel/usbipd-win/releases

  2. usb 상태 확인 및 공유

 1C:\Users\kdh>usbipd list
 25-4    0781:5591  USB 대용량 저장 장치                    Not shared
 3
 4# usb 공유 명령. 경고가 발생하면 force로 연결해보자. 
 5PS C:\Users\kdh> usbipd bind --busid 5-4 --force
 6
 7# 여기에서 usb 상태가 Shared 로 변경됨 
 85-4    0781:5591  USB 대용량 저장 장치                    Shared
 9
10# usb 를 wsl에 연결
11PS C:\Users\kdh> usbipd attach --wsl --busid 5-4
12usbipd: info: Using WSL distribution 'Ubuntu-22.04' to attach; the device will be available in all WSL 2 distributions.
13usbipd: info: Detected networking mode 'nat'.
14usbipd: info: Using IP address 172.24.16.1 to reach the host.
15
16# usb 상태가 Attached 로 변경됨
175-4    0781:5591  USB 대용량 저장 장치                    Attached
18
19PS C:\Users\kdh> usbipd detach --busid 5-4
20PS C:\Users\kdh> usbipd unbind --busid 5-4
  1. WSL(ubuntu)에서 확인 및 사용하기
1kdh@DESKTOP-MHEA7GE:~$ lsusb
2Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
3Bus 001 Device 003: ID 0781:5591 SanDisk Corp. Ultra Flair
4Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
5
6# Bus 001, Device 003 이라 이렇게 접근할 수 있다. 
7kdh@DESKTOP-MHEA7GE:~$ ls -ld /dev/bus/usb/001/003
8crw-rw-r-- 1 root root 189, 2 Feb 14 00:54 /dev/bus/usb/001/003
comments powered by Disqus