From patchwork Mon Aug 19 14:53:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 821085 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77C8C173357 for ; Mon, 19 Aug 2024 14:55:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079306; cv=none; b=hiHFoWcBIwdFOLrcR6D+pQfR3rWSdppIuoNXvybXULYMF1fRjLE4hpc//k0iNgAmn0J2WYznrsus2F7j+rVbwtsII3ASDuJKBBR5cqjkR4PM9s3rWHTRlBj67NIqpQy95/y8DA51RjlsD1C/yu+oL0Wya0gos4MmoOPOBxcyuVA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079306; c=relaxed/simple; bh=sPWPyK91Xn7Sv/iGrU2KXWFFhVGbbd9clCRXcFC1a5s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=vAQxQz9/Y6HoBrVEIbJuy3FCI7suwBXmIywOX8CnbI3JcaQrSftc4H/R4B0kHpnzIzZYue5kWG9PK9U1MbrUbo8Il2E483uy8RoBlyJauPsr6d/HYFV4/oRlRCqlioS9so0VYIL3ygreQZHsDUHOMtIn0/thjEGdBDjW+xUqqh0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=Wb4QFBCD; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="Wb4QFBCD" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079304; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=wiBxTOGq9aVdhn1+CpbHzQqObWNoKSOYg9dxENZx8/E=; b=Wb4QFBCD0lp1/IRyr7/AmBrZkK+swEyBvi8HHeMlNJ1WjIQrvtvzB/ZezsvfNCluHdtrSM WMcTWVv42Ewhvuchr30Qu/QpKAUNVuyTVNdcWR4LuRno74FMkb5ZrJmMCh5i/FpqOSil8s V1oy7JvRrmH80gFxC/xj58q2rGbamJU= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-94-Ue7ktWKDOO-5JaPjXde9nA-1; Mon, 19 Aug 2024 10:55:00 -0400 X-MC-Unique: Ue7ktWKDOO-5JaPjXde9nA-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 897141944D01; Mon, 19 Aug 2024 14:54:58 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id E114C19560A3; Mon, 19 Aug 2024 14:54:49 +0000 (UTC) From: Pingfan Liu To: linux-efi@vger.kernel.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFCv2 1/9] efi/libstub: Ask efi_random_alloc() to skip unusable memory Date: Mon, 19 Aug 2024 22:53:34 +0800 Message-ID: <20240819145417.23367-2-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 efi_random_alloc() demands EFI_ALLOCATE_ADDRESS when allocate_pages(), but the current implement can not ensure the selected target locates inside free area, that is to exclude EFI_BOOT_SERVICES_*, EFI_RUNTIME_SERVICES_* etc. Fix the issue by checking md->type. Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel To: linux-efi@vger.kernel.org --- drivers/firmware/efi/libstub/randomalloc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/firmware/efi/libstub/randomalloc.c b/drivers/firmware/efi/libstub/randomalloc.c index c41e7b2091cdd..7304e767688f2 100644 --- a/drivers/firmware/efi/libstub/randomalloc.c +++ b/drivers/firmware/efi/libstub/randomalloc.c @@ -79,6 +79,8 @@ efi_status_t efi_random_alloc(unsigned long size, efi_memory_desc_t *md = (void *)map->map + map_offset; unsigned long slots; + if (!(md->type & (EFI_CONVENTIONAL_MEMORY || EFI_PERSISTENT_MEMORY))) + continue; slots = get_entry_num_slots(md, size, ilog2(align), alloc_min, alloc_max); MD_NUM_SLOTS(md) = slots; @@ -111,6 +113,9 @@ efi_status_t efi_random_alloc(unsigned long size, efi_physical_addr_t target; unsigned long pages; + if (!(md->type & (EFI_CONVENTIONAL_MEMORY || EFI_PERSISTENT_MEMORY))) + continue; + if (total_mirrored_slots > 0 && !(md->attribute & EFI_MEMORY_MORE_RELIABLE)) continue; From patchwork Mon Aug 19 14:53:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 820429 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D8A131741EF for ; Mon, 19 Aug 2024 14:55:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079316; cv=none; b=fvagrhXLEwhYjp5DOINp1RvrNvT4wgDwxhb+dWhrE2FNP/ShX5xAeElgTvuQ7NtrXSDSHzvjQsGXPLdNYU0g37Ya43EBs8K3A3+SAabTRudZzDAF9jF5mrGRgZx2MxF9J512/rlItdIl4NZKE5+0hB5J7DZJmJUsZjcpfAnFxuA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079316; c=relaxed/simple; bh=EowreNZLQ30N4cVY10ETji3j3fdTviu5kI0Pw22l/FQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BM24ft4q1mhyDzTHesctCQt7lR31BvJaKQ8VaDa88hd4Xs0YInFyckYwpVaE7edvE+EAOb9fSOBR7UwVGVpQ8iq3D8jWP19Nedq3lFg14nweI0BBesKbpLNzSXxxC+wqoyOcwkcIZPluPVFs6AUS0mwCQS0wfyghGO0N9BvwJAM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=ZTkxBPEu; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ZTkxBPEu" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079313; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QLgjZO4Zyjz7laVnuZPF54UYEyobsvNSUm+pwm24uBE=; b=ZTkxBPEuFbn8ro0projWq7LKLnK4CUDDWL8W6tSw52TCwgjSyew8tekKjh7eeGcUwhTUxA 1G8dLRzrorW3SD2uyiOTuA+3mv+9QdxcLeP6Ms7SMO2YXd/OvbZuNPLYbZiS47IIqin+RO HOFnEnu/3rX1sJTishL1iiaGbHi3NxY= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-396-Brd712sEOlOpIfxBr1n9_w-1; Mon, 19 Aug 2024 10:55:10 -0400 X-MC-Unique: Brd712sEOlOpIfxBr1n9_w-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5CCB91956095; Mon, 19 Aug 2024 14:55:08 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 75D3819560A3; Mon, 19 Aug 2024 14:54:59 +0000 (UTC) From: Pingfan Liu To: linux-efi@vger.kernel.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-kernel@vger.kernel.org Subject: [RFCv2 2/9] efi/libstub: Complete efi_simple_text_output_protocol Date: Mon, 19 Aug 2024 22:53:35 +0800 Message-ID: <20240819145417.23367-3-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Some uefi applications e.g systemd-stub uses more members in efi_simple_text_output_protocol, hence extending its members as UEFI specification. Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel To: linux-efi@vger.kernel.org --- drivers/firmware/efi/libstub/efistub.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 27abb4ce02917..a075d327a11ba 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -506,6 +506,13 @@ union efi_simple_text_output_protocol { efi_status_t (__efiapi *output_string)(efi_simple_text_output_protocol_t *, efi_char16_t *); void *test_string; + void *query_mode; + void *set_mode; + void *set_attribute; + void *clean_screen; + void *set_cursor_pos; + void *enable_cursor; + void *mode; }; struct { u32 reset; From patchwork Mon Aug 19 14:53:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 821084 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77DA1174EDB for ; Mon, 19 Aug 2024 14:55:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079330; cv=none; b=Uqq8ibVC+qYMpbtPPGQGgiiJK9lTR9BNTT1CYJZ8DLleoyT6YpAyuEb9BzrLQBC768p9GRTsFrFTEEq0E/kLM2256fNQDuLyREHxxVtMtDHU9iocC8WQZHRtXRgb1J9KR8xnh5IdwFoVPpHudbZxn1i/Lv3otvwljODBqaU7Ui4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079330; c=relaxed/simple; bh=G0JhpL24kKkhtP2Yl5e/paTlsRaHUMdW5sMBPag46v8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Vs9KIzKbvUU3dPAsJpZMFyamRIPjZtePEnSIxjzy370T3J6amMtu4RNle3sJMyrby+ivnkxPeOdwvQTmgTdvlg9dUf5YdqchCMjHNnS9OswUM+y+uxzKVikKhAA87QAH6YmrBd8ElQqf8hKsYkAsWHcJEE/rODoY5qH0xJ5bAzo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=ey7nq12v; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="ey7nq12v" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079325; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=/nUnlpRgfnHSQvAfyZpIAiaoZ1eeQlE7Hh8wiqCBupI=; b=ey7nq12vkZdFHAfIrej5DLaJd+Vb55RxQCYx/jERN2iY7bqqH9r9IGFyh94dx3zzh8QuZ3 HX2irwQV/8O+il6q1lFhWoPJSAWlws2JUBEDc0iTKxVNHQsEBIApqYIFHg0kc674JyWBDC Xa4JXOq+ANhT7CQdMbsiaOVyxkEoQcw= Received: from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-68-RgfSY5ycPmieuTSfW7NXJQ-1; Mon, 19 Aug 2024 10:55:21 -0400 X-MC-Unique: RgfSY5ycPmieuTSfW7NXJQ-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 9DF161955BF2; Mon, 19 Aug 2024 14:55:19 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 5CE3119560A3; Mon, 19 Aug 2024 14:55:08 +0000 (UTC) From: Pingfan Liu To: linux-efi@vger.kernel.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-kernel@vger.kernel.org, Pingfan Liu Subject: [RFCv2 3/9] efi/emulator: Initial rountines to emulate EFI boot time service Date: Mon, 19 Aug 2024 22:53:36 +0800 Message-ID: <20240819145417.23367-4-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 From: Pingfan Liu efi emulator aims to serve the kexec if the kernel wrapped by efistub. It is paired with efistub, so only implements the minimus set of EFI boot service which merely enough boots up efistub. To simplify the code, the task such as the building of page table etc is shift to the first kernel as more as possible. (This part is implement in the later patch in this series) To do: -1. a simple memory allocator -2. a few extra efi boot service. Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel Cc: kexec@lists.infradead.org To: linux-efi@vger.kernel.org --- drivers/firmware/efi/Makefile | 1 + drivers/firmware/efi/efi_emulator/Makefile | 99 +++++ .../firmware/efi/efi_emulator/amba-pl011.c | 81 ++++ .../efi_emulator/arm64_emulator_service.lds | 45 +++ .../firmware/efi/efi_emulator/config_table.c | 25 ++ drivers/firmware/efi/efi_emulator/core.c | 376 ++++++++++++++++++ .../firmware/efi/efi_emulator/device_handle.c | 138 +++++++ drivers/firmware/efi/efi_emulator/earlycon.h | 19 + .../firmware/efi/efi_emulator/efi_emulator.S | 12 + drivers/firmware/efi/efi_emulator/emulator.h | 106 +++++ drivers/firmware/efi/efi_emulator/entry.c | 61 +++ drivers/firmware/efi/efi_emulator/head.S | 10 + drivers/firmware/efi/efi_emulator/lib.c | 73 ++++ drivers/firmware/efi/efi_emulator/memory.c | 27 ++ .../firmware/efi/efi_emulator/memory_api.c | 74 ++++ drivers/firmware/efi/efi_emulator/misc.c | 43 ++ drivers/firmware/efi/efi_emulator/pe_loader.c | 173 ++++++++ drivers/firmware/efi/efi_emulator/printf.c | 373 +++++++++++++++++ .../efi/efi_emulator/protocol_device_path.c | 75 ++++ .../protocol_simple_text_output.c | 50 +++ include/linux/efi_emulator.h | 46 +++ include/linux/kexec.h | 2 + 22 files changed, 1909 insertions(+) create mode 100644 drivers/firmware/efi/efi_emulator/Makefile create mode 100644 drivers/firmware/efi/efi_emulator/amba-pl011.c create mode 100644 drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds create mode 100644 drivers/firmware/efi/efi_emulator/config_table.c create mode 100644 drivers/firmware/efi/efi_emulator/core.c create mode 100644 drivers/firmware/efi/efi_emulator/device_handle.c create mode 100644 drivers/firmware/efi/efi_emulator/earlycon.h create mode 100644 drivers/firmware/efi/efi_emulator/efi_emulator.S create mode 100644 drivers/firmware/efi/efi_emulator/emulator.h create mode 100644 drivers/firmware/efi/efi_emulator/entry.c create mode 100644 drivers/firmware/efi/efi_emulator/head.S create mode 100644 drivers/firmware/efi/efi_emulator/lib.c create mode 100644 drivers/firmware/efi/efi_emulator/memory.c create mode 100644 drivers/firmware/efi/efi_emulator/memory_api.c create mode 100644 drivers/firmware/efi/efi_emulator/misc.c create mode 100644 drivers/firmware/efi/efi_emulator/pe_loader.c create mode 100644 drivers/firmware/efi/efi_emulator/printf.c create mode 100644 drivers/firmware/efi/efi_emulator/protocol_device_path.c create mode 100644 drivers/firmware/efi/efi_emulator/protocol_simple_text_output.c create mode 100644 include/linux/efi_emulator.h diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index a2d0009560d0f..41471416945eb 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_EFI_VARS_PSTORE) += efi-pstore.o obj-$(CONFIG_UEFI_CPER) += cper.o cper_cxl.o obj-$(CONFIG_EFI_RUNTIME_WRAPPERS) += runtime-wrappers.o subdir-$(CONFIG_EFI_STUB) += libstub +obj-$(CONFIG_ARCH_SELECTS_KEXEC_PEIMAGE) += efi_emulator/ obj-$(CONFIG_EFI_BOOTLOADER_CONTROL) += efibc.o obj-$(CONFIG_EFI_TEST) += test/ obj-$(CONFIG_EFI_DEV_PATH_PARSER) += dev-path-parser.o diff --git a/drivers/firmware/efi/efi_emulator/Makefile b/drivers/firmware/efi/efi_emulator/Makefile new file mode 100644 index 0000000000000..d696381d168ba --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/Makefile @@ -0,0 +1,99 @@ +# non-x86 reuses KBUILD_CFLAGS, x86 does not +cflags-y := $(KBUILD_CFLAGS) + +cflags-$(CONFIG_X86_32) := -march=i386 +cflags-$(CONFIG_X86_64) := -mcmodel=small +cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \ + -fno-strict-aliasing -mno-red-zone \ + -mno-mmx -mno-sse -fshort-wchar \ + -Wno-pointer-sign \ + $(call cc-disable-warning, address-of-packed-member) \ + $(call cc-disable-warning, gnu) \ + -fno-asynchronous-unwind-tables \ + $(CLANG_FLAGS) + +# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly +# disable the stackleak plugin +cflags-$(CONFIG_ARM64) += $(DISABLE_STACKLEAK_PLUGIN) \ + -fno-unwind-tables -fno-asynchronous-unwind-tables +cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ + -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ + -DEFI_HAVE_STRCMP -fno-builtin \ + $(call cc-option,-mno-single-pic-base) +cflags-$(CONFIG_RISCV) += -DNO_ALTERNATIVE -mno-relax +cflags-$(CONFIG_LOONGARCH) += + +cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt + +cflags-y += -I drivers/firmware/efi/libstub + +KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(cflags-y)) \ + -Os -DDISABLE_BRANCH_PROFILING \ + -D__NO_FORTIFY \ + -ffreestanding \ + -fno-stack-protector \ + $(call cc-option,-fno-addrsig) \ + -D__DISABLE_EXPORTS + +# +# struct randomization only makes sense for Linux internal types, which the EFI +# stub code never touches, so let's turn off struct randomization for the stub +# altogether +# +KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS), $(KBUILD_CFLAGS)) + +# remove SCS flags from all objects in this directory +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_SCS), $(KBUILD_CFLAGS)) +# disable CFI +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_CFI), $(KBUILD_CFLAGS)) +# disable LTO +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS)) + +GCOV_PROFILE := n +# Sanitizer runtimes are unavailable and cannot be linked here. +KASAN_SANITIZE := n +KCSAN_SANITIZE := n +KMSAN_SANITIZE := n +UBSAN_SANITIZE := n +OBJECT_FILES_NON_STANDARD := y + +# Prevents link failures: __sanitizer_cov_trace_pc() is not linked in. +KCOV_INSTRUMENT := n + +OBJECT_FILES_NON_STANDARD := y +emulator-y := head.o entry.o \ + core.o pe_loader.o memory.o memory_api.o config_table.o misc.o \ + device_handle.o protocol_simple_text_output.o protocol_device_path.o \ + lib.o printf.o \ + amba-pl011.o +obj-y := efi_emulator.o + + +EMULATOR_OBJS = $(addprefix $(obj)/,$(emulator-y)) + +quiet_cmd_ar_emulator = PAD $@ + cmd_ar_emulator = $(AR) rcSTP $@ $^ + +$(obj)/emulator.a: $(EMULATOR_OBJS) + $(call if_changed,ar_emulator) + + +quiet_cmd_link_emulator = PAD $@ + cmd_link_emulator = ld -z norelro -z noexecstack -shared --no-undefined -X -Bsymbolic -z notext --emit-relocs --no-apply-dynamic-relocs \ + -T $(srctree)/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds \ + --whole-archive $< --no-whole-archive -o $@ + + +$(obj)/emulator.ro: $(obj)/emulator.a FORCE + $(call if_changed,link_emulator) + + +$(obj)/emulator.raw: $(obj)/emulator.ro FORCE + @$(OBJCOPY) -O binary -R .note -R .note.gnu.build-id -R .comment -g $< $@ + +$(obj)/efi_emulator.o: $(obj)/emulator.raw + + +targets += $(emulator-y) + + diff --git a/drivers/firmware/efi/efi_emulator/amba-pl011.c b/drivers/firmware/efi/efi_emulator/amba-pl011.c new file mode 100644 index 0000000000000..e81b12b2a7d21 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/amba-pl011.c @@ -0,0 +1,81 @@ +//SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include "earlycon.h" + +#define SERIAL_IO_MEM32 3 +#define UPIO_MEM32 SERIAL_IO_MEM32 + +struct uart_port { + unsigned long iobase; /* in/out[bwl] */ + unsigned char __iomem *membase; /* read/write[bwl] */ + unsigned char iotype; /* io access style */ +}; + +static struct uart_port pl011_port; + +static void pl011_putc(struct uart_port *port, unsigned char c) +{ + while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF) + cpu_relax(); + if (port->iotype == UPIO_MEM32) + writel(c, port->membase + UART01x_DR); + else + writeb(c, port->membase + UART01x_DR); + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) + cpu_relax(); +} + +static int pl011_put_str(const char *str, void *data) +{ + char *p = (char *)str; + struct uart_port *port = (struct uart_port *)data; + + for (; *p != '\0'; p++) + pl011_putc(port, *p); + + return (p - str); +} + +static void pl011_write(struct uart_port *port, unsigned int reg, unsigned int val) +{ + void __iomem *addr = port->membase + reg; + + if (port->iotype == UPIO_MEM32) + writel_relaxed(val, addr); + else + writew_relaxed(val, addr); +} + +static bool pl011_match(struct efi_emulator_param *param, const char *name) +{ + struct uart_port *port = &pl011_port; + + if (strcmp(param->earlycon_name, name)) + return false; + + port->iotype = UPIO_MEM32; + port->membase = (unsigned char *)param->earlycon_reg_base; + return true; +} + +static void pl011_reset(void *data) +{ + struct uart_port *port = data; + + /* disable DMA */ + pl011_write(port, UART011_DMACR, 0); + /* disable interrupt */ + pl011_write(port, UART011_IMSC, 0); + /* Skip: set clk rate */ + /* Now, pl011 can be used in poll mode */ +} + +struct earlycon pl011 = { + .match = pl011_match, + .reset = pl011_reset, + .put_str = pl011_put_str, + .data = &pl011_port, + .name = "amba-pl011", +}; diff --git a/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds b/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds new file mode 100644 index 0000000000000..82d7659346af5 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/arm64_emulator_service.lds @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +ENTRY(emulator_entry) +EMULATOR_BASE_ADDR = 0x0; + +SECTIONS +{ + . = EMULATOR_BASE_ADDR; + .head : ALIGN(4096) { + *(.head.text) + } + + .text : { + *(.text* .init.text*) + } + + .rodata : ALIGN(8) { + *(.rodata* .init.rodata* .srodata*) + + _etext = ALIGN(4096); + . = _etext; + } + + .rela.dyn : ALIGN(4096) { + _rela_start = .; + *(.rela .rela*) + _rela_end = .; + } + + .data : ALIGN(4096) { + *(.data* .init.data*) + _edata = ALIGN(512); + . = _edata; + } + + .bss : { + *(.bss* .init.bss*) + _end = ALIGN(512); + . = _end; + } + + /DISCARD/ : { + *(.modinfo .init.modinfo) + } +} diff --git a/drivers/firmware/efi/efi_emulator/config_table.c b/drivers/firmware/efi/efi_emulator/config_table.c new file mode 100644 index 0000000000000..32b702a156ba4 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/config_table.c @@ -0,0 +1,25 @@ +//SPDX-License-Identifier: GPL-2.0 + +#include "emulator.h" + +static efi_status_t conjoin_memreserve_table(void *table, efi_config_table_t *head) +{ + struct linux_efi_memreserve *new, *next; + + new = (struct linux_efi_memreserve *)table; + new->next = 0; + next = (struct linux_efi_memreserve *)head->table; + while (next->next != 0) + next = (struct linux_efi_memreserve *)next->next; + next->next = (phys_addr_t)new; + + return EFI_SUCCESS; +} + +efi_status_t conjoin_table(efi_guid_t *uuid, void *table, efi_config_table_t *t) +{ + if (!efi_guidcmp(t->guid, LINUX_EFI_MEMRESERVE_TABLE_GUID)) + return conjoin_memreserve_table(table, t); + + return EFI_OUT_OF_RESOURCES; +} diff --git a/drivers/firmware/efi/efi_emulator/core.c b/drivers/firmware/efi/efi_emulator/core.c new file mode 100644 index 0000000000000..9045f6953250c --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/core.c @@ -0,0 +1,376 @@ +//SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "emulator.h" + +int emulator_initialize(void); + +struct efi_emulator_param *emulator_param; +bool print_enabled; + +static efi_loaded_image_t loaded_image; + +static LIST_HEAD(image_instance_list); + +struct efi_pe_instance *get_instance_by_handle(efi_handle_t h) +{ + struct efi_pe_instance *pos; + + list_for_each_entry(pos, &image_instance_list, node) + if (pos->handle == h) + return pos; + + return NULL; +} + +/* no free path */ +struct efi_pe_instance *allocate_pe_instance(char *file_base, + unsigned long file_size) +{ + struct efi_pe_instance *inst; + + inst = aligned_alloc(8, sizeof(struct efi_pe_instance)); + /* identity */ + inst->handle = (efi_handle_t)inst; + inst->image_file_buf = file_base; + inst->image_file_size = file_size; + emulator_list_add(&inst->node, &image_instance_list); + + return inst; +} + +/* The 1st kernel convert cmdline to utf16 and pass to emulator */ +static efi_status_t handle_protocol_loaded_image(efi_handle_t h, void **data) +{ + void *base; + + loaded_image.load_options = emulator_param->cmdline; + loaded_image.load_options_size = emulator_param->sz_in_byte; + + /* loaded address */ + base = (void *)find_image_base_for_handle(h); + loaded_image.image_base = base; + + *data = &loaded_image; + return EFI_SUCCESS; + +} + + +static efi_status_t __efiapi emulator_handle_protocol(efi_handle_t h, + efi_guid_t *uuid, void **data) +{ + if (!efi_guidcmp(*uuid, LOADED_IMAGE_PROTOCOL_GUID)) + return handle_protocol_loaded_image(h, data); + + if (!efi_guidcmp(*uuid, EFI_LOAD_FILE2_PROTOCOL_GUID)) + return device_handle_protocol(h, uuid, data); + + return EFI_UNSUPPORTED; +} + +/* + * LocateProtocol() finds the first device handle that support Protocol, and + * returns a pointer to the protocol interface from that handle in Interface. + * If no protocol instances are found, then Interface is set to NULL + */ +static efi_status_t __efiapi emulator_locate_protocol(efi_guid_t *uuid, + void *registration, void **interface) +{ + if (!efi_guidcmp(*uuid, EFI_TCG2_PROTOCOL_GUID)) { + return EFI_UNSUPPORTED; + } else if (!efi_guidcmp(*uuid, EFI_CC_MEASUREMENT_PROTOCOL_GUID)) { + return EFI_UNSUPPORTED; + } else if (!efi_guidcmp(*uuid, EFI_RNG_PROTOCOL_GUID)) { + *interface = &emulator_rng; + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + +/* each pair is {efi_guid_t *, void *} */ +static efi_status_t __efiapi emulator_install_multiple_protocol_interfaces(efi_handle_t *handle, ...) +{ + efi_status_t ret = EFI_SUCCESS; + efi_guid_t *guid; + void *proto; + va_list args; + int i; + + if (*handle == 0) + ret = device_create_handle(handle); + + va_start(args, handle); + for (i = 0; ret == EFI_SUCCESS; i++) { + /* If protocol is NULL, then it's the end of the list */ + guid = va_arg(args, efi_guid_t *); + if (guid == NULL) + break; + proto = va_arg(args, void *); + + if (!efi_guidcmp(*guid, EFI_DEVICE_PATH_PROTOCOL_GUID)) { + ret = device_attach_dev_path(*handle, proto); + continue; + } + + /* install one protocol on the device */ + ret = device_register_protocol(*handle, *guid, proto); + } + va_end(args); + + return ret; +} + +static efi_status_t __efiapi emulator_uninstall_multiple_protocol_interfaces(efi_handle_t, ...) +{ + return EFI_UNSUPPORTED; +} + + +// 2do +static efi_status_t __efiapi emulator_allocate_pages(int alloc_type, int mem_type, + unsigned long nr_pages, efi_physical_addr_t *addr) +{ + return __emulator_allocate_pages(alloc_type, mem_type, nr_pages, addr); +} + +// 2do +static efi_status_t __efiapi emulator_free_pages(efi_physical_addr_t addr, + unsigned long nr_4KB) +{ + return EFI_SUCCESS; + +} + +static efi_status_t __efiapi emulator_allocate_pool(int mem_type, unsigned long sz, + void **pool) +{ + return __emulator_allocate_pool(mem_type, sz, pool); + +} + +static efi_status_t __efiapi emulator_free_pool(void *pool) +{ + return EFI_SUCCESS; + +} + +/* memmove() alias as memcpy() */ +static void __efiapi emulator_copy_mem(void *dest, const void *src, unsigned long count) +{ + char *tmp; + const char *s; + + if (dest <= src) { + tmp = dest; + s = src; + while (count--) + *tmp++ = *s++; + } else { + tmp = dest; + tmp += count; + s = src; + s += count; + while (count--) + *--tmp = *--s; + } + +} + +static void __efiapi emulator_set_mem(void *dst, unsigned long cnt, unsigned char val) +{ + unsigned char *dst_ptr = (char *)dst; + unsigned long i; + + for (i = 0; i < cnt; i++) + dst_ptr[i] = val; +} + +static efi_status_t __efiapi emulator_stall(unsigned long ms) +{ + + return EFI_SUCCESS; +} + +static efi_status_t __efiapi emulator_locate_handle(int, efi_guid_t *, + void *, unsigned long *, + efi_handle_t *) +{ + return EFI_UNSUPPORTED; +} + +/* + * locates all devices on DevicePath that support Protocol and returns the + * handle to the device that is closest to DevicePath + */ +static efi_status_t __efiapi emulator_locate_device_path(efi_guid_t *guid, + efi_device_path_protocol_t **dp, efi_handle_t *handle) +{ + efi_status_t ret; + /* Only one device implements this protocol, so dp can be ignored */ + if (!efi_guidcmp(*guid, EFI_LOAD_FILE2_PROTOCOL_GUID)) { + ret = device_find_handle_by_path(dp, handle); + return ret; + } + + return EFI_NOT_FOUND; +} + +static efi_status_t __efiapi emulator_install_configuration_table(efi_guid_t *uuid, + void *table) +{ + efi_config_table_t *t = (efi_config_table_t *)systabs.tables; + int i; + + for (i = 0; i < systabs.nr_tables; i++, t++) { + if (!efi_guidcmp(t->guid, *uuid)) + return conjoin_table(uuid, table, t); + } + t->guid = *uuid; + t->table = table; + systabs.nr_tables++; + + return EFI_SUCCESS; +} + +/* + * For UKI, systemd-stub loads linux image and start image. + * @path: The DeviceHandle specific file path from which the image is loaded + */ +static efi_status_t __efiapi emulator_load_image(bool boot_policy, + efi_handle_t parent_image, efi_device_path_protocol_t *path, + void *src_buf, unsigned long src_sz, + efi_handle_t *handle) +{ + struct efi_pe_instance *inst; + char *dst; + + /* copy the in-memory image */ + if (!!src_buf) { + dst = aligned_alloc(8, src_sz); + if (!dst) { + printf("OOM\n"); + return EFI_OUT_OF_RESOURCES; + } + emulator_copy_mem(dst, src_buf, src_sz); + inst = allocate_pe_instance(dst, src_sz); + inst->handle = inst; + *handle = inst->handle; + /* EFI_SIMPLE_FILE_SYSTEM_PROTOCOL or EFI_LOAD_FILE_PROTOCOL */ + } else { + + } + return EFI_SUCCESS; +} + +static efi_status_t __efiapi emulator_start_image(efi_handle_t handle, + unsigned long *exit_data_sz, efi_char16_t **exit_data) +{ + + struct efi_pe_instance *inst; + + inst = get_instance_by_handle(handle); + if (unlikely(!inst)) { + printf("error: can not find image\n"); + return EFI_NOT_FOUND; + } + load_kernel_pe(inst, &systabs); + + return EFI_SUCCESS; +} + +/* + * As the final stage, destroy the boottime context, e.g. release the memory + * occupied by some data struct. + */ +static efi_status_t __efiapi emulator_exit_boot_services(efi_handle_t handle, + unsigned long map_key) +{ + return EFI_SUCCESS; +} + +static efi_boot_services_t bt_services = { + .handle_protocol = emulator_handle_protocol, + .locate_protocol = emulator_locate_protocol, + .install_multiple_protocol_interfaces = emulator_install_multiple_protocol_interfaces, + .uninstall_multiple_protocol_interfaces = emulator_uninstall_multiple_protocol_interfaces, + + .allocate_pool = emulator_allocate_pool, + .free_pool = emulator_free_pool, + .allocate_pages = emulator_allocate_pages, + .free_pages = emulator_free_pages, + .copy_mem = emulator_copy_mem, + .set_mem = emulator_set_mem, + .get_memory_map = emulator_get_memory_map, + + .stall = emulator_stall, + + .locate_handle = emulator_locate_handle, + .locate_device_path = emulator_locate_device_path, + .install_configuration_table = emulator_install_configuration_table, + .load_image = emulator_load_image, + .start_image = emulator_start_image, + .exit_boot_services = emulator_exit_boot_services, +}; + +static efi_char16_t vendor[] = u"Linux Kexec"; + +static efi_status_t unsupported_func(void) +{ + return EFI_UNSUPPORTED; +} + +efi_system_table_t systabs = { + .hdr = { + .signature = EFI_SYSTEM_TABLE_SIGNATURE, + }, + .fw_vendor = (unsigned long)vendor, + .fw_revision = 0x10001, + .con_in_handle = 0x0, + .con_in = (efi_simple_text_input_protocol_t *)unsupported_func, + .con_out_handle = 0x0, + .con_out = &text_out, + .stderr_handle = 0x0, + /* Per specification, A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL */ + .stderr = (unsigned long)unsupported_func, + /* Passed in by the 1st kernel */ + .runtime = NULL, + .boottime = &bt_services, + .nr_tables = 0, + .tables = 0, +}; + +static efi_rt_properties_table_t rt_support = { + .runtime_services_supported = 0, +}; + +int initialize_emulator_service(struct efi_emulator_param *param) +{ + + efi_config_table_t *tables; + unsigned int i; + + printf("initialize_emulator_service, dtb=0x%lx, mempool_start=0x%lx, end:0x%lx\n", + param->dtb, param->mempool_start, param->mempool_start + param->mempool_sz); + emulator_param = param; + print_enabled = param->print_enabled; + i = param->rt_info.systab_nr_tables; + systabs.tables = (unsigned long)¶m->rt_info.systab_tables; + tables = param->rt_info.systab_tables; + tables[i].guid = DEVICE_TREE_GUID; + tables[i].table = (void *)param->dtb; + i++; + if (!param->noefi_boot) { + rt_support.runtime_services_supported = param->rt_info.runtime_supported_mask; + } + tables[i].guid = EFI_RT_PROPERTIES_TABLE_GUID; + tables[i].table = (void *)&rt_support; + i++; + systabs.nr_tables = i; + + systabs.runtime = (efi_runtime_services_t *)param->rt_info.runtime; + return 0; +} diff --git a/drivers/firmware/efi/efi_emulator/device_handle.c b/drivers/firmware/efi/efi_emulator/device_handle.c new file mode 100644 index 0000000000000..6e15979f62e9a --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/device_handle.c @@ -0,0 +1,138 @@ +//SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "emulator.h" + +static LIST_HEAD(devices_list); + +struct protocol_entry { + efi_guid_t guid; + void *proto; +}; + +/* + * Drivers can implement their own version of efi_load_file_protocol_t. This represents one. + * For example, refer to systemd-stub initrd_load_file() + * + * BS->InstallMultipleProtocolInterfaces(handle, ...) + * BS->LocateDevicePath(guid, dp, handle) + * BS->HandleProtocol(handle, guid, interface) + * interface->func() + * This struct can be abstracted to serve EFI_BOOT_SERVICES.LocateDevicePath(guid, dp, handle) + */ +struct device_instance { + struct list_head node; + efi_handle_t handle; + /* + * Quote UEFI specification: + * 'It is illegal to have two handles in the handle database with identical device paths' + */ + efi_device_path_protocol_t *dp; + /* For simplity, keep the capacity at 8 for the time being */ + struct protocol_entry entries[8]; +}; + +static struct device_instance *find_device_by_handle(efi_handle_t h) +{ + struct device_instance *inst; + + list_for_each_entry(inst, &devices_list, node) { + if (inst->handle == h) + return inst; + } + + return NULL; +} + +efi_status_t device_create_handle(efi_handle_t *handle) +{ + struct device_instance *inst; + int sz; + + sz = sizeof(struct device_instance); + inst = aligned_alloc(4, sz); + memset(inst, 0, sz); + emulator_list_add(&inst->node, &devices_list); + inst->handle = (efi_handle_t)inst; + *handle = inst->handle; + + return EFI_SUCCESS; +} + +efi_status_t device_attach_dev_path(efi_handle_t h, efi_device_path_protocol_t *dp) +{ + struct device_instance *inst = (struct device_instance *)h; + int sz; + + sz = efi_device_path_size(dp); + inst->dp = aligned_alloc(4, sz); + /* clone the device path */ + efi_device_path_clone(inst->dp, dp); + + return EFI_SUCCESS; +} + +/* + * BS->InstallMultipleProtocolInterfaces() calls down to here. + * + * A driver implements its own efi_load_file_protocol_t. + * + * According to EFI_LOAD_FILE2_PROTOCOL.LoadFile(), only + * efi_device_path_protocol_t is required. + */ +efi_status_t device_register_protocol(efi_handle_t handle, efi_guid_t guid, + void *proto) +{ + struct device_instance *inst; + + inst = find_device_by_handle(handle); + if (!inst) + return EFI_NOT_FOUND; + + for (int i = 0; i < 8; i++) { + if (!efi_guidcmp(inst->entries[i].guid, NULL_GUID)) { + inst->entries[i].guid = guid; + inst->entries[i].proto = proto; + return EFI_SUCCESS; + } + } + + return EFI_OUT_OF_RESOURCES; +} + +efi_status_t device_find_handle_by_path(efi_device_path_protocol_t **dp, + efi_handle_t *h) +{ + struct device_instance *inst; + int ret = -1; + + list_for_each_entry(inst, &devices_list, node) { + ret = efi_device_path_compare(*dp, inst->dp); + if (!ret) { + *h = inst->handle; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +efi_status_t device_handle_protocol(efi_handle_t h, efi_guid_t *uuid, void **data) +{ + struct device_instance *inst; + + list_for_each_entry(inst, &devices_list, node) { + if (inst->handle == h) { + for (int i = 0; i < 8; i++) { + if (!efi_guidcmp(inst->entries[i].guid, *uuid)) { + *data = inst->entries[i].proto; + return EFI_SUCCESS; + } + } + /* no need to try other handles */ + return EFI_NOT_FOUND; + } + } + + return EFI_NOT_FOUND; +} diff --git a/drivers/firmware/efi/efi_emulator/earlycon.h b/drivers/firmware/efi/efi_emulator/earlycon.h new file mode 100644 index 0000000000000..189af549d5af2 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/earlycon.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#include +#include +#include +#include + +struct earlycon { + bool (*match)(struct efi_emulator_param *param, const char *name); + int (*put_str)(const char *str, void *data); + void (*reset)(void *data); + void *data; + const char *name; +}; + +extern struct earlycon pl011; + +extern int pl011_puts(const char *str); +void setup_earlycon(struct efi_emulator_param *param); diff --git a/drivers/firmware/efi/efi_emulator/efi_emulator.S b/drivers/firmware/efi/efi_emulator/efi_emulator.S new file mode 100644 index 0000000000000..fb52593ba3b2e --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/efi_emulator.S @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + + .section .rodata, "a" + + .align 8 +_efi_emulator_start: + .globl _efi_emulator_start + .incbin "drivers/firmware/efi/efi_emulator/emulator.raw" + + .align 8 +_efi_emulator_end: + .globl _efi_emulator_end diff --git a/drivers/firmware/efi/efi_emulator/emulator.h b/drivers/firmware/efi/efi_emulator/emulator.h new file mode 100644 index 0000000000000..9b38d1ee69cea --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/emulator.h @@ -0,0 +1,106 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include + +/* Included from drivers/firmware/efi/libstub */ +#include + +#define EMULATOR_BASE_ADDR 0 + +typedef union efi_rng_protocol efi_rng_protocol_t; + +union efi_rng_protocol { + struct { + efi_status_t (__efiapi *get_info)(efi_rng_protocol_t *, + unsigned long *, + efi_guid_t *); + efi_status_t (__efiapi *get_rng)(efi_rng_protocol_t *, + efi_guid_t *, unsigned long, + u8 *out); + }; + struct { + u32 get_info; + u32 get_rng; + } mixed_mode; +}; + +typedef efi_status_t (*uefi_pe_entry)(efi_handle_t handle, efi_system_table_t *systab); + +struct efi_pe_instance { + struct list_head node; + efi_handle_t handle; + char *image_file_buf; + unsigned long image_file_size; + /* load address for the instance */ + unsigned long image_base; + unsigned long image_size; + uefi_pe_entry entry; +}; + +static inline void __emulator_list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + WRITE_ONCE(prev->next, new); +} + +static inline void emulator_list_add(struct list_head *new, struct list_head *head) +{ + __emulator_list_add(new, head, head->next); +} + +extern bool print_enabled; +extern struct efi_emulator_param *emulator_param; +extern efi_tcg2_protocol_t emulator_tcg2; +extern efi_cc_protocol_t emulator_cc; +extern efi_rng_protocol_t emulator_rng; +extern efi_simple_text_output_protocol_t text_out; +extern efi_system_table_t systabs; +extern char *heap_start, *heap_end, *heap_cur; + +void *aligned_alloc(size_t alignment, size_t size); +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +int strcmp(const char *cs, const char *ct); +size_t wcslen(const wchar_t *str); +int wcscmp(const wchar_t *s1, const wchar_t *s2); +int printf(const char *format, ...); +void print_ucs2_string(efi_char16_t* ucs2_str); +extern unsigned long find_image_base_for_handle(efi_handle_t handle); + +efi_status_t device_create_handle(efi_handle_t *handle); +efi_status_t device_attach_dev_path(efi_handle_t h, efi_device_path_protocol_t *dp); +efi_status_t device_register_protocol(efi_handle_t handle, efi_guid_t guid, + void *proto); +efi_status_t device_find_handle_by_path(efi_device_path_protocol_t **dp, + efi_handle_t *h); +efi_status_t device_handle_protocol(efi_handle_t h, efi_guid_t *uuid, + void **data); + +int efi_device_path_compare(efi_device_path_protocol_t *path1, + efi_device_path_protocol_t *path2); +size_t efi_device_path_size(efi_device_path_protocol_t *path); +int efi_device_path_clone(efi_device_path_protocol_t *dst, + efi_device_path_protocol_t *dp); + +efi_status_t __emulator_allocate_pages(int alloc_type, int mem_type, + unsigned long nr_pages, efi_physical_addr_t *addr); +efi_status_t __emulator_allocate_pool(int mem_type, unsigned long sz, + void **pool); +efi_status_t emulator_get_memory_map(unsigned long *map_sz, + void *memmap, unsigned long *map_key, unsigned long *desc_sz, + unsigned int *desc_version); + +efi_status_t conjoin_table(efi_guid_t *uuid, void *table, efi_config_table_t *t); + +struct efi_pe_instance *get_instance_by_handle(efi_handle_t h); +struct efi_pe_instance *allocate_pe_instance(char *file_buf, unsigned long size); +int initialize_emulator_service(struct efi_emulator_param *param); +void initialize_heap(struct efi_emulator_param *param); +void load_kernel_pe(struct efi_pe_instance *inst, efi_system_table_t *systabs); +void emulator_main(struct efi_emulator_param *param); +void emulator_entry(struct efi_emulator_param *param); + diff --git a/drivers/firmware/efi/efi_emulator/entry.c b/drivers/firmware/efi/efi_emulator/entry.c new file mode 100644 index 0000000000000..e5a31bd303858 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/entry.c @@ -0,0 +1,61 @@ +//SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include + +#include "emulator.h" +#include "earlycon.h" + +extern void enable_sctlr_el1(unsigned long scratch_reg); +static void arch_handle_mmu(struct efi_emulator_param *param) +{ + if (!param->mmu_on && param->pgd_root) { + } +} + +extern const Elf64_Rela _rela_start[], _rela_end[]; + +static void noinline arch_reloc_fixup(long delta) +{ + unsigned long *apply_addr, res; + Elf64_Rela *rela; + + /* fix rela */ + for (rela = (Elf64_Rela *)_rela_start; rela < _rela_end; rela++) { + //todo counterpart of R_AARCH64_RELATIVE on riscv + if (ELF64_R_TYPE(rela->r_info) != R_AARCH64_RELATIVE) + continue; + apply_addr = (unsigned long *)(rela->r_offset + delta); + res = rela->r_addend + delta; + *apply_addr = res; + } + // todo flush cache + +} + +/* + * Ensure this entry and @param is in the mapping before jump to it. + * It should be PIC and at the beginning of emulator. + * It should be memory aligned + */ +void emulator_main(struct efi_emulator_param *param) +{ + long delta = param->load_address - EMULATOR_BASE_ADDR; + struct efi_pe_instance *inst; + + arch_handle_mmu(param); + arch_reloc_fixup(delta); + setup_earlycon(param); + printf("param:0x%lx, delta=0x%lx\n", (unsigned long)param, delta); + printf("kernel_img_start:0x%lx, sz:0x%lx\n", (unsigned long)param->kernel_img_start, (unsigned long)param->kernel_img_sz); + initialize_emulator_service(param); + initialize_heap(param); + printf(" load_kernel_pe\n"); + + inst = allocate_pe_instance((char *)param->kernel_img_start, + param->kernel_img_sz); + load_kernel_pe(inst, &systabs); +} diff --git a/drivers/firmware/efi/efi_emulator/head.S b/drivers/firmware/efi/efi_emulator/head.S new file mode 100644 index 0000000000000..705ff131e5fab --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/head.S @@ -0,0 +1,10 @@ +//SPDX-License-Identifier: GPL-2.0 +.section ".head.text","ax" + +/* x0 holds the physical address of emulator_param */ +emulator_entry: + ldr x1, [x0] + mov sp, x1 + adrp x2, emulator_main + add x2, x2, #:lo12:emulator_main + br x2 diff --git a/drivers/firmware/efi/efi_emulator/lib.c b/drivers/firmware/efi/efi_emulator/lib.c new file mode 100644 index 0000000000000..f12aa40405650 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/lib.c @@ -0,0 +1,73 @@ +//SPDX-License-Identifier: GPL-2.0 +#include "emulator.h" + +void *memcpy(void *dest, const void *src, size_t count) +{ + char *tmp = dest; + const char *s = src; + + while (count--) + *tmp++ = *s++; + return dest; +} + +void *memset(void *s, int c, size_t count) +{ + char *xs = s; + + while (count--) + *xs++ = c; + return s; +} + +int memcmp(const void *cs, const void *ct, size_t count) +{ + const unsigned char *su1, *su2; + int res = 0; + + for (su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) + if ((res = *su1 - *su2) != 0) + break; + return res; +} + +size_t strlen(const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +int strcmp(const char *cs, const char *ct) +{ + unsigned char c1, c2; + + while (1) { + c1 = *cs++; + c2 = *ct++; + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + } + return 0; +} + +int wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + while (*s1 && (*s1 == *s2)) { + s1++; + s2++; + } + return (int)(*s1 - *s2); +} + +size_t wcslen(const wchar_t *str) +{ + const wchar_t *s; + + for (s = str; *s; ++s); + return (s - str); +} diff --git a/drivers/firmware/efi/efi_emulator/memory.c b/drivers/firmware/efi/efi_emulator/memory.c new file mode 100644 index 0000000000000..518ab7f30020e --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/memory.c @@ -0,0 +1,27 @@ +//SPDX-License-Identifier: GPL-2.0 +#include "emulator.h" + +char *heap_start, *heap_end, *heap_cur; + +void initialize_heap(struct efi_emulator_param *param) +{ + heap_start = (char *)param->mempool_start; + heap_end = heap_start + param->mempool_sz; + heap_cur = heap_start; +} + +//2do, the memory management is more complicated since we need to distinguish EFI_BOOT_SERVICE, RUNTIME, LOADER memory descr + +void *aligned_alloc(size_t alignment, size_t size) +{ + char *p; + + p = (char *)ALIGN((unsigned long)heap_cur, alignment); + heap_cur = p + size; + + //todo, update the efi_memory_desc to include this page, if it crosses the PAGE boundary + //as EFI_BOOT_SERVICE, + return p; +} + + diff --git a/drivers/firmware/efi/efi_emulator/memory_api.c b/drivers/firmware/efi/efi_emulator/memory_api.c new file mode 100644 index 0000000000000..bd57942cb0410 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/memory_api.c @@ -0,0 +1,74 @@ +//SPDX-License-Identifier: GPL-2.0 +#include +#include + +#include "emulator.h" + +/* + * mem_type affects the allocated chunk in efi_memory_desc_t's type. Later, + * kernel can know whether to reclaim them. + */ +efi_status_t __emulator_allocate_pages(int alloc_type, int mem_type, + unsigned long nr_pages, efi_physical_addr_t *addr) +{ + efi_physical_addr_t res; + efi_status_t status; + + if (alloc_type == EFI_ALLOCATE_ANY_PAGES) { + res = (efi_physical_addr_t)aligned_alloc(PAGE_SIZE, nr_pages << PAGE_SHIFT); + *addr = res; + status = EFI_SUCCESS; + } else if (alloc_type == EFI_ALLOCATE_MAX_ADDRESS) { + //tmp + res = (efi_physical_addr_t)aligned_alloc(PAGE_SIZE, nr_pages << PAGE_SHIFT); + *addr = res; + status = EFI_SUCCESS; + /* e.g. aarch64 kimage loaded alignment */ + } else if (alloc_type == EFI_ALLOCATE_ADDRESS) { + //tmp, just aligned on 2MB as aarch64 boot protocol + res = (efi_physical_addr_t)aligned_alloc(1<<21, nr_pages << PAGE_SHIFT); + *addr = res; + status = EFI_SUCCESS; + } + + return status; +} + +//todo +efi_status_t __emulator_allocate_pool(int mem_type, unsigned long sz, + void **pool) +{ + void *res; + + res = aligned_alloc(sizeof(unsigned long), sz); + *pool = res; + return EFI_SUCCESS; +} + +/* @memmap: only holds efi_memory_desc */ +efi_status_t emulator_get_memory_map(unsigned long *map_sz, + void *memmap, unsigned long *map_key, unsigned long *desc_sz, + unsigned int *desc_version) +{ + //todo rt_info.memmap will be accessed by kernel, so it should be marked as reserved + struct efi_boot_memmap *p = &emulator_param->rt_info.memmap; + //efi_memory_desc_t *desc = p->map; + + if (!map_sz || !desc_sz) + return EFI_INVALID_PARAMETER; + if (*map_sz < p->map_size || !memmap) { + *map_sz = p->map_size; + *desc_sz = p->desc_size; + return EFI_BUFFER_TOO_SMALL; + } + + /* desc range size*/ + *map_sz = p->map_size; + memcpy(memmap, p->map, p->map_size); + if (!!desc_sz) + *desc_sz = p->desc_size; + if (!!desc_version) + *desc_version = p->desc_ver; + + return EFI_SUCCESS; +} diff --git a/drivers/firmware/efi/efi_emulator/misc.c b/drivers/firmware/efi/efi_emulator/misc.c new file mode 100644 index 0000000000000..0d6e92bc539cf --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/misc.c @@ -0,0 +1,43 @@ +//SPDX-License-Identifier: GPL-2.0 + +#include +#include + +#include "emulator.h" + +static efi_status_t __efiapi emulator_get_rng(efi_rng_protocol_t * this, + efi_guid_t *uuid, unsigned long size, + u8 *out) +{ + /* in fact, disable aslr */ + *out = 0; + return EFI_SUCCESS; +} + +efi_rng_protocol_t emulator_rng = { + .get_rng = emulator_get_rng, +}; + +static efi_status_t __efiapi emulator_get_memory_attributes( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64 *) +{ + return EFI_SUCCESS; +} + +static efi_status_t __efiapi emulator_set_memory_attributes( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64) +{ + return EFI_SUCCESS; +} + +static efi_status_t __efiapi emulator_clear_memory_attributes( + efi_memory_attribute_protocol_t *, efi_physical_addr_t, u64, u64) +{ + return EFI_SUCCESS; +} + +efi_memory_attribute_protocol_t emulator_memory_attribute = { + .get_memory_attributes = emulator_get_memory_attributes, + .set_memory_attributes = emulator_set_memory_attributes, + .clear_memory_attributes = emulator_clear_memory_attributes, +}; diff --git a/drivers/firmware/efi/efi_emulator/pe_loader.c b/drivers/firmware/efi/efi_emulator/pe_loader.c new file mode 100644 index 0000000000000..9113827fd7c9a --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/pe_loader.c @@ -0,0 +1,173 @@ +//SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include "emulator.h" + +#define VALID_PAYLOAD (IMAGE_SCN_CNT_CODE |IMAGE_SCN_CNT_INITIALIZED_DATA |IMAGE_SCN_CNT_UNINITIALIZED_DATA) + +/* Refer to PECOFF spec, 'Base Relocation Types' */ +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_DIR64 10 + +unsigned long find_image_base_for_handle(efi_handle_t handle) +{ + struct efi_pe_instance *inst; + + inst = get_instance_by_handle(handle); + if (!inst) + return (unsigned long)-1; + return inst->image_base; +} + +typedef struct __packed base_relocation_block { + uint32_t page_rva; + uint32_t block_size; + struct { + uint16_t offset : 12; + uint16_t type : 4; /* higher 4-bits in Word */ + } entries[]; +} base_relocation_block_t; + +static int pe_image_handle_base_relocation(unsigned long base_reloc_table, + unsigned long sz, unsigned long load_addr, unsigned long delta) +{ + base_relocation_block_t *blk = (base_relocation_block_t *)base_reloc_table; + unsigned long page_addr, *target_addr, value; + uint32_t i, array_num; + + for (; (unsigned long)blk < (base_reloc_table + sz); + blk = (base_relocation_block_t *)((unsigned char*)blk + blk->block_size)) { + + /* block_size includes the total base_relocation_block structure */ + array_num = (blk->block_size - sizeof(base_relocation_block_t)) >> 1; + page_addr = blk->page_rva + load_addr; + for (i = 0; i < array_num; i++) { + switch (blk->entries[i].type) { + case IMAGE_REL_BASED_ABSOLUTE: + continue; + case IMAGE_REL_BASED_DIR64: + target_addr = (unsigned long *)(page_addr + blk->entries[i].offset); + value = *target_addr + delta; + *target_addr = value; + continue; + default: + printf("ERR: unhandled reloc type: %d\n"); + return -1; + } + } + } + + return 0; +} + +/* + * @pe_hdr_offset supplies the size of Dos Header and Stub. + */ +static int load_pe(char *file_buf, unsigned long buf_sz, unsigned long pe_hdr_offset, + struct efi_pe_instance *inst) +{ + unsigned long exec_sz, load_addr, delta; + unsigned long base_reloc_table, sz; + struct pe_hdr *pe_hdr; + struct pe32plus_opt_hdr *opt_hdr; + struct data_directory *dir; + struct data_dirent *dirent; + struct section_header *sect_hdr; + int section_nr, i; + char *pe_part_buf, *src, *dst; + printf("load_pe\n"); + pe_part_buf = file_buf + pe_hdr_offset; + pe_hdr = (struct pe_hdr *)pe_part_buf; + if (pe_hdr->opt_hdr_size == 0) { + printf("ERR: optional header is missing\n"); + return -1; + } + section_nr = pe_hdr->sections; + opt_hdr = (struct pe32plus_opt_hdr *)(pe_part_buf + sizeof(struct pe_hdr)); + sect_hdr = (struct section_header *)((char *)opt_hdr + pe_hdr->opt_hdr_size); + exec_sz = opt_hdr->image_size; + + /* + * PE header must be loaded since some efi stubs parse them e.g. systemd-stub + */ + load_addr = (unsigned long)aligned_alloc(opt_hdr->section_align, exec_sz); + + /* + * Each section has the same delta. Got the delta based on the first + * section's RVA. + */ + delta = load_addr - opt_hdr->image_base; + /* copy PE headers */ + memcpy((void *)load_addr, file_buf, opt_hdr->header_size); + + /* copy section to segment */ + for (i = 0; i < section_nr; i++) { + printf("section: %s, relocs: %u\n", sect_hdr->name, sect_hdr->num_relocs); + if (!(sect_hdr->flags & VALID_PAYLOAD)) { + sect_hdr++; + continue; + } + /* data_addr is relative to the whole file */ + src = file_buf + sect_hdr->data_addr; + dst = (char *)(sect_hdr->virtual_address + load_addr); + memcpy(dst, src, sect_hdr->raw_data_size); + printf("virtual_address: 0x%u, src: %u, dst: %u\n", sect_hdr->virtual_address, src, dst); + /* + * The SizeOfRawData is rounded but the VirtualSize is not, hence + * the former can be greater than latter. + */ + if (sect_hdr->virtual_size > sect_hdr->raw_data_size) + memset(dst + sect_hdr->raw_data_size, 0, sect_hdr->virtual_size - sect_hdr->raw_data_size); + sect_hdr++; + } + + /* If there are relocs */ + if (pe_hdr->opt_hdr_size > + (offsetof(struct data_directory, base_relocations) + sizeof(struct pe32plus_opt_hdr))) { + dir = (void *)pe_hdr + sizeof(struct pe_hdr) + sizeof(struct pe32plus_opt_hdr); + dirent = &dir->base_relocations; + base_reloc_table = dirent->virtual_address + load_addr; + sz = dirent->size; + pe_image_handle_base_relocation(base_reloc_table, sz, load_addr, delta); + } + + /* Since gcc adheres to ABI, using the current SP is fine for new image instance */ + + inst->entry = (uefi_pe_entry)(opt_hdr->entry_point + load_addr); + inst->image_base = load_addr; + inst->image_size = opt_hdr->image_size; + + printf("entry_point:0x%lx, delta:0x%lx, final inst's entry at:0x%lx\n", + opt_hdr->entry_point, delta, inst->entry); + return 0; +} + +static int parse_kernel_pe(struct efi_pe_instance *inst) +{ + char *buf = (char *)inst->image_file_buf; + u32 pe_hdr_offset; + + pe_hdr_offset = *((u32 *)(buf + 0x3c)); + buf += pe_hdr_offset; + if (!!memcmp(buf, "PE\0\0", 4)) { + printf("Not a PE file\n"); + return -1; + } + + load_pe((char *)inst->image_file_buf, inst->image_file_size, + pe_hdr_offset, inst); + + return 0; +} + +void load_kernel_pe(struct efi_pe_instance *inst, efi_system_table_t *systabs) +{ + int ret; + + ret = parse_kernel_pe(inst); + if (ret < 0) + return; + (*(inst->entry))(inst->handle, systabs); + +} diff --git a/drivers/firmware/efi/efi_emulator/printf.c b/drivers/firmware/efi/efi_emulator/printf.c new file mode 100644 index 0000000000000..4d8daf91b426b --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/printf.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* The most of this file is copied from arch/x86/boot/printf.c */ + +#include +#include + +#include "earlycon.h" +#include "emulator.h" + +static int skip_atoi(const char **s) +{ + int i = 0; + + while (isdigit(**s)) + i = i * 10 + *((*s)++) - '0'; + return i; +} + +#define ZEROPAD 1 /* pad with zero */ +#define SIGN 2 /* unsigned/signed long */ +#define PLUS 4 /* show plus */ +#define SPACE 8 /* space if plus */ +#define LEFT 16 /* left justified */ +#define SMALL 32 /* Must be 32 == 0x20 */ +#define SPECIAL 64 /* 0x */ + +#define __do_div(n, base) ({ \ +int __res; \ +__res = ((unsigned long) n) % (unsigned) base; \ +n = ((unsigned long) n) / (unsigned) base; \ +__res; }) + +static char *number(char *str, long num, int base, int size, int precision, + int type) +{ + /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ + static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ + + char tmp[66]; + char c, sign, locase; + int i; + + /* locase = 0 or 0x20. ORing digits or letters with 'locase' + * produces same digits or (maybe lowercased) letters */ + locase = (type & SMALL); + if (type & LEFT) + type &= ~ZEROPAD; + if (base < 2 || base > 16) + return NULL; + c = (type & ZEROPAD) ? '0' : ' '; + sign = 0; + if (type & SIGN) { + if (num < 0) { + sign = '-'; + num = -num; + size--; + } else if (type & PLUS) { + sign = '+'; + size--; + } else if (type & SPACE) { + sign = ' '; + size--; + } + } + if (type & SPECIAL) { + if (base == 16) + size -= 2; + else if (base == 8) + size--; + } + i = 0; + if (num == 0) + tmp[i++] = '0'; + else + while (num != 0) + tmp[i++] = (digits[__do_div(num, base)] | locase); + if (i > precision) + precision = i; + size -= precision; + if (!(type & (ZEROPAD + LEFT))) + while (size-- > 0) + *str++ = ' '; + if (sign) + *str++ = sign; + if (type & SPECIAL) { + if (base == 8) + *str++ = '0'; + else if (base == 16) { + *str++ = '0'; + *str++ = ('X' | locase); + } + } + if (!(type & LEFT)) + while (size-- > 0) + *str++ = c; + while (i < precision--) + *str++ = '0'; + while (i-- > 0) + *str++ = tmp[i]; + while (size-- > 0) + *str++ = ' '; + return str; +} + +size_t strnlen(const char *s, size_t count) +{ + const char *sc; + + for (sc = s; count-- && *sc != '\0'; ++sc) + /* nothing */; + return sc - s; +} + +int vsprintf(char *buf, const char *fmt, va_list args) +{ + int len; + unsigned long num; + int i, base; + char *str; + const char *s; + + int flags; /* flags to number() */ + + int field_width; /* width of output field */ + int precision; /* min. # of digits for integers; max + number of chars for from string */ + int qualifier; /* 'h', 'l', or 'L' for integer fields */ + + for (str = buf; *fmt; ++fmt) { + if (*fmt != '%') { + *str++ = *fmt; + continue; + } + + /* process flags */ + flags = 0; + repeat: + ++fmt; /* this also skips first '%' */ + switch (*fmt) { + case '-': + flags |= LEFT; + goto repeat; + case '+': + flags |= PLUS; + goto repeat; + case ' ': + flags |= SPACE; + goto repeat; + case '#': + flags |= SPECIAL; + goto repeat; + case '0': + flags |= ZEROPAD; + goto repeat; + } + + /* get field width */ + field_width = -1; + if (isdigit(*fmt)) + field_width = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + field_width = va_arg(args, int); + if (field_width < 0) { + field_width = -field_width; + flags |= LEFT; + } + } + + /* get the precision */ + precision = -1; + if (*fmt == '.') { + ++fmt; + if (isdigit(*fmt)) + precision = skip_atoi(&fmt); + else if (*fmt == '*') { + ++fmt; + /* it's the next argument */ + precision = va_arg(args, int); + } + if (precision < 0) + precision = 0; + } + + /* get the conversion qualifier */ + qualifier = -1; + if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') { + qualifier = *fmt; + ++fmt; + } + + /* default base */ + base = 10; + + switch (*fmt) { + case 'c': + if (!(flags & LEFT)) + while (--field_width > 0) + *str++ = ' '; + *str++ = (unsigned char)va_arg(args, int); + while (--field_width > 0) + *str++ = ' '; + continue; + + case 's': + s = va_arg(args, char *); + len = strnlen(s, precision); + + if (!(flags & LEFT)) + while (len < field_width--) + *str++ = ' '; + for (i = 0; i < len; ++i) + *str++ = *s++; + while (len < field_width--) + *str++ = ' '; + continue; + + case 'p': + if (field_width == -1) { + field_width = 2 * sizeof(void *); + flags |= ZEROPAD; + } + str = number(str, + (unsigned long)va_arg(args, void *), 16, + field_width, precision, flags); + continue; + + case 'n': + if (qualifier == 'l') { + long *ip = va_arg(args, long *); + *ip = (str - buf); + } else { + int *ip = va_arg(args, int *); + *ip = (str - buf); + } + continue; + + case '%': + *str++ = '%'; + continue; + + /* integer number formats - set up the flags and "break" */ + case 'o': + base = 8; + break; + + case 'x': + flags |= SMALL; + fallthrough; + case 'X': + base = 16; + break; + + case 'd': + case 'i': + flags |= SIGN; + case 'u': + break; + + default: + *str++ = '%'; + if (*fmt) + *str++ = *fmt; + else + --fmt; + continue; + } + if (qualifier == 'l') + num = va_arg(args, unsigned long); + else if (qualifier == 'h') { + num = (unsigned short)va_arg(args, int); + if (flags & SIGN) + num = (short)num; + } else if (flags & SIGN) + num = va_arg(args, int); + else + num = va_arg(args, unsigned int); + str = number(str, num, base, field_width, precision, flags); + } + *str = '\0'; + return str - buf; +} + +int sprintf(char *buf, const char *fmt, ...) +{ + va_list args; + int i; + + va_start(args, fmt); + i = vsprintf(buf, fmt, args); + va_end(args); + return i; +} + +static struct earlycon *con; + +static int puts(const char *s) +{ + if (con) + return con->put_str(s, con->data); + else + return 0; +} + +int printf(const char *fmt, ...) +{ + char printf_buf[1024]; + va_list args; + int printed; + + va_start(args, fmt); + printed = vsprintf(printf_buf, fmt, args); + va_end(args); + + puts(printf_buf); + + return printed; +} + +static char *ucs2_to_ascii(char *ascii_str, efi_char16_t *ucs2_str) +{ + size_t len = 0; + + while (ucs2_str[len] != 0) { + len++; + } + + /* Convert each UCS-2 character to ASCII */ + for (size_t i = 0; i < len; i++) { + if (ucs2_str[i] <= 127) { + /* Character is in ASCII range */ + ascii_str[i] = (char)ucs2_str[i]; + } else { + /* Character is outside ASCII range */ + ascii_str[i] = '?'; + } + } + /* Enforce a null-terminate the ASCII string */ + ascii_str[len] = '\0'; + + return ascii_str; +} + + +/* Convert the UCS-2 string to a ascii string */ +void print_ucs2_string(efi_char16_t *ucs2_str) +{ + char ascii_str[1024]; + char* p = ascii_str; + + ucs2_to_ascii(p, ucs2_str); + /* Print the ascii string */ + printf("%s\n", ascii_str); +} + +static struct earlycon *all_con_types[] = { &pl011, }; + +void setup_earlycon(struct efi_emulator_param *param) +{ + struct earlycon *p; + int i; + + for (i = 0; i < sizeof(all_con_types) / sizeof(struct earlycon *); i++) { + + p = all_con_types[i]; + if (p->match(param, p->name)) { + con = p; + p->reset(p->data); + break; + } + } +} diff --git a/drivers/firmware/efi/efi_emulator/protocol_device_path.c b/drivers/firmware/efi/efi_emulator/protocol_device_path.c new file mode 100644 index 0000000000000..c31dcb8d0016b --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/protocol_device_path.c @@ -0,0 +1,75 @@ +//SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "emulator.h" + +static inline bool is_end_node(efi_device_path_protocol_t *node) +{ + return node->type == EFI_DEV_END_PATH && + node->sub_type == EFI_DEV_END_ENTIRE; +} + +static inline efi_device_path_protocol_t * + next_device_path_node(efi_device_path_protocol_t *node) +{ + return (efi_device_path_protocol_t *)((u8 *)node + node->length); +} + +/* + * efi_device_path_compare - Compare two EFI device paths + * + * Return: 0 if equal, otherwise non-zero + */ +int efi_device_path_compare(efi_device_path_protocol_t *path1, + efi_device_path_protocol_t *path2) +{ + efi_device_path_protocol_t *node1 = path1; + efi_device_path_protocol_t *node2 = path2; + + while (!is_end_node(node1) && !is_end_node(node2)) { + if (node1->type != node2->type || + node1->sub_type != node2->sub_type || + node1->length != node2->length) + return 1; + + node1 = next_device_path_node(node1); + node2 = next_device_path_node(node2); + } + + /* Check if both reached the end */ + if (is_end_node(node1) && is_end_node(node2)) + return 0; + + return 1; +} + +/* + * efi_device_path_size - Calculate the total size of an EFI device path + * @path: Pointer to the first EFI_DEVICE_PATH structure + * + * Return: Total size of the EFI device path + */ +size_t efi_device_path_size(efi_device_path_protocol_t *path) +{ + efi_device_path_protocol_t *node = path; + size_t total_size = 0; + + while (!is_end_node(node)) { + total_size += node->length; + node = next_device_path_node(node); + } + + /* Include the size of the end node */ + total_size += node->length; + + return total_size; +} + +int efi_device_path_clone(efi_device_path_protocol_t *dst, + efi_device_path_protocol_t *dp) +{ + size_t sz = efi_device_path_size(dp); + + memcpy((void *)dst, (void *)dp, sz); + return 0; +} diff --git a/drivers/firmware/efi/efi_emulator/protocol_simple_text_output.c b/drivers/firmware/efi/efi_emulator/protocol_simple_text_output.c new file mode 100644 index 0000000000000..6544bfc7f43dc --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/protocol_simple_text_output.c @@ -0,0 +1,50 @@ +//SPDX-License-Identifier: GPL-2.0 + +#include "emulator.h" + +/* UCS-2 (Universal Coded Character Set) */ +static efi_status_t __efiapi output_string(efi_simple_text_output_protocol_t *simple, + efi_char16_t *str) +{ + if (print_enabled) + print_ucs2_string(str); + return EFI_SUCCESS; +} + +struct simple_text_output_mode { + int32_t max_mode; + int32_t mode; + int32_t attribute; + int32_t cursor_column; + int32_t cursor_row; + bool cursor_visible; +}; + +struct simple_text_output_mode output_mode; + +static efi_status_t __efiapi text_reset( + efi_simple_text_output_protocol_t *this, + bool extended_verification) +{ + return EFI_UNSUPPORTED; +} + +static efi_status_t __efiapi text_set_attribute( + efi_simple_text_output_protocol_t *this, + unsigned int attribute) +{ + return EFI_UNSUPPORTED; +} + +efi_simple_text_output_protocol_t text_out = { + + .reset = text_reset, + .output_string = output_string, + .query_mode = NULL, + .set_mode = NULL, + .set_attribute = text_set_attribute, + .clean_screen = NULL, + .set_cursor_pos = NULL, + .enable_cursor = NULL, + .mode = &output_mode, +}; diff --git a/include/linux/efi_emulator.h b/include/linux/efi_emulator.h new file mode 100644 index 0000000000000..ef235861f3a85 --- /dev/null +++ b/include/linux/efi_emulator.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_EFI_EMULATOR_H +#define _LINUX_EFI_EMULATOR_H + +#include +#include +#include + +//todo, arch abstraction, for x86, it is efi_info +struct efi_rt_info { + const efi_runtime_services_t *runtime; /* EFI runtime services table */ + unsigned int runtime_version; /* Runtime services version */ + u32 runtime_supported_mask; + /* Build systab tables from the following */ + unsigned int systab_nr_tables; + efi_config_table_t systab_tables[20]; + struct efi_boot_memmap memmap; +}; + +/* 1st kernel passes information through this struct */ +struct efi_emulator_param { + unsigned long sp; + /* Should be page-aligned */ + unsigned long load_address; + unsigned int sz_in_byte; + wchar_t cmdline[512]; + bool noefi_boot; + bool print_enabled; + char earlycon_name[16]; + phys_addr_t earlycon_reg_base; + unsigned long earlycon_reg_sz; + + bool mmu_on; + /* root of pgtable */ + phys_addr_t pgd_root; + phys_addr_t kernel_img_start; + unsigned long kernel_img_sz; + phys_addr_t dtb; + phys_addr_t mempool_start; + unsigned long mempool_sz; + /* The last struct */ + struct efi_rt_info rt_info; +}; + +extern unsigned char _efi_emulator_start[], _efi_emulator_end[]; +#endif diff --git a/include/linux/kexec.h b/include/linux/kexec.h index f0e9f8eda7a3c..cff6b6869498b 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -325,6 +325,7 @@ struct kimage { unsigned int hotplug_support:1; #endif + bool is_pe; #ifdef ARCH_HAS_KIMAGE_ARCH struct kimage_arch arch; #endif @@ -462,6 +463,7 @@ static inline int arch_kexec_post_alloc_pages(void *vaddr, unsigned int pages, g static inline void arch_kexec_pre_free_pages(void *vaddr, unsigned int pages) { } #endif +extern phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage); extern bool kexec_file_dbg_print; #define kexec_dprintk(fmt, arg...) \ From patchwork Mon Aug 19 14:53:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 820428 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C8EA2175570 for ; Mon, 19 Aug 2024 14:55:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079337; cv=none; b=ZkA664AJvQz3ycxAfdVWE76GaKZY62M0oe5hq8ru4cZSGdM4ucx11tsO/YoVy/C/kbmlsXHo5bI9X8SmasQ6oafUiiKMtEDVi2YeQkWGxKAnYbJhMA6GPG4bUD+a1Pu0NW9JjeOUa2mup3hCA5vgzYSAFcCvuqiKsYaT8vTUpbI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079337; c=relaxed/simple; bh=PBnBckwf3HHTrpM6pd6d+I3Xu0Da6Cv6R3/6mUnABVY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IYKiv9KdaPFd40fExf9Vbyq1HozVRJaB26oezev1/sJ1z0jaKUswVJ9upiCaXO/c63f2NQIx4uctjXuyeNG2Qyj7oHl75oS5DPmezH4tc7DhXnuJkBbl5Gg5wGHOH6uXt3ONGTqKoKsucoIX5ir6i6rOkLk+A413Bpah2/FU7ME= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=fEFwLvhN; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="fEFwLvhN" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079334; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=LcKEDsrUy4VHo6FTRlO8InFKgG7AEDUaPffRYgLjGFs=; b=fEFwLvhNQFaEx6clMddJhemB6cOjnWKW1i8juHPa7cGdDi9U9d+CSmLzdaeTBROxYve414 Ny9DTCPE5fPxz3cOhWBn89LfbBuNAqlFfxwqY1A+HxZb78VJHvhrwbNjbwnJGbsFC28TBy 6iS9jSymybcc2sjCZEYZF4rcmgWbe4s= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-304-U2exi1kMPfW4KE9F8i_Osg-1; Mon, 19 Aug 2024 10:55:31 -0400 X-MC-Unique: U2exi1kMPfW4KE9F8i_Osg-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 8631E1955D57; Mon, 19 Aug 2024 14:55:29 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9D3B71956053; Mon, 19 Aug 2024 14:55:20 +0000 (UTC) From: Pingfan Liu To: linux-efi@vger.kernel.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Subject: [RFCv2 4/9] efi/emulator: Turn on mmu for arm64 Date: Mon, 19 Aug 2024 22:53:37 +0800 Message-ID: <20240819145417.23367-5-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 On arm64, when kexec switches to the new kernel, the mmu is off. So the efi emulator should turn on the mmu to enable the identity mapping at the first stage. In fact, the kexec switching can run with mmu-on if it enters emulator, but that requires the re-arrangement of the relocate_kernel.S [1]. After that, this patch can be drop. But let us focus on emulator itself and keep things simple for the time being. [1]: https://lore.kernel.org/linux-arm-kernel/20240328115656.24090-1-piliu@redhat.com/ Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel Cc: Mark Rutland Cc: linux-arm-kernel@lists.infradead.org To: linux-efi@vger.kernel.org --- drivers/firmware/efi/efi_emulator/Makefile | 2 +- .../firmware/efi/efi_emulator/arm64_proc.S | 175 ++++++++++++++++++ drivers/firmware/efi/efi_emulator/entry.c | 7 + 3 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/efi/efi_emulator/arm64_proc.S diff --git a/drivers/firmware/efi/efi_emulator/Makefile b/drivers/firmware/efi/efi_emulator/Makefile index d696381d168ba..9d295b77a2be3 100644 --- a/drivers/firmware/efi/efi_emulator/Makefile +++ b/drivers/firmware/efi/efi_emulator/Makefile @@ -65,7 +65,7 @@ emulator-y := head.o entry.o \ core.o pe_loader.o memory.o memory_api.o config_table.o misc.o \ device_handle.o protocol_simple_text_output.o protocol_device_path.o \ lib.o printf.o \ - amba-pl011.o + amba-pl011.o arm64_proc.o obj-y := efi_emulator.o diff --git a/drivers/firmware/efi/efi_emulator/arm64_proc.S b/drivers/firmware/efi/efi_emulator/arm64_proc.S new file mode 100644 index 0000000000000..8364c459b8348 --- /dev/null +++ b/drivers/firmware/efi/efi_emulator/arm64_proc.S @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Based on arch/arm/mm/proc.S + * + * Copyright (C) 2001 Deep Blue Solutions Ltd. + * Copyright (C) 2012 ARM Ltd. + * Author: Catalin Marinas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ARM64_64K_PAGES +#define TCR_TG_FLAGS TCR_TG0_64K | TCR_TG1_64K +#elif defined(CONFIG_ARM64_16K_PAGES) +#define TCR_TG_FLAGS TCR_TG0_16K | TCR_TG1_16K +#else /* CONFIG_ARM64_4K_PAGES */ +#define TCR_TG_FLAGS TCR_TG0_4K | TCR_TG1_4K +#endif + +#ifdef CONFIG_RANDOMIZE_BASE +#define TCR_KASLR_FLAGS TCR_NFD1 +#else +#define TCR_KASLR_FLAGS 0 +#endif + +#define TCR_SMP_FLAGS TCR_SHARED + +/* PTWs cacheable, inner/outer WBWA */ +#define TCR_CACHE_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA + +#ifdef CONFIG_KASAN_SW_TAGS +#define TCR_KASAN_SW_FLAGS TCR_TBI1 | TCR_TBID1 +#else +#define TCR_KASAN_SW_FLAGS 0 +#endif + +#ifdef CONFIG_KASAN_HW_TAGS +#define TCR_MTE_FLAGS TCR_TCMA1 | TCR_TBI1 | TCR_TBID1 +#elif defined(CONFIG_ARM64_MTE) +/* + * The mte_zero_clear_page_tags() implementation uses DC GZVA, which relies on + * TBI being enabled at EL1. + */ +#define TCR_MTE_FLAGS TCR_TBI1 | TCR_TBID1 +#else +#define TCR_MTE_FLAGS 0 +#endif + +/* + * Default MAIR_EL1. MT_NORMAL_TAGGED is initially mapped as Normal memory and + * changed during mte_cpu_setup to Normal Tagged if the system supports MTE. + */ +#define MAIR_EL1_SET \ + (MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRnE, MT_DEVICE_nGnRnE) | \ + MAIR_ATTRIDX(MAIR_ATTR_DEVICE_nGnRE, MT_DEVICE_nGnRE) | \ + MAIR_ATTRIDX(MAIR_ATTR_NORMAL_NC, MT_NORMAL_NC) | \ + MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL) | \ + MAIR_ATTRIDX(MAIR_ATTR_NORMAL, MT_NORMAL_TAGGED)) + + +SYM_FUNC_START(__cpu_setup) + tlbi vmalle1 // Invalidate local TLB + dsb nsh + + msr cpacr_el1, xzr // Reset cpacr_el1 + mov_q x1, CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN | CPACR_EL1_ZEN_EL1EN + msr cpacr_el1, x1 // avoid the trap of FPSIMD in uefi code + isb + mov x1, #1 << 12 // Reset mdscr_el1 and disable + msr mdscr_el1, x1 // access to the DCC from EL0 + reset_pmuserenr_el0 x1 // Disable PMU access from EL0 + reset_amuserenr_el0 x1 // Disable AMU access from EL0 + + /* + * Default values for VMSA control registers. These will be adjusted + * below depending on detected CPU features. + */ + mair .req x17 + tcr .req x16 + mov_q mair, MAIR_EL1_SET + mov_q tcr, TCR_T0SZ(IDMAP_VA_BITS) | TCR_T1SZ(VA_BITS_MIN) | TCR_CACHE_FLAGS | \ + TCR_SMP_FLAGS | TCR_TG_FLAGS | TCR_KASLR_FLAGS | TCR_ASID16 | \ + TCR_TBI0 | TCR_A1 | TCR_KASAN_SW_FLAGS | TCR_MTE_FLAGS + + tcr_clear_errata_bits tcr, x9, x5 + +#ifdef CONFIG_ARM64_VA_BITS_52 + mov x9, #64 - VA_BITS +alternative_if ARM64_HAS_VA52 + tcr_set_t1sz tcr, x9 +#ifdef CONFIG_ARM64_LPA2 + orr tcr, tcr, #TCR_DS +#endif +alternative_else_nop_endif +#endif + + /* + * Set the IPS bits in TCR_EL1. + */ + tcr_compute_pa_size tcr, #TCR_IPS_SHIFT, x5, x6 +#ifdef CONFIG_ARM64_HW_AFDBM + /* + * Enable hardware update of the Access Flags bit. + * Hardware dirty bit management is enabled later, + * via capabilities. + */ + mrs x9, ID_AA64MMFR1_EL1 + and x9, x9, ID_AA64MMFR1_EL1_HAFDBS_MASK + cbz x9, 1f + orr tcr, tcr, #TCR_HA // hardware Access flag update +1: +#endif /* CONFIG_ARM64_HW_AFDBM */ + msr mair_el1, mair + msr tcr_el1, tcr + + mrs_s x1, SYS_ID_AA64MMFR3_EL1 + ubfx x1, x1, #ID_AA64MMFR3_EL1_S1PIE_SHIFT, #4 + cbz x1, .Lskip_indirection + + /* + * The PROT_* macros describing the various memory types may resolve to + * C expressions if they include the PTE_MAYBE_* macros, and so they + * can only be used from C code. The PIE_E* constants below are also + * defined in terms of those macros, but will mask out those + * PTE_MAYBE_* constants, whether they are set or not. So #define them + * as 0x0 here so we can evaluate the PIE_E* constants in asm context. + */ + +#define PTE_MAYBE_NG 0 +#define PTE_MAYBE_SHARED 0 + + mov_q x0, PIE_E0 + msr REG_PIRE0_EL1, x0 + mov_q x0, PIE_E1 + msr REG_PIR_EL1, x0 + +#undef PTE_MAYBE_NG +#undef PTE_MAYBE_SHARED + + mov x0, TCR2_EL1x_PIE + msr REG_TCR2_EL1, x0 + +.Lskip_indirection: + + /* + * Prepare SCTLR + */ + mov_q x0, INIT_SCTLR_EL1_MMU_ON + ret // return to head.S + + .unreq mair + .unreq tcr +SYM_FUNC_END(__cpu_setup) + +SYM_FUNC_START(enable_sctlr_el1) + stp x29, x30, [sp, #-16]! + bl __cpu_setup + set_sctlr_el1 x0 + ldp x29, x30, [sp], #16 + ret +SYM_FUNC_END(enable_sctlr_el1) + diff --git a/drivers/firmware/efi/efi_emulator/entry.c b/drivers/firmware/efi/efi_emulator/entry.c index e5a31bd303858..835021ab0f629 100644 --- a/drivers/firmware/efi/efi_emulator/entry.c +++ b/drivers/firmware/efi/efi_emulator/entry.c @@ -13,6 +13,13 @@ extern void enable_sctlr_el1(unsigned long scratch_reg); static void arch_handle_mmu(struct efi_emulator_param *param) { if (!param->mmu_on && param->pgd_root) { + unsigned long scratch_reg = 0; + // in fact, we need SYM_FUNC_START(__cpu_setup), later, set SCTLR_EL1 + // At present, the mmu is not ON + write_sysreg(param->pgd_root, ttbr0_el1); + isb(); + /* scratch_reg asks the C compiler to save x0 */ + enable_sctlr_el1(scratch_reg); } } From patchwork Mon Aug 19 14:53:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 821083 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3FA4617557C for ; Mon, 19 Aug 2024 14:55:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079346; cv=none; b=LNJUSVCPR78Vg+RPOgHV+r5nnyBS3bk0UTay+Y3cGcraTvUFB16Gr8u38KG+XuxAbBlbSBGKClUVa42qi+aPiKRJVlQBCzxg4M5q/1L1OYcrpJCAxxjORaWy+2Q3KgwU2EMMDiO7sHPxJXrVJX6EO6iuu2Ymu2SBNU7eDiodMcU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079346; c=relaxed/simple; bh=8s4T1MmfOYRHa3jn57rN4qLtao9vpsJGkAiHYpBYsWg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X/crcAqhoXzR74SvyA5iVre9bkQ1OZBRWTrnXnPgCB004UvKqMTs1HluZJO44LvQ6ykHupJ7myTh4FiEMfAFACZxueFQmd8gllpL8njw6wuu73+gxy5LiBGeIeU3coB2yKBG+jFumafYIV4uWKNg7wkzSFnY61vSUeMIkYjg/2o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=U+Ktwuwm; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="U+Ktwuwm" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079343; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=4jaYTHdT48upkBaupKJQL4htOu5I8EmdY+ZBsK3Qb3Y=; b=U+Ktwuwm9EQK2qTEp0YlPmB4ISSSvJC3/VYTvNTB6odtZvQWDicvn1Sr8+W1TjuSg7c7Hr EkHWfFe2Pw4by2zXnBkVmAKIjedOtnBetdTwJVytfc9TK6ogBz9Jg8XSAht1BnLYZlS1Ku ZdEouAs2BihA9PRolnv56GBPEsqazgc= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-263-ojJaEWOyO4-NUJRFsmIPpg-1; Mon, 19 Aug 2024 10:55:39 -0400 X-MC-Unique: ojJaEWOyO4-NUJRFsmIPpg-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 448F51955D58; Mon, 19 Aug 2024 14:55:38 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 666D619560A3; Mon, 19 Aug 2024 14:55:29 +0000 (UTC) From: Pingfan Liu To: kexec@lists.infradead.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFCv2 5/9] kexec: Introduce kexec_pe_image to parse and load PE file Date: Mon, 19 Aug 2024 22:53:38 +0800 Message-ID: <20240819145417.23367-6-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 As UEFI becomes popular, a few architectures support to boot a PE format kernel image directly. And the internal of PE format may be different. This introduces a new kexec_file_ops implementation, named pe_image_ops, which prepares UEFI environment for the trampoline code 'efi emulator'. The pe_image_ops considers efi emulator and its input parameters, 'efi_emulator_param' as two additional kexec_segment. And it constructs efi_memory_desc_t[], encodes efi runtime service info inside the parameter buffer. Finally, it asks architecture implement's page table routine to set up identity map for all memory used in 'efi emulator' To do: This is a POC version, at present, it aims for arm64, later, it needs abstraction to cope with x86 Signed-off-by: Pingfan Liu Cc: Baoquan He Cc: Dave Young Cc: Eric Biederman Cc: Ard Biesheuvel Cc: linux-kernel@vger.kernel.org To: kexec@lists.infradead.org --- include/linux/kexec.h | 1 + kernel/Makefile | 1 + kernel/kexec_pe_image.c | 503 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 kernel/kexec_pe_image.c diff --git a/include/linux/kexec.h b/include/linux/kexec.h index cff6b6869498b..57b98bcaa5228 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -385,6 +385,7 @@ static inline int machine_kexec_post_load(struct kimage *image) { return 0; } extern struct kimage *kexec_image; extern struct kimage *kexec_crash_image; +extern const struct kexec_file_ops pe_image_ops; bool kexec_load_permitted(int kexec_image_type); diff --git a/kernel/Makefile b/kernel/Makefile index 3c13240dfc9f0..f14d78b03fd0f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_KEXEC_CORE) += kexec_core.o obj-$(CONFIG_CRASH_DUMP) += crash_core.o obj-$(CONFIG_KEXEC) += kexec.o obj-$(CONFIG_KEXEC_FILE) += kexec_file.o +obj-$(CONFIG_ARCH_SELECTS_KEXEC_PEIMAGE) += kexec_pe_image.o obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o obj-$(CONFIG_COMPAT) += compat.o diff --git a/kernel/kexec_pe_image.c b/kernel/kexec_pe_image.c new file mode 100644 index 0000000000000..d14b8a5f69a99 --- /dev/null +++ b/kernel/kexec_pe_image.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kexec image loader + + * Copyright (C) 2024 Red Hat, Inc + * Author: Pingfan Liu + */ + +#define pr_fmt(fmt) "kexec_file(Image): " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * The UEFI Terse Executable (TE) image has MZ header. +*/ +static int pe_image_probe(const char *kernel_buf, unsigned long kernel_len) +{ + struct mz_hdr *mz = (struct mz_hdr *)kernel_buf; + struct pe_hdr *pe; + + if (mz->magic != MZ_MAGIC) + return -1; + pe = (struct pe_hdr *)(kernel_buf + mz->peaddr); + if (pe->magic != PE_MAGIC) + return -1; + + return 0; +} + +/* + * Efi runtime code or data are marked as EFI_RUNTIME_SERVICES_CODE + * or EFI_RUNTIME_SERVICES_DATA memory descriptor. When building mapping for the + * emulator, they should be mapped. Here just recording the entries to those regions. + */ +static void build_rt_info(struct efi_rt_info *rt) +{ + unsigned int desc_size = efi.memmap.desc_size; + int i, cnt; + + i = cnt = 0; + if (desc_size) { + cnt = (efi.memmap.map_end - efi.memmap.map) / desc_size; + /* The emulator heap memory split the continous memory into three chunk */ + cnt += 2; + } + + /* + * This virtual address is in UEFI address space, and the mapping is recorded + * in efi_memory_desc_t[] and passed to reboot kernel. + * + * EFI stub may use runtime service, so its mapping should be set up + */ + rt->runtime = efi.runtime; + rt->runtime_version = efi.runtime_version; + rt->runtime_supported_mask = efi.runtime_supported_mask; + /* + * Reconstruct the persistent systab's tables, which are recorded in efi. + */ + if (efi.acpi != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.acpi; + rt->systab_tables[i].guid = ACPI_TABLE_GUID; + i++; + } + if (efi.acpi20 != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.acpi20; + rt->systab_tables[i].guid = ACPI_20_TABLE_GUID; + i++; + } + if (efi.smbios != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.smbios; + rt->systab_tables[i].guid = SMBIOS_TABLE_GUID; + i++; + } + if (efi.smbios3 != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.smbios3; + rt->systab_tables[i].guid = SMBIOS3_TABLE_GUID; + i++; + } + if (efi.esrt != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.esrt; + rt->systab_tables[i].guid = EFI_SYSTEM_RESOURCE_TABLE_GUID; + i++; + } + if (efi.tpm_log != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.tpm_log; + rt->systab_tables[i].guid = LINUX_EFI_TPM_EVENT_LOG_GUID; + i++; + } + if (efi.tpm_final_log != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.tpm_final_log; + rt->systab_tables[i].guid = EFI_TCG2_FINAL_EVENTS_TABLE_GUID; + i++; + } +#ifdef CONFIG_LOAD_UEFI_KEYS + if (efi.mokvar_table != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.mokvar_table; + rt->systab_tables[i].guid = LINUX_EFI_MOK_VARIABLE_TABLE_GUID; + i++; + } +#endif +#ifdef CONFIG_EFI_COCO_SECRET + if (efi.coco_secret != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.coco_secret; + rt->systab_tables[i].guid = LINUX_EFI_COCO_SECRET_AREA_GUID; + i++; + } +#endif +#ifdef CONFIG_UNACCEPTED_MEMORY + if (efi.unaccepted != EFI_INVALID_TABLE_ADDR) { + rt->systab_tables[i].table = (void *)efi.unaccepted; + rt->systab_tables[i].guid = LINUX_EFI_UNACCEPTED_MEM_TABLE_GUID; + i++; + } +#endif + if (!desc_size) { + //todo, if noefi boot, LINUX_EFI_MEMRESERVE_TABLE_GUID should be used to pass IMA + } + + rt->systab_nr_tables = i; + + rt->memmap.map_size = desc_size * cnt; + /* In case of non-EFI booting, these two fields will be faked later */ + rt->memmap.desc_size = efi.memmap.desc_size; + rt->memmap.desc_ver = efi.memmap.desc_version; +} + +static int create_md_from_res(struct resource *res, void *data) +{ + struct efi_emulator_param *param = (struct efi_emulator_param *)data; + struct efi_boot_memmap *memmap = ¶m->rt_info.memmap; + efi_memory_desc_t *dst_md; + + dst_md = (void *)memmap->map + memmap->map_size; + + /* Split res into three chunk */ + if ((res->start <= param->mempool_start) && + res->end > (param->mempool_start + param->mempool_sz)) { + + dst_md->phys_addr = res->start; + dst_md->num_pages = (param->mempool_start - res->start) >> EFI_PAGE_SHIFT; + /* Pretend that it is occupied */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + dst_md->attribute = EFI_MEMORY_WB; + dst_md = (void *)dst_md + memmap->desc_size; + + dst_md->phys_addr = param->mempool_start; + dst_md->num_pages = param->mempool_sz >> EFI_PAGE_SHIFT; + /* Confine memory footprint inside this region before exit boot service */ + dst_md->type = EFI_CONVENTIONAL_MEMORY; + dst_md->attribute = EFI_MEMORY_WB; + dst_md = (void *)dst_md + memmap->desc_size; + + dst_md->phys_addr = param->mempool_start + param->mempool_sz; + dst_md->num_pages = (res->end - dst_md->phys_addr) >> EFI_PAGE_SHIFT; + /* Pretend that it is occupied */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + dst_md->attribute = EFI_MEMORY_WB; + + memmap->map_size += 3 * (memmap->desc_size); + } else { + dst_md->phys_addr = res->start; + dst_md->num_pages = (res->end - res->start) >> EFI_PAGE_SHIFT; + /* Pretend that it is occupied */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + dst_md->attribute = EFI_MEMORY_WB; + + memmap->map_size += memmap->desc_size; + } + + return 0; +} + +static efi_memory_desc_t *conclude_md(efi_memory_desc_t *dst_md, + unsigned int desc_size, unsigned long pool_start, unsigned long num_pages) +{ + if (dst_md->num_pages == 0) + return dst_md; + + /* Split into three sections */ + if (dst_md->phys_addr <= pool_start && dst_md->num_pages >= num_pages ) { + u64 virt_base, phys_base, left_pages, attribute; + u32 type; + + type = dst_md->type; + attribute = dst_md->attribute; + phys_base = dst_md->phys_addr; + /* + * After SetVirtualAddressMap, the mapping is installed into + * firmware and can not be changed. The second kernel should + * be aware of this info + */ + virt_base = dst_md->virt_addr; + left_pages = dst_md->num_pages; + + dst_md->num_pages = (pool_start - phys_base) >> EFI_PAGE_SHIFT; + /* Pretend that it is occupied until exit boot service */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + left_pages -= dst_md->num_pages; + + dst_md = (void *)dst_md + desc_size; + dst_md->phys_addr = pool_start; + dst_md->virt_addr = virt_base + (dst_md->phys_addr - phys_base); + dst_md->num_pages = num_pages; + dst_md->attribute = attribute; + /* Confine the memory footprint on it, which has page table mapping */ + dst_md->type = EFI_CONVENTIONAL_MEMORY; + left_pages -= dst_md->num_pages; + + dst_md = (void *)dst_md + desc_size; + dst_md->phys_addr = pool_start + (num_pages << EFI_PAGE_SHIFT); + dst_md->virt_addr = virt_base + (dst_md->phys_addr - phys_base); + dst_md->num_pages = left_pages; + dst_md->attribute = attribute; + /* Pretend that it is occupied until exit boot service */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + } + + dst_md = (void *)dst_md + desc_size; + + return dst_md; +} + +static void create_md_from_efi(efi_memory_desc_t *dst, unsigned int desc_sz, + struct efi_emulator_param *param) +{ + efi_memory_desc_t *md, *dst_md, *prev_md = NULL; + unsigned long pool_start, num_pages; + + dst_md = dst; + pool_start = param->mempool_start; + num_pages = param->mempool_sz >> EFI_PAGE_SHIFT; + for_each_efi_memory_desc(md) { + switch (md->type) { + // todo, how to ensure kexec dst avoid the EFI_RUNTIME_SERVICES_CODE etc + case EFI_RUNTIME_SERVICES_CODE: + case EFI_RUNTIME_SERVICES_DATA: + case EFI_RESERVED_TYPE: + case EFI_UNUSABLE_MEMORY: + case EFI_ACPI_RECLAIM_MEMORY: + //test whether the current dst_md covers the heap, if yes, split it into three + dst_md = conclude_md(dst_md, desc_sz, pool_start, num_pages); + dst_md->phys_addr = md->phys_addr; + dst_md->virt_addr = md->virt_addr; + dst_md->num_pages = md->num_pages; + dst_md->type = md->type; + dst_md->attribute = md->attribute; + dst_md = conclude_md(dst_md, desc_sz, pool_start, num_pages); + break; + + default: + if (dst_md->num_pages == 0) { + dst_md->phys_addr = md->phys_addr; + dst_md->virt_addr = md->virt_addr; + dst_md->num_pages = md->num_pages; + /* + * Pretend as boot service data to prevent allocation + * from it before efi_exit_boot_services. + */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + dst_md->attribute = md->attribute; + } else { + /* merge */ + if (prev_md && prev_md->attribute == md->attribute && + (md->phys_addr - prev_md->phys_addr) >> EFI_PAGE_SHIFT == prev_md->num_pages) { + dst_md->num_pages += md->num_pages; + } else { + dst_md = conclude_md(dst_md, desc_sz, pool_start, num_pages); + dst_md->phys_addr = md->phys_addr; + dst_md->virt_addr = md->virt_addr; + dst_md->num_pages = md->num_pages; + /* Pretend as boot service data */ + dst_md->type = EFI_BOOT_SERVICES_DATA; + dst_md->attribute = md->attribute; + } + } + break; + } + + prev_md = md; + } + dst_md = conclude_md(dst_md, desc_sz, pool_start, num_pages); + param->rt_info.memmap.map_size = (void *)dst_md - (void *)dst; +} + +/* + * All efi runtime information should be passed to kexec reboot kernel. Record + * them in scratch. These information should be in EFI_RUNTIME_SERVICES md, so + * copying index is enough. Later the mapping for that md will be set up in + * arch_emulator_prepare_pgtable(). + * + * If the system is not booted by efi, fake one. + */ +static void encode_efi_runtime_info(struct efi_emulator_param *param, struct kimage *image) +{ + struct efi_rt_info *rt = ¶m->rt_info; + efi_memory_desc_t *dst_md; + unsigned int sz; + + build_rt_info(rt); + sz = efi.memmap.map_end - efi.memmap.map; + /* booted by efi firmware */ + if (sz) { + unsigned long desc_size = efi.memmap.desc_size; + + dst_md = rt->memmap.map; + /* + * It has no point to pass efi.memmap directly to the reboot kernel since + * EFI_BOOT_SERVICES_DATA etc has changed. But EFI_RUNTIME_SERVICES_DATA + * etc should be paid attention to. + */ + create_md_from_efi(dst_md, desc_size, param); + } else { + param->noefi_boot = true; + /* + * Kernel is booted by non-EFI loader, which parses the PE format. In + * kexec case, faking memory descriptor so that efi-stub can self-parse. + * But there is no efi runtime service, and the kernel cmdline should + * have 'noefi' + */ + rt->memmap.desc_size = sizeof(efi_memory_desc_t); + rt->memmap.desc_ver = EFI_MEMORY_DESCRIPTOR_VERSION; + walk_system_ram_res(0, ULONG_MAX, param, create_md_from_res); + + /* + * Besides, efi stub removes two dt node '/memreserve', one for initrd + * and the other for IMA. EFI_RESERVED_TYPE can not serve that purpose. + * That should be handled by LINUX_EFI_MEMRESERVE_TABLE_GUID + */ + } +} + +extern phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage, + struct efi_emulator_param *param); + +static void detect_earlycon(struct efi_emulator_param *param) +{ + //todo, autodetect the console register base and size + // aarch64 MACHINE_VIRT_UART_BASE + size_t sz = 15 < strlen("amba-pl011") ? 15 : strlen("amba-pl011"); + + memcpy(param->earlycon_name, "amba-pl011", sz); + param->earlycon_reg_base = 0x9000000; + param->earlycon_reg_sz = PAGE_SIZE; + param->print_enabled = true; +} + +static phys_addr_t emulator_prepare_pgtable(struct kimage *kimage, + struct efi_emulator_param *param) +{ + + detect_earlycon(param); + + return arch_emulator_prepare_pgtable(kimage, param); +} + +/* param, stack, and basic heap */ +#define EMULATOR_STACK_SIZE (1 << 17) +#define EMULATOR_PARAM_SIZE (1 << 20) + +static void *pe_image_load(struct kimage *image, + char *kernel, unsigned long kernel_len, + char *initrd, unsigned long initrd_len, + char *cmdline, unsigned long cmdline_len) +{ + struct kexec_segment *emulator_seg, *param_seg, *kernel_segment; + struct efi_emulator_param *param; + struct page *param_pages; + struct kexec_buf kbuf; + unsigned long kseg_num; + int ret; + + image->is_pe = true; + /* Do not parse image format, just load it. */ + kbuf.image = image; + kbuf.buf_min = 0; + kbuf.buf_max = ULONG_MAX; + kbuf.top_down = false; + kbuf.buffer = kernel; + kbuf.bufsz = kernel_len; + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + kbuf.memsz = kernel_len; + kbuf.buf_align = MIN_KIMG_ALIGN; + + kseg_num = image->nr_segments; + /* + * The location of the kernel segment may make it impossible to satisfy + * the other segment requirements, so we try repeatedly to find a + * location that will work. + */ + while ((ret = kexec_add_buffer(&kbuf)) == 0) { + /* Try to load additional data */ + kernel_segment = &image->segment[kseg_num]; + ret = load_other_segments(image, kernel_segment->mem, + kernel_segment->memsz, initrd, + initrd_len, cmdline); + if (!ret) + break; + + /* + * We couldn't find space for the other segments; erase the + * kernel segment and try the next available hole. + */ + image->nr_segments -= 1; + kbuf.buf_min = kernel_segment->mem + kernel_segment->memsz; + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + } + + if (ret) { + pr_err("Could not find any suitable kernel location!"); + return ERR_PTR(ret); + } + kernel_segment = &image->segment[kseg_num]; + + /* Load EFI emulator */ + emulator_seg = &image->segment[image->nr_segments]; + kbuf.buffer = _efi_emulator_start; + kbuf.bufsz = _efi_emulator_end - _efi_emulator_start; + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + kbuf.memsz = kbuf.bufsz; + kbuf.buf_align = PAGE_SIZE; + ret = kexec_add_buffer(&kbuf); + if (ret) + return ERR_PTR(ret); + + /* + * Prepare param and memory for emulator. One page for param, + * rear page for stack and the rest for runtime heap. + */ + param_seg = &image->segment[image->nr_segments]; + //to do, zero page is not required in kimage_entry_t + // and free them + param_pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, 2); + param = page_to_virt(param_pages); + + /* These chunk of information will be copied to the KEXEC SOURCE PAGE */ + /* emulator loaded address */ + param->load_address = emulator_seg->mem; + /* PE file payload at the beginning of this RAM range */ + param->kernel_img_start = kernel_segment->mem; + param->kernel_img_sz = kernel_segment->memsz; + /* uefi protocol need it */ + param->dtb = image->arch.dtb_mem; + param->sz_in_byte = utf8s_to_utf16s(cmdline, cmdline_len, + UTF16_LITTLE_ENDIAN, param->cmdline, 512); + param->sz_in_byte *= sizeof(wchar_t); + kbuf.buffer = param; + /* + * One page for param, one page for stack, the rest for heap, which should + * also has enough room for kernel and its decompression (256MB) + */ + kbuf.bufsz = EMULATOR_PARAM_SIZE + kernel_segment->memsz + (1 << 28); + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; + kbuf.memsz = kbuf.bufsz; + kbuf.buf_align = PAGE_SIZE; + ret = kexec_add_buffer(&kbuf); + if (ret) + return ERR_PTR(ret); + + /* These info is formed after param segment is built */ + /* stack size equals 64K -sizeof(*param) */ + param->sp = param_seg->mem + EMULATOR_STACK_SIZE; + /* heap information */ + param->mempool_start = param->sp; + param->mempool_sz = kbuf.bufsz - EMULATOR_STACK_SIZE; + + param->pgd_root = emulator_prepare_pgtable(image, param); + /* For the time being, reset routine will turn off mmu */ + param->mmu_on = false; + encode_efi_runtime_info(param, image); + + /* relocate.s jumps to it */ + image->start = emulator_seg->mem; + /* the 3rd input param for restart() */ + image->arch.param_mem = param_seg->mem; + image->arch.dtb_mem = image->arch.param_mem; + + kexec_dprintk("Loaded emulator at 0x%lx bufsz=0x%lx\n", + emulator_seg->mem, emulator_seg->memsz); + kexec_dprintk("Loaded param blob at 0x%lx bufsz=0x%lx, sp=0x%lx\n", + param_seg->mem, param_seg->memsz, param->sp); + kexec_dprintk("pgd_root:0x%llx\n", param->pgd_root); + + return NULL; +} + +const struct kexec_file_ops pe_image_ops = { + .probe = pe_image_probe, + .load = pe_image_load, +#ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG + .verify_sig = kexec_kernel_verify_pe_sig, +#endif +}; From patchwork Mon Aug 19 14:53:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 820427 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 98D7C17556B for ; Mon, 19 Aug 2024 14:55:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079358; cv=none; b=GdWmVjBb7qbgY9wpad2djcqDmpn5bUF+LANUNCjoWoUrus7BfwYQcAzw7KfkB7kU9m96uxEzkYvjJ4pFjl2lmL0V1W/6mMEBlks7IrB1kd6yFisdqeEN09pmDLS7G8y5BJMbG+bqKlaO8I0pWGzqNRe6kqeo8cR5bPmecM3uRYI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079358; c=relaxed/simple; bh=gvXUPSVXR+TJbArq4gRza0CovZloy+FUB0ztPEaCf80=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=H09inemaoFwki4J23A0g9S5AmZRHFyg3f/P7ZfpZxFF5ZwGhDvtNEC2MtqrEyOHJwpXCwC+TTY8P4fl43MhRSDMqSiDPn6YK9ZVRNXpEW0xNyH+PA2f+86RmJ2vVcLL4kcEy9I4Md1N/Pjtec42+gFD1d+E6lM4g9unimDvAA8Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=MCGpV7sE; arc=none smtp.client-ip=170.10.133.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="MCGpV7sE" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079355; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=nORFBheCnmBdqnoFe9uPUvA5oaRV47y8v5ImE/FbbjU=; b=MCGpV7sE78mCcN0cwRs+txDlYWsmDeHQi9CPsTPVSDRMaRoUnVpNq9kqHsTc4f5WmMTBUG zd/eB/j5xpl9THkkux3ubs4NKG44pZFBfvJZaLQdH00ejemKdY4QcrxjFGnk2HXXXv2LJL 4klvCgGrXuGLjTQLUTrp8WgaYQqNtLc= Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-632-KAsVic3cOMOfMu4DfSmE5w-1; Mon, 19 Aug 2024 10:55:51 -0400 X-MC-Unique: KAsVic3cOMOfMu4DfSmE5w-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5D79A1955D52; Mon, 19 Aug 2024 14:55:49 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 6199619560A3; Mon, 19 Aug 2024 14:55:38 +0000 (UTC) From: Pingfan Liu To: linux-arm-kernel@lists.infradead.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFCv2 6/9] arm64: kexec: Introduce a new member param_mem to kimage_arch Date: Mon, 19 Aug 2024 22:53:39 +0800 Message-ID: <20240819145417.23367-7-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 relocate_kernel will be used either to boot vmlinux directly or PE image. In the latter case, the efi emulator needs efi_emulator_param instead of dtb as input parameter. On the other hand, dtb is still required to pass down through efi_emulator_param to the second kernel, so keep kimage_arch->dtb_mem as scratch and introduce another member 'param_mem' When booting vmlinux, param_mem equals dtb_mem. Signed-off-by: Pingfan Liu Cc: Catalin Marinas Cc: Will Deacon Cc: Mark Rutland Cc: Ard Biesheuvel To: linux-arm-kernel@lists.infradead.org --- arch/arm64/include/asm/kexec.h | 1 + arch/arm64/kernel/asm-offsets.c | 2 +- arch/arm64/kernel/machine_kexec.c | 2 +- arch/arm64/kernel/machine_kexec_file.c | 1 + arch/arm64/kernel/relocate_kernel.S | 2 +- 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/kexec.h b/arch/arm64/include/asm/kexec.h index 4d9cc7a76d9ca..f4c63a1a9531f 100644 --- a/arch/arm64/include/asm/kexec.h +++ b/arch/arm64/include/asm/kexec.h @@ -109,6 +109,7 @@ int machine_kexec_post_load(struct kimage *image); struct kimage_arch { void *dtb; phys_addr_t dtb_mem; + phys_addr_t param_mem; phys_addr_t kern_reloc; phys_addr_t el2_vectors; phys_addr_t ttbr0; diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c index 81496083c0413..8633a94fcb9f1 100644 --- a/arch/arm64/kernel/asm-offsets.c +++ b/arch/arm64/kernel/asm-offsets.c @@ -187,7 +187,7 @@ int main(void) BLANK(); #endif #ifdef CONFIG_KEXEC_CORE - DEFINE(KIMAGE_ARCH_DTB_MEM, offsetof(struct kimage, arch.dtb_mem)); + DEFINE(KIMAGE_ARCH_PARAM_MEM, offsetof(struct kimage, arch.param_mem)); DEFINE(KIMAGE_ARCH_EL2_VECTORS, offsetof(struct kimage, arch.el2_vectors)); DEFINE(KIMAGE_ARCH_ZERO_PAGE, offsetof(struct kimage, arch.zero_page)); DEFINE(KIMAGE_ARCH_PHYS_OFFSET, offsetof(struct kimage, arch.phys_offset)); diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c index 82e2203d86a31..6958c1fc84a5a 100644 --- a/arch/arm64/kernel/machine_kexec.c +++ b/arch/arm64/kernel/machine_kexec.c @@ -192,7 +192,7 @@ void machine_kexec(struct kimage *kimage) cpu_install_idmap(); restart = (void *)__pa_symbol(cpu_soft_restart); - restart(is_hyp_nvhe(), kimage->start, kimage->arch.dtb_mem, + restart(is_hyp_nvhe(), kimage->start, kimage->arch.param_mem, 0, 0); } else { void (*kernel_reloc)(struct kimage *kimage); diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index af1ca875c52ce..9fca3a35f04d5 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -184,6 +184,7 @@ int load_other_segments(struct kimage *image, goto out_err; image->arch.dtb = dtb; image->arch.dtb_mem = kbuf.mem; + image->arch.param_mem = image->arch.dtb_mem; kexec_dprintk("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n", kbuf.mem, kbuf.bufsz, kbuf.memsz); diff --git a/arch/arm64/kernel/relocate_kernel.S b/arch/arm64/kernel/relocate_kernel.S index 413f899e4ac63..84e1bbb7def21 100644 --- a/arch/arm64/kernel/relocate_kernel.S +++ b/arch/arm64/kernel/relocate_kernel.S @@ -44,7 +44,7 @@ SYM_CODE_START(arm64_relocate_new_kernel) */ ldr x28, [x0, #KIMAGE_START] ldr x27, [x0, #KIMAGE_ARCH_EL2_VECTORS] - ldr x26, [x0, #KIMAGE_ARCH_DTB_MEM] + ldr x26, [x0, #KIMAGE_ARCH_PARAM_MEM] /* Setup the list loop variables. */ ldr x18, [x0, #KIMAGE_ARCH_ZERO_PAGE] /* x18 = zero page for BBM */ From patchwork Mon Aug 19 14:53:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 821082 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AB103175D53 for ; Mon, 19 Aug 2024 14:56:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079369; cv=none; b=oFiUmZ7JkRl8h/RwQNQLPL/t35DrZLBkGtkbWi53tHXQR7swAKUYxYAFVD1BYr3b/FSoa+PW6+t0w3s2r0mvjmeK23Db7WUd3P2Ipx8D/EE4HaS5VqSDbHIxdh689BTamoVupFY9UAtLCJbHyVjRTHHy8FQfhqI+SxNuvMkWV8Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079369; c=relaxed/simple; bh=+dthhNZKDtKKFU7kbV8zoxuDYOoD9TPphYxpvoesauY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YQz6cdfbv4U53vCzhjF0E+Hc31w5n3pbaH/tZPpNy7AWD9s9o8plfZPQ89KNMRwdRkigo+HGhx/Wfnv8QAM/GUAagLnw5zGEdOjQ6R5nEUYUT1WfOIhVOoE1NLYHUsShCNTG0vLFOwMV1nwVYHPy8gS2GTKzxvuK7sfvPzQoDlE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=cGvwVfAT; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="cGvwVfAT" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079366; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=uVVz1zO8BEoWzJpfVYvNY32S88JW7SsI5g78gkxTnig=; b=cGvwVfATJkRNy0e4LLmf3gGx+rmjWjewD1D7WyAyFmHK79lxD+efB/eYyzmE83NSSw1bca Qfd8xQpYhkNe9jk9Tj7G9HD8Hu5mXPyjuEjCYpZHhI20ch0ob9DpPjlGAnMfkMa9QzNzaH 7eyuErRhucZUxXFR/UqSMvpwPoQSA1k= Received: from mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-635-L0cX0TIwN3KmO02ATSssbA-1; Mon, 19 Aug 2024 10:56:00 -0400 X-MC-Unique: L0cX0TIwN3KmO02ATSssbA-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-03.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id D9C761955BF7; Mon, 19 Aug 2024 14:55:58 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 281A419560A3; Mon, 19 Aug 2024 14:55:49 +0000 (UTC) From: Pingfan Liu To: linux-arm-kernel@lists.infradead.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFCv2 7/9] arm64: mm: Change to prototype of Date: Mon, 19 Aug 2024 22:53:40 +0800 Message-ID: <20240819145417.23367-8-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 The current allocator of __create_pgd_mapping_locked() takes only one parameter, which reflects the current level of page table. But kexec_page_alloc() should extra control information from the parameter kimage. In order to make kexec_page_alloc() as an allocator, re-shape the prototype of the allocator and hence of the __create_pgd_mapping_locked(). Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel Cc: Mark Rutland Cc: Will Deacon Cc: Catalin Marinas To: linux-arm-kernel@lists.infradead.org --- arch/arm64/include/asm/mmu.h | 6 ++++ arch/arm64/mm/mmu.c | 67 ++++++++++++++++++++---------------- 2 files changed, 44 insertions(+), 29 deletions(-) diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h index 65977c7783c58..4b908e0650105 100644 --- a/arch/arm64/include/asm/mmu.h +++ b/arch/arm64/include/asm/mmu.h @@ -64,6 +64,12 @@ extern void arm64_memblock_init(void); extern void paging_init(void); extern void bootmem_init(void); extern void __iomem *early_io_map(phys_addr_t phys, unsigned long virt); +extern void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys, + unsigned long virt, phys_addr_t size, + pgprot_t prot, + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, + int flags); extern void create_mapping_noalloc(phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot); extern void create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c index c927e9312f102..875e2bc0cfb39 100644 --- a/arch/arm64/mm/mmu.c +++ b/arch/arm64/mm/mmu.c @@ -106,7 +106,7 @@ pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn, } EXPORT_SYMBOL(phys_mem_access_prot); -static phys_addr_t __init early_pgtable_alloc(int shift) +static phys_addr_t __init early_pgtable_alloc(int shift, void *unused) { phys_addr_t phys; @@ -190,7 +190,8 @@ static void init_pte(pte_t *ptep, unsigned long addr, unsigned long end, static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), + phys_addr_t (*pgtable_alloc)(int, void *arg), + void *arg, int flags) { unsigned long next; @@ -205,7 +206,7 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr, if (flags & NO_EXEC_MAPPINGS) pmdval |= PMD_TABLE_PXN; BUG_ON(!pgtable_alloc); - pte_phys = pgtable_alloc(PAGE_SHIFT); + pte_phys = pgtable_alloc(PAGE_SHIFT, arg); ptep = pte_set_fixmap(pte_phys); init_clear_pgtable(ptep); ptep += pte_index(addr); @@ -241,7 +242,9 @@ static void alloc_init_cont_pte(pmd_t *pmdp, unsigned long addr, static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), int flags) + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, + int flags) { unsigned long next; @@ -263,7 +266,7 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end, READ_ONCE(pmd_val(*pmdp)))); } else { alloc_init_cont_pte(pmdp, addr, next, phys, prot, - pgtable_alloc, flags); + pgtable_alloc, arg, flags); BUG_ON(pmd_val(old_pmd) != 0 && pmd_val(old_pmd) != READ_ONCE(pmd_val(*pmdp))); @@ -275,7 +278,9 @@ static void init_pmd(pmd_t *pmdp, unsigned long addr, unsigned long end, static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), int flags) + phys_addr_t (*pgtable_alloc)(int, void *), + void * arg, + int flags) { unsigned long next; pud_t pud = READ_ONCE(*pudp); @@ -292,7 +297,7 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, if (flags & NO_EXEC_MAPPINGS) pudval |= PUD_TABLE_PXN; BUG_ON(!pgtable_alloc); - pmd_phys = pgtable_alloc(PMD_SHIFT); + pmd_phys = pgtable_alloc(PMD_SHIFT, arg); pmdp = pmd_set_fixmap(pmd_phys); init_clear_pgtable(pmdp); pmdp += pmd_index(addr); @@ -312,7 +317,7 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, (flags & NO_CONT_MAPPINGS) == 0) __prot = __pgprot(pgprot_val(prot) | PTE_CONT); - init_pmd(pmdp, addr, next, phys, __prot, pgtable_alloc, flags); + init_pmd(pmdp, addr, next, phys, __prot, pgtable_alloc, arg, flags); pmdp += pmd_index(next) - pmd_index(addr); phys += next - addr; @@ -323,7 +328,8 @@ static void alloc_init_cont_pmd(pud_t *pudp, unsigned long addr, static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, int flags) { unsigned long next; @@ -337,7 +343,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end, if (flags & NO_EXEC_MAPPINGS) p4dval |= P4D_TABLE_PXN; BUG_ON(!pgtable_alloc); - pud_phys = pgtable_alloc(PUD_SHIFT); + pud_phys = pgtable_alloc(PUD_SHIFT, arg); pudp = pud_set_fixmap(pud_phys); init_clear_pgtable(pudp); pudp += pud_index(addr); @@ -368,7 +374,7 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end, READ_ONCE(pud_val(*pudp)))); } else { alloc_init_cont_pmd(pudp, addr, next, phys, prot, - pgtable_alloc, flags); + pgtable_alloc, arg, flags); BUG_ON(pud_val(old_pud) != 0 && pud_val(old_pud) != READ_ONCE(pud_val(*pudp))); @@ -381,7 +387,8 @@ static void alloc_init_pud(p4d_t *p4dp, unsigned long addr, unsigned long end, static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end, phys_addr_t phys, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, int flags) { unsigned long next; @@ -395,7 +402,7 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end, if (flags & NO_EXEC_MAPPINGS) pgdval |= PGD_TABLE_PXN; BUG_ON(!pgtable_alloc); - p4d_phys = pgtable_alloc(P4D_SHIFT); + p4d_phys = pgtable_alloc(P4D_SHIFT, arg); p4dp = p4d_set_fixmap(p4d_phys); init_clear_pgtable(p4dp); p4dp += p4d_index(addr); @@ -411,7 +418,7 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end, next = p4d_addr_end(addr, end); alloc_init_pud(p4dp, addr, next, phys, prot, - pgtable_alloc, flags); + pgtable_alloc, arg, flags); BUG_ON(p4d_val(old_p4d) != 0 && p4d_val(old_p4d) != READ_ONCE(p4d_val(*p4dp))); @@ -422,10 +429,11 @@ static void alloc_init_p4d(pgd_t *pgdp, unsigned long addr, unsigned long end, p4d_clear_fixmap(); } -static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys, +void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, int flags) { unsigned long addr, end, next; @@ -445,7 +453,7 @@ static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys, do { next = pgd_addr_end(addr, end); alloc_init_p4d(pgdp, addr, next, phys, prot, pgtable_alloc, - flags); + arg, flags); phys += next - addr; } while (pgdp++, addr = next, addr != end); } @@ -453,12 +461,13 @@ static void __create_pgd_mapping_locked(pgd_t *pgdir, phys_addr_t phys, static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), + phys_addr_t (*pgtable_alloc)(int, void *), + void *arg, int flags) { mutex_lock(&fixmap_lock); __create_pgd_mapping_locked(pgdir, phys, virt, size, prot, - pgtable_alloc, flags); + pgtable_alloc, arg, flags); mutex_unlock(&fixmap_lock); } @@ -466,10 +475,10 @@ static void __create_pgd_mapping(pgd_t *pgdir, phys_addr_t phys, extern __alias(__create_pgd_mapping_locked) void create_kpti_ng_temp_pgd(pgd_t *pgdir, phys_addr_t phys, unsigned long virt, phys_addr_t size, pgprot_t prot, - phys_addr_t (*pgtable_alloc)(int), int flags); + phys_addr_t (*pgtable_alloc)(int, void *), void *arg, int flags); #endif -static phys_addr_t __pgd_pgtable_alloc(int shift) +static phys_addr_t __pgd_pgtable_alloc(int shift, void *unused) { /* Page is zeroed by init_clear_pgtable() so don't duplicate effort. */ void *ptr = (void *)__get_free_page(GFP_PGTABLE_KERNEL & ~__GFP_ZERO); @@ -478,9 +487,9 @@ static phys_addr_t __pgd_pgtable_alloc(int shift) return __pa(ptr); } -static phys_addr_t pgd_pgtable_alloc(int shift) +static phys_addr_t pgd_pgtable_alloc(int shift, void *unused) { - phys_addr_t pa = __pgd_pgtable_alloc(shift); + phys_addr_t pa = __pgd_pgtable_alloc(shift, unused); struct ptdesc *ptdesc = page_ptdesc(phys_to_page(pa)); /* @@ -512,7 +521,7 @@ void __init create_mapping_noalloc(phys_addr_t phys, unsigned long virt, &phys, virt); return; } - __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, + __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, NULL, NO_CONT_MAPPINGS); } @@ -528,7 +537,7 @@ void __init create_pgd_mapping(struct mm_struct *mm, phys_addr_t phys, flags = NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; __create_pgd_mapping(mm->pgd, phys, virt, size, prot, - pgd_pgtable_alloc, flags); + pgd_pgtable_alloc, NULL, flags); } static void update_mapping_prot(phys_addr_t phys, unsigned long virt, @@ -540,7 +549,7 @@ static void update_mapping_prot(phys_addr_t phys, unsigned long virt, return; } - __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, + __create_pgd_mapping(init_mm.pgd, phys, virt, size, prot, NULL, NULL, NO_CONT_MAPPINGS); /* flush the TLBs after updating live kernel mappings */ @@ -551,7 +560,7 @@ static void __init __map_memblock(pgd_t *pgdp, phys_addr_t start, phys_addr_t end, pgprot_t prot, int flags) { __create_pgd_mapping(pgdp, start, __phys_to_virt(start), end - start, - prot, early_pgtable_alloc, flags); + prot, early_pgtable_alloc, NULL, flags); } void __init mark_linear_text_alias_ro(void) @@ -742,7 +751,7 @@ static int __init map_entry_trampoline(void) memset(tramp_pg_dir, 0, PGD_SIZE); __create_pgd_mapping(tramp_pg_dir, pa_start, TRAMP_VALIAS, entry_tramp_text_size(), prot, - __pgd_pgtable_alloc, NO_BLOCK_MAPPINGS); + __pgd_pgtable_alloc, NULL, NO_BLOCK_MAPPINGS); /* Map both the text and data into the kernel page table */ for (i = 0; i < DIV_ROUND_UP(entry_tramp_text_size(), PAGE_SIZE); i++) @@ -1344,7 +1353,7 @@ int arch_add_memory(int nid, u64 start, u64 size, flags |= NO_BLOCK_MAPPINGS | NO_CONT_MAPPINGS; __create_pgd_mapping(swapper_pg_dir, start, __phys_to_virt(start), - size, params->pgprot, __pgd_pgtable_alloc, + size, params->pgprot, __pgd_pgtable_alloc, NULL, flags); memblock_clear_nomap(start, size); From patchwork Mon Aug 19 14:53:41 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 820426 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 04A1D1779B1 for ; Mon, 19 Aug 2024 14:56:15 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079377; cv=none; b=VSTM3FNFcr23KW8PEAEiyLvJYUkkLF5lcgxBR4bV6RnMa1hbsJ9G5qtI1JMbdHlOxloiTMRITK9v6HEG1qJnnUhXEKF6T7xAyDNl80hBpMQK0l8W/Vxv+VIxo7hOaUJX7LnjQq32KnMgyKXyshGGEh6RoOjt3ylZMp2GAoBnRmA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079377; c=relaxed/simple; bh=ehRAQ21OMPu9VTeAFsmD9ehQvbNSIsV1Aulxae74dQI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Bs7hJok+4nqa7TNCQPwDCB6X56Ye6gdN7EE0azQSYwS6v2r+9yRl6dzbI3QF1j3J3Pq1VfLzV6P3zeVlm9fa2+sbgfFHTT5CEpyMvtIJHbIlsNRhhT8sGOmA8DkmyQOegcVMiiKagYolpbS1E/TMo0485adrK4lw1IMLJLfInIs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=CVtoQz+J; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="CVtoQz+J" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079375; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=x1wlLI8HeV7ugiPZwClPrcrHBMcL2Ga2rfvmngOKpRU=; b=CVtoQz+JoYnvxaLm+l2cMxTw5a7Hh8S+mtQ8OPrbxA+qzDl6GGO/S2F4+FpyQmB//DCDKC qLpxNI+F06JMIST9dSH+LX1WZhFAY20Ht9olfUHJC5zsBTK8A4jlo7lTr2MWeV/Z3k3On+ TqJzU9xdVTBCG/wFYX8DYQJnM5Qy2ac= Received: from mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-113-C2pU-VYVONmHz2FowVFgWg-1; Mon, 19 Aug 2024 10:56:10 -0400 X-MC-Unique: C2pU-VYVONmHz2FowVFgWg-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id C97BB1955BF2; Mon, 19 Aug 2024 14:56:08 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id D257319560A3; Mon, 19 Aug 2024 14:55:59 +0000 (UTC) From: Pingfan Liu To: linux-arm-kernel@lists.infradead.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFCv2 8/9] arm64: kexec: Prepare page table for emulator Date: Mon, 19 Aug 2024 22:53:41 +0800 Message-ID: <20240819145417.23367-9-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 The emulator will run in identity mapping, and the first kernel prepares for it. The special allocator resorts to kimage_alloc_control_pages(), which can avoid the allocation on the spot of the destination and overwritten during copying the kernel. The identity mapping covers only all of the kexec segments and efi runtime service. This reduces the memory consumption of page table. Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel Cc: Mark Rutland Cc: Will Deacon Cc: Catalin Marinas To: linux-arm-kernel@lists.infradead.org --- arch/arm64/kernel/machine_kexec.c | 101 ++++++++++++++++++++++++++++-- include/linux/kexec.h | 5 +- 2 files changed, 101 insertions(+), 5 deletions(-) diff --git a/arch/arm64/kernel/machine_kexec.c b/arch/arm64/kernel/machine_kexec.c index 6958c1fc84a5a..871ee1163ebca 100644 --- a/arch/arm64/kernel/machine_kexec.c +++ b/arch/arm64/kernel/machine_kexec.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -86,8 +87,22 @@ static void kexec_segment_flush(const struct kimage *kimage) } } +/* todo: alloc page for the pgtable used by efi emulator in crashkernel range */ +static phys_addr_t crash_page_alloc(int unused, void *arg) +{ + struct kimage *kimage = (struct kimage *)arg; + int i; + + //skip kimage->segment[].mem + for (i = 0; i < kimage->nr_segments; i ++) { + //seg = &kimage->segment[i]; + } + //skip any range allocated + return -1; +} + /* Allocates pages for kexec page table */ -static void *kexec_page_alloc(void *arg) +static void *__kexec_page_alloc(void *arg) { struct kimage *kimage = arg; struct page *page = kimage_alloc_control_pages(kimage, 0); @@ -102,6 +117,82 @@ static void *kexec_page_alloc(void *arg) return vaddr; } +static phys_addr_t kexec_page_alloc(int unused, void *arg) +{ + void *vaddr; + + vaddr = __kexec_page_alloc(arg); + if (!vaddr) + return (phys_addr_t)-1; + return virt_to_phys(vaddr); +} + +/* + * This function should be called after all kimage segments have been profiled + * Return physical address of page table's root + */ +phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage, + struct efi_emulator_param *param) +{ + efi_memory_desc_t *md; + struct kexec_segment *seg; + unsigned long paddr, vaddr, sz; + pgd_t *pgd; + typedef phys_addr_t (* alloc_fn)(int, void *); + alloc_fn alloc; + phys_addr_t pgd_paddr; + int i; + + /* + * Set up pgtable of emulator, either for crash or for reboot. + * All of the segments have been profiled, and kimage_alloc_normal_control_pages() + * will allocate page in safe zone. + * On the other hand, these pages are not in any segment, which means they are + * left, not copied. Hence the radix tree laying out on them is not broken. + */ + if (kimage->head & IND_DONE) + alloc = crash_page_alloc; + else + alloc = kexec_page_alloc; + pgd_paddr = alloc(0, kimage); + pgd = (pgd_t *)phys_to_virt(pgd_paddr); + for (i = 0; i < kimage->nr_segments; i ++) { + seg = &kimage->segment[i]; + paddr = ALIGN_DOWN(seg->mem, PAGE_SIZE); + sz = ALIGN(seg->mem - paddr + seg->memsz, PAGE_SIZE); + kexec_dprintk("Set up mapping for phyaddr: 0x%lx, size:0x%lx", paddr, sz); + //todo: distinguish executable segment + __create_pgd_mapping_locked(pgd, paddr, paddr, sz, + PAGE_KERNEL_EXEC, alloc, kimage, 0); + } + + /* + * UEFI stub can call EFI runtime service either before or after one-shot + * SetVirtualAddressMap(). That means the mapping for + * EFI_RUNTIME_SERVICES_CODE/_DATA should be set up here. + * And the virtual address range occupied by md must be reserved, + * accordingly, its physical address should not be allocated by kexec + * allocator + */ + for_each_efi_memory_desc(md) { + if (md->attribute & EFI_MEMORY_RUNTIME) { + vaddr = md->virt_addr; + paddr = md->phys_addr; + sz = md->num_pages * EFI_PAGE_SIZE; + kexec_dprintk("Set up mapping for md phyaddr: 0x%lx, virt: 0x%lx, size:0x%lx", paddr, vaddr, sz); + __create_pgd_mapping_locked(pgd, paddr, vaddr, sz, + PAGE_KERNEL_EXEC, alloc, kimage, 0); + } + } + + if (param->print_enabled) + __create_pgd_mapping_locked(pgd, param->earlycon_reg_base, + param->earlycon_reg_base, param->earlycon_reg_sz, + pgprot_device(PAGE_KERNEL), alloc, kimage, 0); + + return pgd_paddr; +} + int machine_kexec_post_load(struct kimage *kimage) { int rc; @@ -109,7 +200,7 @@ int machine_kexec_post_load(struct kimage *kimage) void *reloc_code = page_to_virt(kimage->control_code_page); long reloc_size; struct trans_pgd_info info = { - .trans_alloc_page = kexec_page_alloc, + .trans_alloc_page = __kexec_page_alloc, .trans_alloc_arg = kimage, }; @@ -129,7 +220,7 @@ int machine_kexec_post_load(struct kimage *kimage) } /* Create a copy of the linear map */ - trans_pgd = kexec_page_alloc(kimage); + trans_pgd = __kexec_page_alloc(kimage); if (!trans_pgd) return -ENOMEM; rc = trans_pgd_create_copy(&info, &trans_pgd, PAGE_OFFSET, PAGE_END); @@ -145,6 +236,7 @@ int machine_kexec_post_load(struct kimage *kimage) &kimage->arch.t0sz, reloc_code); if (rc) return rc; + kimage->arch.phys_offset = virt_to_phys(kimage) - (long)kimage; /* Flush the reloc_code in preparation for its execution. */ @@ -175,7 +267,6 @@ void machine_kexec(struct kimage *kimage) "Some CPUs may be stale, kdump will be unreliable.\n"); pr_info("Bye!\n"); - local_daif_mask(); /* @@ -192,6 +283,7 @@ void machine_kexec(struct kimage *kimage) cpu_install_idmap(); restart = (void *)__pa_symbol(cpu_soft_restart); + /* kimage->start can be either the entry of kernel or efi emulator */ restart(is_hyp_nvhe(), kimage->start, kimage->arch.param_mem, 0, 0); } else { @@ -201,6 +293,7 @@ void machine_kexec(struct kimage *kimage) __hyp_set_vectors(kimage->arch.el2_vectors); cpu_install_ttbr0(kimage->arch.ttbr0, kimage->arch.t0sz); kernel_reloc = (void *)kimage->arch.kern_reloc; + //tell between the emulator and normal kernel inside the relocate code kernel_reloc(kimage); } diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 57b98bcaa5228..1599c21e7c5d5 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -22,6 +22,7 @@ #include #include +#include extern note_buf_t __percpu *crash_notes; @@ -464,7 +465,9 @@ static inline int arch_kexec_post_alloc_pages(void *vaddr, unsigned int pages, g static inline void arch_kexec_pre_free_pages(void *vaddr, unsigned int pages) { } #endif -extern phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage); +extern phys_addr_t arch_emulator_prepare_pgtable(struct kimage *kimage, + struct efi_emulator_param *param); + extern bool kexec_file_dbg_print; #define kexec_dprintk(fmt, arg...) \ From patchwork Mon Aug 19 14:53:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pingfan Liu X-Patchwork-Id: 821081 Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 47E0D17557E for ; Mon, 19 Aug 2024 14:56:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079387; cv=none; b=HzZ/Q7RM89uKJT1I8fJLe+Ap0Z509TbgA0Ojna4ODPidaCI3ar52JpX65Hv+Mj/4uRYrUPlvv9sW2UzYH9YrjOS6tP64RfEDRkpn5qSTOyWdIYMJGEpG0u9OV3NeI34OpKFNv9WW/Dqq1wtiCsg2hlz1iu9q5j+NBAwA8PNcjgU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1724079387; c=relaxed/simple; bh=bexWma/7ne+ZIXrU6dDSPnHuDTwc7JRVTEsbOGaQTlo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KL0v5Y5Lp51JhE7KQGDfP4jNIS8rO6jE2EgNdvay0oxy34ZIj9nbhcunzIplqN46QV/Hhs1UB/6Aan2jg4VjmHCiSGwEg7PNA8fnN+lE1BE3Atdm0uyKNqZXdkhxCyYvLPXtzhaQXKhEPpKftR20LJSIbIpeSoydB/xDSaozSG4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com; spf=pass smtp.mailfrom=redhat.com; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b=D8UW8Flb; arc=none smtp.client-ip=170.10.129.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=redhat.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=redhat.com header.i=@redhat.com header.b="D8UW8Flb" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1724079383; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=tuBomQYrL4neI9/UIiUoEKpHGEUfWQV2ECUiNi8myCc=; b=D8UW8FlbKYEPDCaUMpQuUohZiu7TVBsrGzxIYoiVY2gfg9/vFdoNJ4Tku3ePWFSBolOTdy cCIGZ0/WrqVWTXoPonKCu7kTmuVyoIAjCVSFBeS5+bE3sN+UBh9/1PzZxErOeWT3O++UHm /f+M9GqDOZf6px9aFJJwOvyvWcN58r0= Received: from mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-193-CWQEnUUsNYCtspDcNI2Rqg-1; Mon, 19 Aug 2024 10:56:20 -0400 X-MC-Unique: CWQEnUUsNYCtspDcNI2Rqg-1 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-04.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 4DEEC1955D57; Mon, 19 Aug 2024 14:56:18 +0000 (UTC) Received: from fedora.redhat.com (unknown [10.72.116.15]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 9A8CE1955F45; Mon, 19 Aug 2024 14:56:09 +0000 (UTC) From: Pingfan Liu To: linux-arm-kernel@lists.infradead.org Cc: Pingfan Liu , Ard Biesheuvel , Jan Hendrik Farr , Philipp Rudo , Lennart Poettering , Jarkko Sakkinen , Eric Biederman , Baoquan He , Dave Young , Mark Rutland , Will Deacon , Catalin Marinas , kexec@lists.infradead.org, linux-efi@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [RFCv2 9/9] arm64: kexec: Enable kexec_pe_image Date: Mon, 19 Aug 2024 22:53:42 +0800 Message-ID: <20240819145417.23367-10-piliu@redhat.com> In-Reply-To: <20240819145417.23367-1-piliu@redhat.com> References: <20240819145417.23367-1-piliu@redhat.com> Precedence: bulk X-Mailing-List: linux-efi@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 Now, anything is ready, enable PE image loading on arm64 Signed-off-by: Pingfan Liu Cc: Ard Biesheuvel Cc: Mark Rutland Cc: Will Deacon Cc: Catalin Marinas To: linux-arm-kernel@lists.infradead.org --- arch/arm64/Kconfig | 4 ++++ arch/arm64/kernel/machine_kexec_file.c | 3 +++ 2 files changed, 7 insertions(+) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 5d91259ee7b53..fb6de7b4cd008 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -1550,6 +1550,10 @@ config ARCH_SELECTS_KEXEC_FILE depends on KEXEC_FILE select HAVE_IMA_KEXEC if IMA +config ARCH_SELECTS_KEXEC_PEIMAGE + def_bool y + depends on KEXEC_FILE + config ARCH_SUPPORTS_KEXEC_SIG def_bool y diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c index 9fca3a35f04d5..c259d871b8e33 100644 --- a/arch/arm64/kernel/machine_kexec_file.c +++ b/arch/arm64/kernel/machine_kexec_file.c @@ -24,6 +24,9 @@ const struct kexec_file_ops * const kexec_file_loaders[] = { &kexec_image_ops, +#ifdef CONFIG_ARCH_SELECTS_KEXEC_PEIMAGE + &pe_image_ops, +#endif NULL };