From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=2a00:1450:400c:c09::241; helo=mail-wm0-x241.google.com; envelope-from=ard.biesheuvel@linaro.org; receiver=edk2-devel@lists.01.org Received: from mail-wm0-x241.google.com (mail-wm0-x241.google.com [IPv6:2a00:1450:400c:c09::241]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 760DC202E60E2 for ; Sat, 21 Oct 2017 06:07:30 -0700 (PDT) Received: by mail-wm0-x241.google.com with SMTP id t69so2212250wmt.2 for ; Sat, 21 Oct 2017 06:11:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=1M/gAHmakdVs+N423Iz4DcS+wAoNEHJXbAZTYhaHgPw=; b=GanGnhvHK+CRbt4gnYFwAttbcBQaJsqBOtF/DLvzFVOr1YdQ62A6XSMCVIbQ/GhCTT +22v5ChneWWNyRXqR1gXR8VyY12V/3Po6HmhUeRCdapICEq58WtHbMVDk06Fc9fyvL+g i4HALQ2R7N4GySosVoD0Gr0rKjkk6vRuMYnxc= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=1M/gAHmakdVs+N423Iz4DcS+wAoNEHJXbAZTYhaHgPw=; b=rihzotDkL8VgzzmA9C+V4IDdATLWNljQo+TV7/y367/CcBrrOtCAsji/oyBjFW3Fi7 3EGUbm59A1XS/2vFL7Z4s24cAT1k0BYT6ItwRAQfNMcWAHc2X3eJKsXDFx+gg30sz1pB Mk3t8bErIbg5/Rsk1/ZuIP+32xT4BuVrNMdHsWqQJ9Tpu1mafiL1zY9KeD0XMklNC7ZC yTKdcZi+rr9JHJKaJlh4Q5NnbqVz6Zx4y2nGMOdSlnyN0jVUG6gjQC5qHTJ0cakcNsdh d5qtYWaHMiUfwmdEAu+/uNiZ52ibgHShH0kVouCTmXJJgVeP2uHycdHDoQy7V1WdqBZD 91jg== X-Gm-Message-State: AMCzsaVcoPwndr5x73MytfsiPW2mKH0H9DPU+ocuu5pn2Es5pwKLMcPs K5Qm3TZLsZyAZ6GDY6XL3TZYuMgAwjw= X-Google-Smtp-Source: ABhQp+T385n8R5FhhW+oqWanyiaSDi6LfkNMdZmIZg+xTNm6yU8/WrXfHyRYF0pa5GxMYLbzAb72Ww== X-Received: by 10.28.210.5 with SMTP id j5mr1334811wmg.15.1508591468207; Sat, 21 Oct 2017 06:11:08 -0700 (PDT) Received: from localhost.localdomain ([154.145.244.244]) by smtp.gmail.com with ESMTPSA id x19sm4233369wrd.10.2017.10.21.06.11.04 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 21 Oct 2017 06:11:06 -0700 (PDT) From: Ard Biesheuvel To: edk2-devel@lists.01.org, leif.lindholm@linaro.org Cc: lersek@redhat.com, daniel.thompson@linaro.org, Ard Biesheuvel Date: Sat, 21 Oct 2017 14:10:49 +0100 Message-Id: <20171021131049.23844-1-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.11.0 Subject: [RFC PATCH] ArmPkg: add driver to add distro installer HTTP boot options X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 21 Oct 2017 13:07:30 -0000 To make it easier for power users to provision a 'desktop' system [as opposed to a VM or server] from scratch, introduce a driver that adds boot options to the boot menu that can launch network installers over HTTP straight off the Internet. Currently, this only supports the 'mini.iso' style netboot installers that Debian/Ubuntu provide: larger images that need to be mounted by the installer when running under the Linux kernel are only supported on ACPI systems with support for the NFIT table, and this was enabled only recently (Linux v4.14) for arm64. For DT boot, there is currently no way at all to expose ramdisks created by UEFI as block devices in Linux. Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Ard Biesheuvel --- Posted as an RFC because: a) Where does this belong? Surely not in ArmPkg but I had to put it somewhere b) Currently, the way the options are created results in them taking priority if no real boot options were set (i.e., for GRUB). c) Is there a use case for downloading 300-500 MB installers off the Internet for a one shot installation? Or should we just stick to the mini.iso flavors. d) Did I miss any distros we may care about? ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.c | 228 ++++++++++++++++++++ ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.inf | 42 ++++ 2 files changed, 270 insertions(+) diff --git a/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.c b/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.c new file mode 100644 index 000000000000..7c5934935924 --- /dev/null +++ b/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.c @@ -0,0 +1,228 @@ +/** @file + * + * Copyright (c) 2016-2017, Linaro Limited. All rights reserved. + * + * This program and the accompanying materials are licensed and made available + * under the terms and conditions of the BSD License which accompanies this + * distribution. The full text of the license may be found at + * http://opensource.org/licenses/bsd-license.php + * + * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct { + CONST CHAR16 *Name; + CONST CHAR8 *Uri; +} OS_INSTALLER_IMAGE; + +STATIC CONST OS_INSTALLER_IMAGE mOsInstallers[] = { + { L"Install Debian Stretch over HTTP", + "http://ftp.us.debian.org/debian/dists/stretch/main/installer-arm64/current/images/netboot/mini.iso" }, + + { L"Install Ubuntu 17.10 (Artful) over HTTP", + "http://ports.ubuntu.com/ubuntu-ports/dists/artful/main/installer-arm64/current/images/netboot/mini.iso" }, + +// +// The links below refer to 300-500 MB netboot images that need to be exposed to +// the OS via a ramdisk after the OS loader boots the installer from it. +// Currently, this requires ACPI/NFIT support, which was only enabled for arm64 +// in Linux in version v4.14. For DT boot, there is currently no solution for +// this. +// +// { L"Install openSUSE Tumbleweed over HTTP", +// "http://download.opensuse.org/ports/aarch64/factory/iso/openSUSE-Tumbleweed-NET-aarch64-Current.iso" }, +// +// { L"Install Fedora Server 26 over HTTP", +// "http://download.fedoraproject.org/pub/fedora-secondary/releases/26/Server/aarch64/iso/Fedora-Server-netinst-aarch64-26-1.5.iso" }, +// +// { L"Install Centos 7 over HTTP", +// "http://mirror.centos.org/altarch/7/isos/aarch64/CentOS-7-aarch64-NetInstall.iso" } +// +}; + +STATIC EFI_EVENT mRegisterProtocolEvent; +STATIC VOID *mRegistration; + +STATIC +EFI_STATUS +CreateOsInstallerBootOptions ( + IN EFI_HANDLE Handle + ) +{ + EFI_DEVICE_PATH_PROTOCOL *ParentDevicePath; + EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath; + EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; + EFI_STATUS Status; + UINTN Idx; + EFI_DEV_PATH *Node; + UINTN Length; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; + UINTN BootOptionCount; + INTN OptionIndex; + + BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, + LoadOptionTypeBoot); + ASSERT (BootOptions != NULL); + + Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, + (VOID **)&ParentDevicePath); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "%a: gBS->HandleProtocol returned %r\n", + __FUNCTION__, Status)); + return Status; + } + + for (Idx = 0; Idx < ARRAY_SIZE (mOsInstallers); Idx++) { + Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH)); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeBootOptions; + } + Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH; + Node->Ipv4.Header.SubType = MSG_IPv4_DP; + SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH)); + + TmpDevicePath = AppendDevicePathNode (ParentDevicePath, + (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + if (TmpDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeBootOptions; + } + + // + // Update the URI node with the input boot file URI. + // + Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + + AsciiStrSize (mOsInstallers[Idx].Uri); + Node = AllocatePool (Length); + if (Node == NULL) { + Status = EFI_OUT_OF_RESOURCES; + FreePool (TmpDevicePath); + goto FreeBootOptions; + } + Node->DevPath.Type = MESSAGING_DEVICE_PATH; + Node->DevPath.SubType = MSG_URI_DP; + SetDevicePathNodeLength (Node, Length); + CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), + mOsInstallers[Idx].Uri, AsciiStrSize (mOsInstallers[Idx].Uri)); + NewDevicePath = AppendDevicePathNode (TmpDevicePath, + (EFI_DEVICE_PATH_PROTOCOL*) Node); + FreePool (Node); + FreePool (TmpDevicePath); + if (NewDevicePath == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto FreeBootOptions; + } + + // + // Create a new load option. + // + Status = EfiBootManagerInitializeLoadOption (&NewOption, + LoadOptionNumberUnassigned, LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, (CHAR16 *)mOsInstallers[Idx].Name, + NewDevicePath, NULL, 0); + ASSERT_EFI_ERROR (Status); + + OptionIndex = EfiBootManagerFindLoadOption (&NewOption, BootOptions, + BootOptionCount); + if (OptionIndex == -1) { + // + // Add the new load option if it did not exist already + // + EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1); + } + EfiBootManagerFreeLoadOption (&NewOption); + FreePool (NewDevicePath); + } + +FreeBootOptions: + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); + return Status; +} + +STATIC +BOOLEAN +MediaDisconnected ( + IN EFI_HANDLE Handle + ) +{ + EFI_SIMPLE_NETWORK_PROTOCOL *Snp; + EFI_STATUS Status; + + Status = gBS->HandleProtocol (Handle, &gEfiSimpleNetworkProtocolGuid, + (VOID **)&Snp); + if (EFI_ERROR (Status) || !Snp->Mode->MediaPresentSupported) { + return FALSE; + } + + Snp->GetStatus (Snp, NULL, NULL); + + return !Snp->Mode->MediaPresent; +} + +STATIC +VOID +OnRegisterProtocol ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + UINTN HandleCount; + EFI_HANDLE *HandleBuffer; + UINTN Idx; + + Status = gBS->LocateHandleBuffer (ByRegisterNotify, + &gEfiHttpServiceBindingProtocolGuid, mRegistration, + &HandleCount, &HandleBuffer); + if (EFI_ERROR (Status)) { + return; + } + + for (Idx = 0; Idx < HandleCount; Idx++) { + if (MediaDisconnected (HandleBuffer[Idx])) { + continue; + } + + CreateOsInstallerBootOptions (HandleBuffer[Idx]); + + // + // Create the options only a single time - we take care to only install + // them for a network interface that has a link, and we should try not to + // confuse the user by having 10 identical options when the system has 10 + // network interfaces. + // + gBS->CloseEvent (Event); + break; + } + FreePool (HandleBuffer); +} + +EFI_STATUS +EFIAPI +OsInstallerMenuDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + mRegisterProtocolEvent = EfiCreateProtocolNotifyEvent ( + &gEfiHttpServiceBindingProtocolGuid, TPL_CALLBACK, + OnRegisterProtocol, NULL, &mRegistration); + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.inf b/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.inf new file mode 100644 index 000000000000..9bc5a81a78a1 --- /dev/null +++ b/ArmPkg/Drivers/OsInstallerMenuDxe/OsInstallerMenuDxe.inf @@ -0,0 +1,42 @@ +# +# Copyright (c) 2016-2017, Linaro Limited. All rights reserved. +# +# This program and the accompanying materials are licensed and made available +# under the terms and conditions of the BSD License which accompanies this +# distribution. The full text of the license may be found at +# http://opensource.org/licenses/bsd-license.php +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +# + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = OsInstallerMenuDxe + FILE_GUID = 4def5019-3233-4925-98ef-db5c60b2aec4 + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 0.1 + ENTRY_POINT = OsInstallerMenuDxeEntryPoint + +[Sources] + OsInstallerMenuDxe.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + MemoryAllocationLib + UefiBootManagerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiHttpServiceBindingProtocolGuid + gEfiDevicePathProtocolGuid + gEfiSimpleNetworkProtocolGuid -- 2.11.0