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をロードできないことがファームウェアの問題であることは間違いないことを明らかに示しています。

おすすめ記事