From nobody Mon Nov 25 04:52:34 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1717259308; cv=none; d=zohomail.com; s=zohoarc; b=Buecoht2xhbtafuv4PRIFbdhqyO2BaK4SZPqouZu4TQjG8e/8hd0YQNBnOBbkuAqkRrYk6xBnXmZIj3SLHvsYXmLJf+P59P7LgVzF1l/U1ooVsxMYQSrwQdt/oy3DTWllqjtYeNyowKJzm7PxxdHj9GRtbrIAUiPTLlft5TRTNI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1717259308; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=vC7zn+nZqIcqq3SeNDeg1Kc3bjDK2LHdbXUivn3Xwgo=; b=M6bQJO3gqKCLyQHkpLpfgizHksKzU21+dHdGLuFJQ8B3utpYAwrYzbhfUYrQxc7NU9nkFz8M6FSLQM/pD+29R3f4gMTH1ChkAtPCrSgX9BSMuW357Xzm1d0DC7+8YJKhi0HhoV/EGPkJa0vRdisU5ix/Wm65bFlXSzizO50Tbng= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 171725930805544.84143644316839; Sat, 1 Jun 2024 09:28:28 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sDRZl-0007H1-LE; Sat, 01 Jun 2024 12:27:26 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sDRZk-0007Gp-6z for qemu-devel@nongnu.org; Sat, 01 Jun 2024 12:27:24 -0400 Received: from mail-pl1-x635.google.com ([2607:f8b0:4864:20::635]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sDRZh-00087r-Br for qemu-devel@nongnu.org; Sat, 01 Jun 2024 12:27:23 -0400 Received: by mail-pl1-x635.google.com with SMTP id d9443c01a7336-1f44b42e9a6so24036195ad.0 for ; Sat, 01 Jun 2024 09:27:21 -0700 (PDT) Received: from localhost.localdomain ([103.103.35.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2c1c27ad3cesm3285036a91.7.2024.06.01.09.27.12 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 01 Jun 2024 09:27:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1717259238; x=1717864038; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=vC7zn+nZqIcqq3SeNDeg1Kc3bjDK2LHdbXUivn3Xwgo=; b=PMyc+X652yzZLBqF99B2cLRy3DWMs6870UpS+yXTzs4kthIRo7gb3HiJhLxXJunJzF xXKKRnzH4g3jHHl2GykOTDGEpmnjt61yB8GemW9sQdzYV+Om89qJwOP1+tuRYkKE2H6R B4yYPNo3+GihTwSO/gRvmtHDdR5CQrJ53zS8lxg7nQBjuaitJD4ibgCgsftsTIJekY+3 6UlKbEjtLVS5eGZG9IrKnI3nuTWBLg4wAjTAFgo+lu+YTG+7fgYtKiu2n07zDBjdwI6o Why/SS9vEeAoLv9+pRBSOCWwOY8NFaFDh8R5JqGKuEk87I6q6RJvq2V4xdwrfxu1ut1M 1AXw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717259238; x=1717864038; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=vC7zn+nZqIcqq3SeNDeg1Kc3bjDK2LHdbXUivn3Xwgo=; b=V2i/Y6uwgaP+sw/KNxtzK0TmtvHPp6+ns/GU16C5b5cwE4FVDd89SaVdTjc0qEAH87 YoYnLo9z8olPOogdDTSMVRuwCKF6SX6SK3K0CVxSh5uOZj7XdigLSFOjio6aNAHtr5Zm L6oUkDUpFmM2vUko8GtkyHjvVmp3BS9yqjs/Rk0WvuZxMtUwm7sbUeNvTy79ZXGirIGJ cRdalu0O4rJBmrrovu2mXkWRJ1xVg1cP61v+auRvPvYQZJ7QapeGajVyvcbl+zCerfSX xBlWqVcSSY5Y5GhfHAiUVwltmXGrZYdTEAKO/kuxBrCNReOpdJv13+L8+sowZSufu9Ew IClQ== X-Gm-Message-State: AOJu0YzFWSItUDetvXojOvWt8Ugseh3Cj+eIZF7N8ZYAGSM4ymFSScOO XTXf+MqsF4JxZQXr+KWg52pOs6NEzSqXiFPUdfic69ew4iDbyOpmk6rAXs8n X-Google-Smtp-Source: AGHT+IFNeKzgXWD4h5Fckwz5Q6NHutLcxDV8BFAqqDk7ZwsHSS+fEdKkNVNb6uEie+YQ1w0/XyGaKw== X-Received: by 2002:a17:902:db08:b0:1f3:1200:ceb3 with SMTP id d9443c01a7336-1f6370a0d07mr59208975ad.51.1717259238312; Sat, 01 Jun 2024 09:27:18 -0700 (PDT) From: Dorjoy Chowdhury To: qemu-devel@nongnu.org Cc: graf@amazon.com, agraf@csgraf.de, stefanha@redhat.com, pbonzini@redhat.com, slp@redhat.com, richard.henderson@linaro.org, eduardo@habkost.net, mst@redhat.com, marcel.apfelbaum@gmail.com, berrange@redhat.com, philmd@linaro.org Subject: [PATCH v2 1/2] machine/microvm: support for loading EIF image Date: Sat, 1 Jun 2024 22:26:51 +0600 Message-Id: <20240601162652.55643-2-dorjoychy111@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240601162652.55643-1-dorjoychy111@gmail.com> References: <20240601162652.55643-1-dorjoychy111@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::635; envelope-from=dorjoychy111@gmail.com; helo=mail-pl1-x635.google.com X-Spam_score_int: 15 X-Spam_score: 1.5 X-Spam_bar: + X-Spam_report: (1.5 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1717259309612100009 Content-Type: text/plain; charset="utf-8" An EIF (Enclave Image Format)[1] image is used to boot an AWS nitro enclave[2] virtual machine. The EIF file contains the necessary kernel, cmdline, ramdisk(s) sections to boot. This commit adds support for loading EIF image using the microvm machine code. For microvm to boot from an EIF file, the kernel and ramdisk(s) are extracted into a temporary kernel and a temporary initrd file which are then hooked into the regular x86 boot mechanism along with the extracted cmdline. Although not useful for the microvm machine itself, this is needed as the following commit adds support for a new machine type 'nitro-enclave' which uses the microvm machine type as parent. The code for checking and loading EIF will be put inside a 'nitro-enclave' machine type check in the following commit so that microvm cannot load EIF because it shouldn't. [1] https://github.com/aws/aws-nitro-enclaves-image-format [2] https://aws.amazon.com/ec2/nitro/nitro-enclaves/ Signed-off-by: Dorjoy Chowdhury Reviewed-by: Alexander Graf --- hw/core/eif.c | 486 ++++++++++++++++++++++++++++++++++++++++++++ hw/core/eif.h | 20 ++ hw/core/meson.build | 1 + hw/i386/microvm.c | 134 +++++++++++- 4 files changed, 640 insertions(+), 1 deletion(-) create mode 100644 hw/core/eif.c create mode 100644 hw/core/eif.h diff --git a/hw/core/eif.c b/hw/core/eif.c new file mode 100644 index 0000000000..281ff43ea5 --- /dev/null +++ b/hw/core/eif.c @@ -0,0 +1,486 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "qapi/error.h" +#include /* for crc32 */ + +#include "hw/core/eif.h" + +#define MAX_SECTIONS 32 + +/* members are ordered according to field order in .eif file */ +typedef struct EifHeader { + uint8_t magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] = */ + uint16_t version; + uint16_t flags; + uint64_t default_memory; + uint64_t default_cpus; + uint16_t reserved; + uint16_t section_cnt; + uint64_t section_offsets[MAX_SECTIONS]; + uint64_t section_sizes[MAX_SECTIONS]; + uint32_t unused; + uint32_t eif_crc32; +} QEMU_PACKED EifHeader; + +/* members are ordered according to field order in .eif file */ +typedef struct EifSectionHeader { + /* + * 0 =3D invalid, 1 =3D kernel, 2 =3D cmdline, 3 =3D ramdisk, 4 =3D si= gnature, + * 5 =3D metadata + */ + uint16_t section_type; + uint16_t flags; + uint64_t section_size; +} QEMU_PACKED EifSectionHeader; + +enum EifSectionTypes { + EIF_SECTION_INVALID =3D 0, + EIF_SECTION_KERNEL =3D 1, + EIF_SECTION_CMDLINE =3D 2, + EIF_SECTION_RAMDISK =3D 3, + EIF_SECTION_SIGNATURE =3D 4, + EIF_SECTION_METADATA =3D 5, + EIF_SECTION_MAX =3D 6, +}; + +static const char *section_type_to_string(uint16_t type) +{ + const char *str; + switch (type) { + case EIF_SECTION_INVALID: + str =3D "invalid"; + break; + case EIF_SECTION_KERNEL: + str =3D "kernel"; + break; + case EIF_SECTION_CMDLINE: + str =3D "cmdline"; + break; + case EIF_SECTION_RAMDISK: + str =3D "ramdisk"; + break; + case EIF_SECTION_SIGNATURE: + str =3D "signature"; + break; + case EIF_SECTION_METADATA: + str =3D "metadata"; + break; + default: + str =3D "unknown"; + break; + } + + return str; +} + +static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc, + Error **errp) +{ + size_t got; + size_t header_size =3D sizeof(*header); + + got =3D fread(header, 1, header_size, f); + if (got !=3D header_size) { + error_setg(errp, "Failed to read EIF header"); + return false; + } + + if (memcmp(header->magic, ".eif", 4) !=3D 0) { + error_setg(errp, "Invalid EIF image. Magic mismatch."); + return false; + } + + /* Exclude header->eif_crc32 field from CRC calculation */ + *crc =3D crc32(*crc, (uint8_t *)header, header_size - 4); + + header->version =3D be16_to_cpu(header->version); + header->flags =3D be16_to_cpu(header->flags); + header->default_memory =3D be64_to_cpu(header->default_memory); + header->default_cpus =3D be64_to_cpu(header->default_cpus); + header->reserved =3D be16_to_cpu(header->reserved); + header->section_cnt =3D be16_to_cpu(header->section_cnt); + + for (int i =3D 0; i < MAX_SECTIONS; ++i) { + header->section_offsets[i] =3D be64_to_cpu(header->section_offsets= [i]); + } + + for (int i =3D 0; i < MAX_SECTIONS; ++i) { + header->section_sizes[i] =3D be64_to_cpu(header->section_sizes[i]); + } + + header->unused =3D be32_to_cpu(header->unused); + header->eif_crc32 =3D be32_to_cpu(header->eif_crc32); + return true; +} + +static bool read_eif_section_header(FILE *f, EifSectionHeader *section_hea= der, + uint32_t *crc, Error **errp) +{ + size_t got; + size_t section_header_size =3D sizeof(*section_header); + + got =3D fread(section_header, 1, section_header_size, f); + if (got !=3D section_header_size) { + error_setg(errp, "Failed to read EIF section header"); + return false; + } + + *crc =3D crc32(*crc, (uint8_t *)section_header, section_header_size); + + section_header->section_type =3D be16_to_cpu(section_header->section_t= ype); + section_header->flags =3D be16_to_cpu(section_header->flags); + section_header->section_size =3D be64_to_cpu(section_header->section_s= ize); + return true; +} + +/* + * Upon success, the caller is responsible for unlinking and freeing *tmp_= path. + */ +static bool get_tmp_file(const char *template, char **tmp_path, Error **er= rp) +{ + int tmp_fd; + + *tmp_path =3D NULL; + tmp_fd =3D g_file_open_tmp(template, tmp_path, NULL); + if (tmp_fd < 0 || *tmp_path =3D=3D NULL) { + error_setg(errp, "Failed to create temporary file for template %s", + template); + return false; + } + + close(tmp_fd); + return true; +} + +static void safe_fclose(FILE *f) +{ + if (f) { + fclose(f); + } +} + +static void safe_unlink(char *f) +{ + if (f) { + unlink(f); + } +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing *kerne= l_path + */ +static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, + uint32_t *crc, Error **errp) +{ + size_t got; + FILE *tmp_file =3D NULL; + uint8_t *kernel =3D NULL; + + *kernel_path =3D NULL; + if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { + goto cleanup; + } + + tmp_file =3D fopen(*kernel_path, "wb"); + if (tmp_file =3D=3D NULL) { + error_setg_errno(errp, errno, "Failed to open temporary file %s", + *kernel_path); + goto cleanup; + } + + kernel =3D g_malloc(size); + got =3D fread(kernel, 1, size, f); + if ((uint64_t) got !=3D size) { + error_setg(errp, "Failed to read EIF kernel section data"); + goto cleanup; + } + + got =3D fwrite(kernel, 1, size, tmp_file); + if ((uint64_t) got !=3D size) { + error_setg(errp, "Failed to write EIF kernel section data to tempo= rary" + " file"); + goto cleanup; + } + + *crc =3D crc32(*crc, kernel, size); + g_free(kernel); + fclose(tmp_file); + + return true; + + cleanup: + safe_fclose(tmp_file); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path =3D NULL; + + g_free(kernel); + return false; +} + +static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, + uint32_t *crc, Error **errp) +{ + size_t got =3D fread(cmdline, 1, size, f); + if ((uint64_t) got !=3D size) { + error_setg(errp, "Failed to read EIF cmdline section data"); + return false; + } + + *crc =3D crc32(*crc, (uint8_t *)cmdline, size); + return true; +} + +static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, + uint32_t *crc, Error **errp) +{ + size_t got; + uint8_t *ramdisk =3D g_malloc(size); + + got =3D fread(ramdisk, 1, size, eif); + if ((uint64_t) got !=3D size) { + error_setg(errp, "Failed to read EIF ramdisk section data"); + goto cleanup; + } + + got =3D fwrite(ramdisk, 1, size, initrd); + if ((uint64_t) got !=3D size) { + error_setg(errp, "Failed to write EIF ramdisk data to temporary fi= le"); + goto cleanup; + } + + *crc =3D crc32(*crc, ramdisk, size); + g_free(ramdisk); + return true; + + cleanup: + g_free(ramdisk); + return false; +} + +/* + * Upon success, the caller is reponsible for unlinking and freeing + * *kernel_path, *initrd_path and freeing *cmdline. + */ +bool read_eif_file(const char *eif_path, char **kernel_path, char **initrd= _path, + char **cmdline, Error **errp) +{ + FILE *f =3D NULL; + FILE *initrd_f =3D NULL; + uint32_t crc =3D 0; + EifHeader eif_header; + bool seen_sections[EIF_SECTION_MAX] =3D {false}; + + *kernel_path =3D *initrd_path =3D *cmdline =3D NULL; + + f =3D fopen(eif_path, "rb"); + if (f =3D=3D NULL) { + error_setg_errno(errp, errno, "Failed to open %s", eif_path); + goto cleanup; + } + + if (!read_eif_header(f, &eif_header, &crc, errp)) { + goto cleanup; + } + + if (eif_header.version < 4) { + error_setg(errp, "Expected EIF version 4 or greater"); + goto cleanup; + } + + if (eif_header.flags !=3D 0) { + error_setg(errp, "Expected EIF flags to be 0"); + goto cleanup; + } + + if (eif_header.section_cnt > MAX_SECTIONS) { + error_setg(errp, "EIF header section count must not be greater tha= n " + "%d but found %d", MAX_SECTIONS, eif_header.section_cnt= ); + goto cleanup; + } + + for (int i =3D 0; i < eif_header.section_cnt; ++i) { + EifSectionHeader section_header; + uint16_t section_type; + + if (fseek(f, eif_header.section_offsets[i], SEEK_SET) !=3D 0) { + error_setg_errno(errp, errno, "Failed to offset to %lu in EIF = file", + eif_header.section_offsets[i]); + goto cleanup; + } + + if (!read_eif_section_header(f, §ion_header, &crc, errp)) { + goto cleanup; + } + + if (section_header.flags !=3D 0) { + error_setg(errp, "Expected EIF section header flags to be 0"); + goto cleanup; + } + + if (eif_header.section_sizes[i] !=3D section_header.section_size) { + error_setg(errp, "EIF section size mismatch between header and= " + "section header: header %lu, section header %lu", + eif_header.section_sizes[i], + section_header.section_size); + goto cleanup; + } + + section_type =3D section_header.section_type; + + switch (section_type) { + case EIF_SECTION_KERNEL: + if (seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. More than 1 kernel " + "section"); + goto cleanup; + } + if (!read_eif_kernel(f, section_header.section_size, kernel_pa= th, + &crc, errp)) { + goto cleanup; + } + + break; + case EIF_SECTION_CMDLINE: + { + uint64_t size; + if (seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. More than 1 cmdline " + "section"); + goto cleanup; + } + size =3D section_header.section_size; + *cmdline =3D g_malloc(size + 1); + if (!read_eif_cmdline(f, size, *cmdline, &crc, errp)) { + goto cleanup; + } + (*cmdline)[size] =3D '\0'; + + break; + } + case EIF_SECTION_RAMDISK: + if (!seen_sections[EIF_SECTION_RAMDISK]) { + /* + * If this is the first time we are seeing a ramdisk secti= on, + * we need to create the initrd temporary file. + */ + if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp))= { + goto cleanup; + } + initrd_f =3D fopen(*initrd_path, "wb"); + if (initrd_f =3D=3D NULL) { + error_setg_errno(errp, errno, "Failed to open file %s", + *initrd_path); + goto cleanup; + } + } + + if (!read_eif_ramdisk(f, initrd_f, section_header.section_size, + &crc, errp)) { + goto cleanup; + } + + break; + default: + /* other sections including invalid or unknown sections */ + { + uint8_t *buf; + size_t got; + uint64_t size =3D section_header.section_size; + buf =3D g_malloc(size); + got =3D fread(buf, 1, size, f); + if ((uint64_t) got !=3D size) { + g_free(buf); + error_setg(errp, "Failed to read EIF %s section data", + section_type_to_string(section_type)); + goto cleanup; + } + crc =3D crc32(crc, buf, size); + g_free(buf); + break; + } + } + + if (section_type < EIF_SECTION_MAX) { + seen_sections[section_type] =3D true; + } + } + + if (!seen_sections[EIF_SECTION_KERNEL]) { + error_setg(errp, "Invalid EIF image. No kernel section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_CMDLINE]) { + error_setg(errp, "Invalid EIF image. No cmdline section."); + goto cleanup; + } + if (!seen_sections[EIF_SECTION_RAMDISK]) { + error_setg(errp, "Invalid EIF image. No ramdisk section."); + goto cleanup; + } + + if (eif_header.eif_crc32 !=3D crc) { + error_setg(errp, "CRC mismatch. Expected %u but header has %u.", + crc, eif_header.eif_crc32); + goto cleanup; + } + + fclose(f); + fclose(initrd_f); + return true; + + cleanup: + safe_fclose(f); + safe_fclose(initrd_f); + + safe_unlink(*kernel_path); + g_free(*kernel_path); + *kernel_path =3D NULL; + + safe_unlink(*initrd_path); + g_free(*initrd_path); + *initrd_path =3D NULL; + + g_free(*cmdline); + *cmdline =3D NULL; + + return false; +} + +bool check_if_eif_file(const char *path, bool *is_eif, Error **errp) +{ + size_t got; + uint8_t buf[4]; + FILE *f =3D NULL; + + f =3D fopen(path, "rb"); + if (f =3D=3D NULL) { + error_setg_errno(errp, errno, "Failed to open file %s", path); + goto cleanup; + } + + got =3D fread(buf, 1, 4, f); + if (got !=3D 4) { + error_setg(errp, "Failed to read magic value from %s", path); + goto cleanup; + } + + fclose(f); + *is_eif =3D !memcmp(buf, ".eif", 4); + return true; + + cleanup: + safe_fclose(f); + return false; +} diff --git a/hw/core/eif.h b/hw/core/eif.h new file mode 100644 index 0000000000..cfc09d044e --- /dev/null +++ b/hw/core/eif.h @@ -0,0 +1,20 @@ +/* + * EIF (Enclave Image Format) related helpers + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef HW_CORE_EIF_H +#define HW_CORE_EIF_H + +bool read_eif_file(const char *eif_path, char **kernel_path, char **initrd= _path, + char **kernel_cmdline, Error **errp); + +bool check_if_eif_file(const char *path, bool *is_eif, Error **errp); + +#endif + diff --git a/hw/core/meson.build b/hw/core/meson.build index a3d9bab9f4..77e26e9cc5 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -24,6 +24,7 @@ system_ss.add(when: 'CONFIG_REGISTER', if_true: files('re= gister.c')) system_ss.add(when: 'CONFIG_SPLIT_IRQ', if_true: files('split-irq.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) +system_ss.add(when: 'CONFIG_MICROVM', if_true: files('eif.c')) =20 system_ss.add(files( 'cpu-sysemu.c', diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index fec63cacfa..27d092ed29 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -30,6 +30,7 @@ #include "acpi-microvm.h" #include "microvm-dt.h" =20 +#include "hw/core/eif.h" #include "hw/loader.h" #include "hw/irq.h" #include "hw/i386/kvm/clock.h" @@ -281,6 +282,127 @@ static void microvm_devices_init(MicrovmMachineState = *mms) x86_bios_rom_init(x86ms, default_firmware, get_system_memory(), true); } =20 +/* Expects file to have offset 0 before this function is called */ +static long get_file_size(FILE *f, Error **errp) +{ + long size; + + if (fseek(f, 0, SEEK_END) !=3D 0) { + error_setg_errno(errp, errno, "Failed to seek to the end of file"); + return -1; + } + + size =3D ftell(f); + if (size =3D=3D -1) { + error_setg_errno(errp, errno, "Failed to get offset"); + return -1; + } + + if (fseek(f, 0, SEEK_SET) !=3D 0) { + error_setg_errno(errp, errno, "Failed to seek back to the start"); + return -1; + } + + return size; +} + +static void load_eif(MicrovmMachineState *mms, FWCfgState *fw_cfg) +{ + Error *err; + char *eif_kernel, *eif_initrd, *eif_cmdline; + MachineState *machine =3D MACHINE(mms); + X86MachineState *x86ms =3D X86_MACHINE(mms); + + if (!read_eif_file(machine->kernel_filename, &eif_kernel, &eif_initrd, + &eif_cmdline, &err)) { + error_report_err(err); + exit(1); + } + + g_free(machine->kernel_filename); + machine->kernel_filename =3D eif_kernel; + + /* + * If an initrd argument was provided, let's concatenate it to the + * extracted EIF initrd temporary file. + */ + if (machine->initrd_filename) { + long size; + size_t got; + uint8_t *buf; + FILE *initrd_f, *eif_initrd_f; + + initrd_f =3D fopen(machine->initrd_filename, "rb"); + if (initrd_f =3D=3D NULL) { + error_setg_errno(&err, errno, "Failed to open initrd file %s", + machine->initrd_filename); + goto cleanup; + } + + size =3D get_file_size(initrd_f, &err); + if (size =3D=3D -1) { + goto cleanup; + } + + buf =3D g_malloc(size); + got =3D fread(buf, 1, size, initrd_f); + if ((uint64_t) got !=3D (uint64_t) size) { + error_setg(&err, "Failed to read initrd file %s", + machine->initrd_filename); + goto cleanup; + } + + eif_initrd_f =3D fopen(eif_initrd, "ab"); + if (eif_initrd_f =3D=3D NULL) { + error_setg_errno(&err, errno, "Failed to open EIF initrd file = %s", + eif_initrd); + goto cleanup; + } + got =3D fwrite(buf, 1, size, eif_initrd_f); + if ((uint64_t) got !=3D (uint64_t) size) { + error_setg(&err, "Failed to append initrd %s to %s", + machine->initrd_filename, eif_initrd); + goto cleanup; + } + + fclose(initrd_f); + fclose(eif_initrd_f); + + g_free(buf); + g_free(machine->initrd_filename); + + machine->initrd_filename =3D eif_initrd; + } else { + machine->initrd_filename =3D eif_initrd; + } + + /* + * If kernel cmdline argument was provided, let's concatenate it to the + * extracted EIF kernel cmdline. + */ + if (machine->kernel_cmdline !=3D NULL) { + char *cmd =3D g_strdup_printf("%s %s", eif_cmdline, + machine->kernel_cmdline); + g_free(eif_cmdline); + g_free(machine->kernel_cmdline); + machine->kernel_cmdline =3D cmd; + } else { + machine->kernel_cmdline =3D eif_cmdline; + } + + x86_load_linux(x86ms, fw_cfg, 0, true); + + unlink(machine->kernel_filename); + unlink(machine->initrd_filename); + return; + + cleanup: + error_report_err(err); + unlink(eif_kernel); + unlink(eif_initrd); + exit(1); +} + static void microvm_memory_init(MicrovmMachineState *mms) { MachineState *machine =3D MACHINE(mms); @@ -330,7 +452,17 @@ static void microvm_memory_init(MicrovmMachineState *m= ms) rom_set_fw(fw_cfg); =20 if (machine->kernel_filename !=3D NULL) { - x86_load_linux(x86ms, fw_cfg, 0, true); + Error *err; + bool is_eif =3D false; + if (!check_if_eif_file(machine->kernel_filename, &is_eif, &err)) { + error_report_err(err); + exit(1); + } + if (is_eif) { + load_eif(mms, fw_cfg); + } else { + x86_load_linux(x86ms, fw_cfg, 0, true); + } } =20 if (mms->option_roms) { --=20 2.39.2 From nobody Mon Nov 25 04:52:34 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1717259307; cv=none; d=zohomail.com; s=zohoarc; b=bEyFWwLVo7VBXiBjfwLZKJXEsI4jt+1CJNI70EyW/C9CG+Zox7nUGEjT5JoNONqRx7kxANBYmTr5l1Qa74R1eCB2OhaUo/gAgsUnfLE7u2dn8mZnYWq5FAt9DJCOTLyP2IXcRen8kH5zUlnbGyXHApRMBXJfu5gA51kK/Js+2Ng= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1717259307; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=M03FvfURsmdCtAGINjDGY35+P1GzwVNmZmCY+CQ1+9g=; b=BMzSlwxLWIJetKy6iywKTlP7h+ndyutOEnNOsxZoaJaPf+0YLxziQ/tccPI2NAKiMZWQRql8Gm/6r52oClo6UlAIEIECUMSo8WJj+iiaZ/oP0Exu5gdu+FEUmHNW+LljWhX8B6CtRL1fSPanw7BxFuqDgK0C91CgapecPXBXs5o= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1717259307953385.9568355580128; Sat, 1 Jun 2024 09:28:27 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sDRZr-0007IL-FR; Sat, 01 Jun 2024 12:27:31 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sDRZp-0007Hc-D9 for qemu-devel@nongnu.org; Sat, 01 Jun 2024 12:27:29 -0400 Received: from mail-pg1-x52b.google.com ([2607:f8b0:4864:20::52b]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1sDRZn-000881-3H for qemu-devel@nongnu.org; Sat, 01 Jun 2024 12:27:29 -0400 Received: by mail-pg1-x52b.google.com with SMTP id 41be03b00d2f7-6c4f3e0e3d2so1141174a12.1 for ; Sat, 01 Jun 2024 09:27:26 -0700 (PDT) Received: from localhost.localdomain ([103.103.35.7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2c1c27ad3cesm3285036a91.7.2024.06.01.09.27.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 01 Jun 2024 09:27:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1717259244; x=1717864044; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=M03FvfURsmdCtAGINjDGY35+P1GzwVNmZmCY+CQ1+9g=; b=WtbU/8Ueme92CFufE6gP7WJ0bUFBDGRKU3hMMiDrd7GrbyqvThUFL8CIfjUZCB8ODR B+CAb79UKr4+gjuCFSyF19X9RZ+JypcVfGrhyP2KsKrOYdTWRaKaVyNRREWNEF67xB4E 7ZFwfLq0mGP1j8D6/PREcJbnBkwM7Ew15IGnonWBAWZCmYL0XkSgYYk2twSM7yxKuCLW M0AlX+AGxCFc62ThPfe4gjOp1Vux83XcQMoefsIuA43r2ETwjLFECz+x77GIjzIlQ9kw 5ZQVAx125hpcS+q0+hd4N9Kpzo8BekdvyZlyCdnD5ZWSWAl19RLD1krDaRUt21Kb26k4 N2FQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1717259244; x=1717864044; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=M03FvfURsmdCtAGINjDGY35+P1GzwVNmZmCY+CQ1+9g=; b=xF+CWQqwvZvOo5BG0FHJS8B/S33eCtHzwxXk+Hjvg9w4dGfQEspUntFGO1j8dRNMhT oEGuqxIt+YQMhGcYuIzzz51MRMB+WXZTknYJhQQI/jGw01nRlYRhtHgdKo7Fynqh/8tZ M/cWfAb22zi3/iFtI3xGrwMbLtcJBI6zaB4EidaL7Y3Xbc81BXvvi9rBKvYWmVfIRQdY 4XwgqjLDsogKglySIqgNQJL8tq0l74lJ45KyDn5Tr0jtAOQ2h5R4fBZg+gQ8nD//owIz uaQZvO4rohvvMT+PqQWZBKDbYnVAd7ilGrHeiGYPhSaeK6FnC2d3lU+4FdoC2V4vCqY4 CQmA== X-Gm-Message-State: AOJu0YxUxFhKOsaE5nhNYfoe0lbx2j0gTLR7UYmd6NRxzLDi77wnLgjY NK0mPFtMkc3ffJlVSPqY/y1vet5WqNPXhdVKetM/4ZT9hijU4iTsvFKoMyRh X-Google-Smtp-Source: AGHT+IFQjmQGUd2xuT0MyMpKBJJ7Yn/hDdc7JiU5IcXRV+eeAGcil+ujepRCM0Lm4+ACN0UuByD+Jg== X-Received: by 2002:a17:90a:9f8d:b0:2bf:3859:d5a5 with SMTP id 98e67ed59e1d1-2c1dc57016dmr4460323a91.11.1717259244061; Sat, 01 Jun 2024 09:27:24 -0700 (PDT) From: Dorjoy Chowdhury To: qemu-devel@nongnu.org Cc: graf@amazon.com, agraf@csgraf.de, stefanha@redhat.com, pbonzini@redhat.com, slp@redhat.com, richard.henderson@linaro.org, eduardo@habkost.net, mst@redhat.com, marcel.apfelbaum@gmail.com, berrange@redhat.com, philmd@linaro.org Subject: [PATCH v2 2/2] machine/nitro-enclave: new machine type for AWS nitro enclave Date: Sat, 1 Jun 2024 22:26:52 +0600 Message-Id: <20240601162652.55643-3-dorjoychy111@gmail.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240601162652.55643-1-dorjoychy111@gmail.com> References: <20240601162652.55643-1-dorjoychy111@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::52b; envelope-from=dorjoychy111@gmail.com; helo=mail-pg1-x52b.google.com X-Spam_score_int: 15 X-Spam_score: 1.5 X-Spam_bar: + X-Spam_report: (1.5 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_SBL_CSS=3.335, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1717259309611100008 Content-Type: text/plain; charset="utf-8" AWS nitro enclaves[1] is an Amazon EC2[2] feature that allows creating isolated execution environments, called enclaves, from Amazon EC2 instances which are used for processing highly sensitive data. Enclaves have no persistent storage and no external networking. The enclave VMs are based on Firecracker microvm with a vhost-vsock device for communication with the parent EC2 instance that spawned it and a Nitro Secure Module (NSM) device for cryptographic attestation. The parent instance VM always has CID 3 while the enclave VM gets a dynamic CID. This commit adds support for limited AWS nitro enclave emulation using a new machine type option '-M nitro-enclave'. This new machine type is based on the 'microvm' machine type (similar to how real nitro enclave VMs are based on Firecracker microvm) which requires a mandatory 'guest-cid' machine type option which becomes the CID of the running nitro-enclave VM using a built-in vhost-vsock device. The code for checking and loading EIF in the microvm machine code added in the previous commit has been put inside a 'nitro-enclave' machine type check so that EIF images can only be loaded using a 'nitro-enclave' machine. Right now, the emulation support is not complete as nitro-enclave cannot be run standalone using QEMU. A separate VM with CID 3 must be run with the necessary vsock communication support that the enclave VM communicates with. Also the Nitro Secure Module (NSM) device is not implemented yet. [1] https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html [2] https://aws.amazon.com/ec2/ Signed-off-by: Dorjoy Chowdhury Reviewed-by: Alexander Graf --- MAINTAINERS | 10 ++ configs/devices/i386-softmmu/default.mak | 1 + docs/system/i386/nitro-enclave.rst | 58 ++++++++++ hw/i386/Kconfig | 4 + hw/i386/meson.build | 1 + hw/i386/microvm.c | 21 ++-- hw/i386/nitro_enclave.c | 134 +++++++++++++++++++++++ include/hw/i386/nitro_enclave.h | 38 +++++++ 8 files changed, 260 insertions(+), 7 deletions(-) create mode 100644 docs/system/i386/nitro-enclave.rst create mode 100644 hw/i386/nitro_enclave.c create mode 100644 include/hw/i386/nitro_enclave.h diff --git a/MAINTAINERS b/MAINTAINERS index 448dc951c5..250ab32743 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1867,6 +1867,16 @@ F: hw/i386/microvm.c F: include/hw/i386/microvm.h F: pc-bios/bios-microvm.bin =20 +nitro-enclave +M: Alexander Graf +M: Dorjoy Chowdhury +S: Maintained +F: docs/system/i386/nitro-enclave.rst +F: hw/core/eif.c +F: hw/core/eif.h +F: hw/i386/nitro_enclave.c +F: include/hw/i386/nitro_enclave.h + Machine core M: Eduardo Habkost M: Marcel Apfelbaum diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i38= 6-softmmu/default.mak index 448e3e3b1b..4faf2f0315 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -29,3 +29,4 @@ # CONFIG_I440FX=3Dn # CONFIG_Q35=3Dn # CONFIG_MICROVM=3Dn +# CONFIG_NITRO_ENCLAVE=3Dn diff --git a/docs/system/i386/nitro-enclave.rst b/docs/system/i386/nitro-en= clave.rst new file mode 100644 index 0000000000..0b86b6ab88 --- /dev/null +++ b/docs/system/i386/nitro-enclave.rst @@ -0,0 +1,58 @@ +'nitro-enclave' virtual machine (``nitro-enclave``) +=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D + +``nitro-enclave`` is a machine type which emulates an ``AWS nitro enclave`` +virtual machine. `AWS nitro enclaves`_ is an `Amazon EC2`_ feature that al= lows +creating isolated execution environments, called enclaves, from Amazon EC2 +instances which are used for processing highly sensitive data. Enclaves ha= ve +no persistent storage and no external networking. The enclave VMs are based +on Firecracker microvm with a vhost-vsock device for communication with the +parent EC2 instance that spawned it and a Nitro Secure Module (NSM) device +for cryptographic attestation. The parent instance VM always has CID 3 whi= le +the enclave VM gets a dynamic CID. Enclaves use an EIF (`Enclave Image For= mat`_) +file which contains the necessary kernel, cmdline and ramdisk(s) to boot. + +In QEMU, ``nitro-enclave`` is a machine type based on ``microvm`` similar = to how +``AWS nitro enclaves`` are based on ``Firecracker``. This is useful for lo= cal +testing of EIF images using QEMU instead of running real AWS Nitro Enclaves +which can be difficult for debugging due to its roots in security. + +.. _AWS nitro enlaves: https://docs.aws.amazon.com/enclaves/latest/user/ni= tro-enclave.html +.. _Amazon EC2: https://aws.amazon.com/ec2/ +.. _Enclave Image Format: https://github.com/aws/aws-nitro-enclaves-image-= format + + +Limitations +----------- + +AWS nitro enclave emulation support is not complete yet: + +- Although support for the vhost-vsock device is implemented, standalone +nitro-enclave VMs cannot be run right now as nitro-enclave VMs communicate +with a parent instance VM with CID 3. So another VM with CID 3 must be run +with necessary vsock communication support. +- Enclaves also have a Nitro Secure Module (NSM) device which is not imple= mented +yet. + + +Using the nitro-enclave machine type +------------------------------ + +Machine-specific options +~~~~~~~~~~~~~~~~~~~~~~~~ + +It supports the following machine-specific options: + +- nitro-enclave.guest-cid=3Duint32_t (required) (Set nitro enclave VM's CI= D) + + +Running a nitro-enclave VM +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A nitro-enclave VM can be run using the following command where ``hello.ei= f`` is +an EIF image you would use to spawn a real AWS nitro enclave virtual machi= ne: + + $ sudo qemu-system-x86_64 -M nitro-enclave,guest-cid=3D8 \ + -enable-kvm -cpu host -m 512m \ + -kernel hello.eif \ + -nographic diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index f4a33b6c08..eba8eaa960 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -129,6 +129,10 @@ config MICROVM select USB_XHCI_SYSBUS select I8254 =20 +config NITRO_ENCLAVE + default y + depends on MICROVM + config X86_IOMMU bool depends on PC diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 03aad10df7..10bdfde27c 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -15,6 +15,7 @@ i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd= _iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microv= m.c', 'acpi-microvm.c', 'microvm-dt.c')) +i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c'= )) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) i386_ss.add(when: 'CONFIG_VMMOUSE', if_true: files('vmmouse.c')) i386_ss.add(when: 'CONFIG_VMPORT', if_true: files('vmport.c')) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 27d092ed29..fd07825c6c 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -35,6 +35,7 @@ #include "hw/irq.h" #include "hw/i386/kvm/clock.h" #include "hw/i386/microvm.h" +#include "hw/i386/nitro_enclave.h" #include "hw/i386/x86.h" #include "target/i386/cpu.h" #include "hw/intc/i8259.h" @@ -452,13 +453,19 @@ static void microvm_memory_init(MicrovmMachineState *= mms) rom_set_fw(fw_cfg); =20 if (machine->kernel_filename !=3D NULL) { - Error *err; - bool is_eif =3D false; - if (!check_if_eif_file(machine->kernel_filename, &is_eif, &err)) { - error_report_err(err); - exit(1); - } - if (is_eif) { + if (object_dynamic_cast(OBJECT(machine), TYPE_NITRO_ENCLAVE_MACHIN= E)) { + Error *err; + bool is_eif =3D false; + if (!check_if_eif_file(machine->kernel_filename, &is_eif, &err= )) { + error_report_err(err); + exit(1); + } + if (!is_eif) { + error_report("%s is not a valid EIF file.", + machine->kernel_filename); + exit(1); + } + load_eif(mms, fw_cfg); } else { x86_load_linux(x86ms, fw_cfg, 0, true); diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c new file mode 100644 index 0000000000..5a9b9b04bf --- /dev/null +++ b/hw/i386/nitro_enclave.c @@ -0,0 +1,134 @@ +/* + * AWS nitro-enclave machine + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/visitor.h" + +#include "hw/sysbus.h" +#include "hw/i386/x86.h" +#include "hw/i386/microvm.h" +#include "hw/i386/nitro_enclave.h" +#include "hw/virtio/vhost-vsock.h" +#include "hw/virtio/virtio-mmio.h" + +static BusState *find_virtio_mmio_bus(void) +{ + BusChild *kid; + BusState *bus =3D sysbus_get_default(); + + QTAILQ_FOREACH(kid, &bus->children, sibling) { + DeviceState *dev =3D kid->child; + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_MMIO)) { + VirtIOMMIOProxy *mmio =3D VIRTIO_MMIO(OBJECT(dev)); + VirtioBusState *mmio_virtio_bus =3D &mmio->bus; + BusState *mmio_bus =3D &mmio_virtio_bus->parent_obj; + return mmio_bus; + } + } + + return NULL; +} + +static void nitro_enclave_devices_init(NitroEnclaveMachineState *nems) +{ + DeviceState *dev =3D qdev_new(TYPE_VHOST_VSOCK); + BusState *bus =3D find_virtio_mmio_bus(); + if (!bus) { + error_report("Failed to find bus for vhost-vsock device."); + exit(1); + } + + if (nems->guest_cid <=3D 3) { + error_report("Nitro enclave machine option 'guest-cid' must be set= " + "with a value greater than or equal to 4"); + exit(1); + } + + qdev_prop_set_uint64(dev, "guest-cid", nems->guest_cid); + qdev_realize_and_unref(dev, bus, &error_fatal); +} + +static void nitro_enclave_machine_state_init(MachineState *machine) +{ + NitroEnclaveMachineClass *ne_class =3D + NITRO_ENCLAVE_MACHINE_GET_CLASS(machine); + NitroEnclaveMachineState *ne_state =3D NITRO_ENCLAVE_MACHINE(machine); + + ne_class->parent_init(machine); + nitro_enclave_devices_init(ne_state); +} + +static void nitro_enclave_machine_initfn(Object *obj) +{ + NitroEnclaveMachineState *nems =3D NITRO_ENCLAVE_MACHINE(obj); + MicrovmMachineState *mms =3D MICROVM_MACHINE(obj); + X86MachineState *x86ms =3D X86_MACHINE(obj); + + nems->guest_cid =3D 0; + + /* AWS nitro enclaves have PCIE and ACPI disabled */ + mms->pcie =3D ON_OFF_AUTO_OFF; + x86ms->acpi =3D ON_OFF_AUTO_OFF; +} + +static void nitro_enclave_get_guest_cid(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + NitroEnclaveMachineState *nems =3D NITRO_ENCLAVE_MACHINE(obj); + uint32_t guest_cid =3D nems->guest_cid; + + visit_type_uint32(v, name, &guest_cid, errp); +} + +static void nitro_enclave_set_guest_cid(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + NitroEnclaveMachineState *nems =3D NITRO_ENCLAVE_MACHINE(obj); + + visit_type_uint32(v, name, &nems->guest_cid, errp); +} + +static void nitro_enclave_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc =3D MACHINE_CLASS(oc); + NitroEnclaveMachineClass *nemc =3D NITRO_ENCLAVE_MACHINE_CLASS(oc); + + mc->family =3D "nitro_enclave_i386"; + mc->desc =3D "AWS Nitro Enclave"; + + nemc->parent_init =3D mc->init; + mc->init =3D nitro_enclave_machine_state_init; + + object_class_property_add(oc, NITRO_ENCLAVE_GUEST_CID, "uint32_t", + nitro_enclave_get_guest_cid, + nitro_enclave_set_guest_cid, + NULL, NULL); + object_class_property_set_description(oc, NITRO_ENCLAVE_GUEST_CID, + "Set enclave machine's cid"); +} + +static const TypeInfo nitro_enclave_machine_info =3D { + .name =3D TYPE_NITRO_ENCLAVE_MACHINE, + .parent =3D TYPE_MICROVM_MACHINE, + .instance_size =3D sizeof(NitroEnclaveMachineState), + .instance_init =3D nitro_enclave_machine_initfn, + .class_size =3D sizeof(NitroEnclaveMachineClass), + .class_init =3D nitro_enclave_class_init, +}; + +static void nitro_enclave_machine_init(void) +{ + type_register_static(&nitro_enclave_machine_info); +} +type_init(nitro_enclave_machine_init); diff --git a/include/hw/i386/nitro_enclave.h b/include/hw/i386/nitro_enclav= e.h new file mode 100644 index 0000000000..9dc3afd494 --- /dev/null +++ b/include/hw/i386/nitro_enclave.h @@ -0,0 +1,38 @@ +/* + * AWS nitro-enclave machine + * + * Copyright (c) 2024 Dorjoy Chowdhury + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * (at your option) any later version. See the COPYING file in the + * top-level directory. + */ + +#ifndef HW_I386_NITRO_ENCLAVE_H +#define HW_I386_NITRO_ENCLAVE_H + +#include "hw/boards.h" +#include "hw/i386/microvm.h" +#include "qom/object.h" + +/* Machine type options */ +#define NITRO_ENCLAVE_GUEST_CID "guest-cid" + +struct NitroEnclaveMachineClass { + MicrovmMachineClass parent; + + void (*parent_init)(MachineState *state); +}; + +struct NitroEnclaveMachineState { + MicrovmMachineState parent; + + /* Machine type options */ + uint32_t guest_cid; +}; + +#define TYPE_NITRO_ENCLAVE_MACHINE MACHINE_TYPE_NAME("nitro-enclave") +OBJECT_DECLARE_TYPE(NitroEnclaveMachineState, NitroEnclaveMachineClass, + NITRO_ENCLAVE_MACHINE) + +#endif --=20 2.39.2