UEFI HTTP 부팅 전용: GRUB2는 신비한 HTTP 프록시 "UefiHttpBoot/1.0"을 사용하여 initrd를 로드하지만 큰 initrd를 완전히 읽을 수 없습니다.

UEFI HTTP 부팅 전용: GRUB2는 신비한 HTTP 프록시 "UefiHttpBoot/1.0"을 사용하여 initrd를 로드하지만 큰 initrd를 완전히 읽을 수 없습니다.

내가 GRUB를 사용하는 이유는 다음과 같습니다.UEFI PXE 부팅또는UEFI HTTP 부팅Linux Live 운영 체제(Ubuntu Focal, Bionic 또는 Jammy에서 제작). 저는 grub.cfg둘 다 같은 방법을 사용했습니다. UEFI PXE 부팅에서는 제대로 작동하지만 UEFI HTTP 부팅 중에는 실패합니다.

그러다가 누가 어느 단계에서 http 액세스를 했는지에 대한 흥미로운 사실을 발견했습니다.

  • UEFI PXE가 시작되면 HTTP 액세스 로그는 다음과 같습니다.
"GET /liveos/focal/kernel HTTP/1.1" 200 11780639 "-" "GRUB 2.06-2ubuntu14.1"
"GET /liveos/focal/initrd HTTP/1.1" 200 89168508 "-" "GRUB 2.06-2ubuntu14.1"
"GET /liveos/focal/squashfs HTTP/1.1" 200 388669747 "-" "Wget"

따라서 GRUB는 커널과 initrd를 로드한 다음 initrd는 wget을 사용하여 squashfs를 로드합니다. 글쎄요, 말이 되네요.

  • 그러나 UEFI HTTP 시작 시 HTTP 액세스 로그는 다음과 같습니다.
"GET /bootx64.efi HTTP/1.1" 200 955941 "-" "UefiHttpBoot/1.0"
"GET /grubx64.efi HTTP/1.1" 200 1493150 "-" "UefiHttpBoot/1.0"

"GET /grub/grub.cfg HTTP/1.1" 200 1823 "-" "UefiHttpBoot/1.0"

"GET /liveos/focal/kernel HTTP/1.1" 200 11780639 "-" "UefiHttpBoot/1.0"
"GET /liveos/focal/initrd HTTP/1.1" 200 449680 "-" "UefiHttpBoot/1.0"

따라서 GRUB는 http 프록시 "UefiHttpBoot/1.0"을 사용하여 커널과 initrd를 로드합니다. 불행하게도 액세스 로그에는 서버가 UEFI HTTP 부팅 클라이언트에 대한 전체 크기 데이터로 응답하지 않는 것으로 표시됩니다. 전체 크기 응답은 다음과 같아야 89168508하지만 지금은 그냥 그렇습니다 449680.

질문은 다음과 같습니다.

  • http 프록시 "UefiHttpBoot/1.0"은 어디에 구현되어 있나요? 펌웨어에서?
  • GRUB2가 UEFI PXE 부팅과 동일한 논리를 사용하지 않는 이유는 무엇입니까? 즉, 자체 http 프록시를 사용하여 initrd를 가져오는 것입니까?

편집: UEFI HTTP 부팅에서 처음부터 bootx64.efi를 로드하는 것은 http 프록시 "UefiHttpBoot/1.0"인 것처럼 보입니다. "UefiHttpBoot/1.0"은 펌웨어에서 나와야 합니다.그러나 실행 제어를 bootx64.efi(GRUB2)에 전달해야 하며 GRUB2는 자체 http 도구를 사용하여 initrd를 읽어야 하지만 그렇지 않습니다. 이유는 무엇입니까? UEFI HTTP 부팅은 어떻게든 Linux 부팅 프로토콜에서 작동하는 것으로 보입니다.

편집: 추가 정보:

  [    1.947449] Unpacking initramfs...
  [    1.950954] Initramfs unpacking failed: junk in compressed archive

  or 
  [    1.943484] Unpacking initramfs...
  [    1.946991] Initramfs unpacking failed: broken padding

grub.cfg는 다음과 같습니다. (UEFI PXE 부팅에서는 제대로 작동한다는 점을 기억하세요):

  set default=0
  set timeout=1
  menuentry boot_liveos {
    linux (http,100.0.0.101)/liveos/focal/kernel nomodeset ro root=squash:http://100.0.0.101/liveos/focal/squashfs ip=::::hostname:BOOTIF ip6=off overlayroot=tmpfs overlayroot_cfgdisk=disabled apparmor=0 ds=nocloud console=tty0 console=ttyS1,115200 BOOTIF=01-${net_default_mac}
    initrd (http,100.0.0.101)/liveos/focal/initrd
  }

PS 뿐만 아니라 , focal역시 같은 문제입니다. 또한 20200320.0에서 20230222.0까지 다양한 GRUB2 버전을 시도했지만 모두 동일한 문제가 있었습니다.bionicjammy

문제는 "UefiHttpBoot/1.0"에 문제가 있어 빅데이터를 수용할 수 없다는 것입니다.

편집: 이것이 펌웨어 문제인 경우 grub에 http 파일 자체를 로드하도록 지시하는 방법이 있습니까?

편집: 문제를 계속 조사하고 있습니다.

$ strings bootx64.efi |grep UefiHttpBoot
UefiHttpBoot/1.0
$ strings grubx64.efi |grep UefiHttpBoot
UefiHttpBoot/1.0

GRUB2 바이너리에는 http 프록시 문자열 "UefiHttpBoot/1.0"이 포함되어 있으므로 GRUB2가 http 액세스에 대해 동일한 프록시 문자열을 계속 사용한다는 점을 제외하면 실행 제어가 GRUB2로 전달되어야 합니다. 소스코드를 확인해 봤습니다http://archive.ubuntu.com/ubuntu/pool/main/g/grub2-unsigned/grub2-unsigned_2.06.orig.tar.xz(상위 페이지는https://packages.ubuntu.com/focus-updates/grub-efi-amd64) 그러나 서로게이트 문자열을 찾을 수 없지만 상수 var가 발견되었습니다. PACKAGE_STRING아마도 바이너리를 빌드할 때 정의되었을 것입니다.

http_establish (struct grub_file *file, grub_off_t offset, int initial)
{
  http_data_t data = file->data;
  grub_uint8_t *ptr;
  int i;
  struct grub_net_buff *nb;
  grub_err_t err;

  nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
               + sizeof ("GET ") - 1
               + grub_strlen (data->filename)
               + sizeof (" HTTP/1.1\r\nHost: ") - 1
               + grub_strlen (file->device->net->server)
               + sizeof ("\r\nUser-Agent: " PACKAGE_STRING
                     "\r\n") - 1
               + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX"
                     "-\r\n\r\n"));
  if (!nb)
    return grub_errno;

GRUB2용 debian 패치 소스에서 http 프록시 문자열을 찾으세요:http://archive.ubuntu.com/ubuntu/pool/main/g/grub2-unsigned/grub2-unsigned_2.06-2ubuntu14.1.debian.tar.xz는 efi 펌웨어 기능을 사용하기 위해 일부 http 관련 기능을 대체하는 패치 파일 suse-add-support-for-UEFI-network-protocols.patch입니다.

+static grub_err_t
+efihttp_request (grub_efi_http_t *http, char *server, char *name, int use_https, int headeronly, grub_off_t *file_size)
+{
+  grub_efi_http_request_data_t request_data;
+  grub_efi_http_message_t request_message;
+  grub_efi_http_token_t request_token;
+  grub_efi_http_response_data_t response_data;
+  grub_efi_http_message_t response_message;
+  grub_efi_http_token_t response_token;
+  grub_efi_http_header_t request_headers[3];
+
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b = grub_efi_system_table->boot_services;
+  char *url = NULL;
+
+  request_headers[0].field_name = (grub_efi_char8_t *)"Host";
+  request_headers[0].field_value = (grub_efi_char8_t *)server;
+  request_headers[1].field_name = (grub_efi_char8_t *)"Accept";
+  request_headers[1].field_value = (grub_efi_char8_t *)"*/*";
+  request_headers[2].field_name = (grub_efi_char8_t *)"User-Agent";
+  request_headers[2].field_value = (grub_efi_char8_t *)"UefiHttpBoot/1.0";
+
...

+  /* request token */
+  request_token.event = NULL;
+  request_token.status = GRUB_EFI_NOT_READY;
+  request_token.message = &request_message;
+
+  request_callback_done = 0;
+  status = efi_call_5 (b->create_event,
+                       GRUB_EFI_EVT_NOTIFY_SIGNAL,
+                       GRUB_EFI_TPL_CALLBACK,
+                       grub_efi_http_request_callback,
+                       NULL,
+                       &request_token.event);
+
+ 
...

편집: 거의 다 왔습니다. 내가 사용하는 GRUB2는 기본 efi 펌웨어 기능을 사용하여 http 파일에 액세스하도록 최적화되어 있습니다. 훌륭해 보이지만 몇 가지 단점이 있습니다.

  • URL 쿼리 문자열을 매우 잘 지원하는 것 같습니다.
  • 빅데이터를 잘 처리하지 못합니다. GRUB2 개발자에게 이 내용을 보고하겠습니다. TCP 재전송과 창 크기 조정이 많이 발생하는 것으로 나타났습니다.

답변1

"UefiHttpBoot/1.0" HTTP 클라이언트는 실제로 UEFI 펌웨어(UEFI 사양 버전 2.5 이상)에서 구현됩니다.

UEFI PXE를 사용하여 부팅할 때 펌웨어는 bootx64.efiTFTP를 사용하여 초기 파일을 로드합니다. 이는 펌웨어가 자체적으로 수행할 수 있는 유일한 네트워크 파일 전송 프로토콜일 수 있기 때문입니다. 그래서 GRUB~ 해야 하다이 경우 HTTP를 사용하려면 자체 HTTP 클라이언트 구현을 가져오세요.

그러나 grepGRUB 소스 트리를 빠르게 살펴본 후에 UEFI HTTP 프로토콜에 대한 참조를 찾을 수 없었습니다. GRUB용 UEFI 네트워크 드라이버,grub-core/net/drivers/efi/efinet.cUEFI 사양의 이전 버전 펌웨어에서 제공하는 단순 네트워크 프로토콜에만 의존합니다.

따라서 UEFI HTTP로 부팅할 때 GRUB는 자신이 네트워크를 사용하고 있다는 사실을 전혀 모르는 것 같습니다. UEFI 사양에 따르면 HTTP 부팅을 통해 펌웨어는 UEFI LoadFile 프로토콜을 지원하는 UEFI 장치 핸들을 제공하므로 GRUB는 이를 사용하여 파일이 디스크에서 로드된 것처럼 파일을 로드할 수 있습니다. 물론 GRUB할 수 있다장치 핸들을 추가로 조사하여 실제로 네트워크 장치인지 확인하십시오.하지만 꼭 그럴 필요는 없어주요 작업을 완료합니다.

이는 또한 UEFI 부팅이 initrd를 로드할 수 없는 것이 펌웨어 문제임이 틀림없다는 것을 분명히 나타냅니다.

답변2

내가 직접 대답해 보자.

해결책은 단어가 포함되지 않은 오래된 grubx64.efi를 찾는 것이었습니다 UefiHttpBoot. 운 좋게도 20200320.0로컬 기록에서 버전을 찾았고 제대로 작동했습니다. (더 이상 우분투 저장소에 존재하지 않거나 인터넷에서 구현되지 않은 것 같습니다.) 해당 bootx64.efi에는 여전히 word 가 포함되어 있지만 UefiHttpBoot이것은 단지 shim이므로 중요하지 않으며 grubx64.efi를 로드한 다음 권한을 제어합니다. 그것으로 전달됩니다.

http 액세스는 다음과 같습니다.

"GET /bootx64.efi HTTP/1.1" 200 1335102 "-" "UefiHttpBoot/1.0"
"GET /grubx64.efi HTTP/1.1" 200 1164950 "-" "UefiHttpBoot/1.0"
...
"GET /grub/grub.cfg HTTP/1.1" 200 737 "-" "GRUB 2.02-2ubuntu8.15"
...
"GET /liveos/focal/kernel HTTP/1.1" 200 11780639 "-" "GRUB 2.02-2ubuntu8.15"
"GET /liveos/focal/initrd HTTP/1.1" 200 89168508 "-" "GRUB 2.02-2ubuntu8.15"
"GET /liveos/focal/squashfs HTTP/1.1" 200 388669747 "-" "Wget"
...

관련 정보