From: ardb@kernel.org
To: linux-efi@vger.kernel.org
Cc: Ard Biesheuvel <ardb@google.com>, Marc Zyngier <maz@kernel.org>,
Will Deacon <will@kernel.org>,
Quentin Perret <qperret@google.com>,
David Brazdil <dbrazdil@google.com>,
Fuad Tabba <tabba@google.com>, Kees Cook <keescook@chromium.org>
Subject: [RFC PATCH v0 4/6] Discover QEMU fwcfg device and use it to load the kernel
Date: Mon, 14 Mar 2022 09:26:42 +0100 [thread overview]
Message-ID: <20220314082644.3436071-5-ardb@kernel.org> (raw)
In-Reply-To: <20220314082644.3436071-1-ardb@kernel.org>
From: Ard Biesheuvel <ardb@google.com>
QEMU exposes a paravirtualized interface to load various items exposed
by the host into the guest. Implement a minimal driver for it, and use
it to load the kernel image into DRAM.
---
src/fwcfg.rs | 85 ++++++++++++++++++++
src/main.rs | 18 +++++
2 files changed, 103 insertions(+)
diff --git a/src/fwcfg.rs b/src/fwcfg.rs
new file mode 100644
index 000000000000..57f405df174b
--- /dev/null
+++ b/src/fwcfg.rs
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+use mmio::{Allow, Deny, VolBox};
+
+pub struct FwCfg {
+ // read-only data register
+ data: VolBox<u64, Allow, Deny>,
+
+ // write-only selector register
+ selector: VolBox<u16, Deny, Allow>,
+
+ // write-only DMA register
+ dmacontrol: VolBox<u64, Deny, Allow>,
+}
+
+const CFG_KERNEL_SIZE: u16 = 0x08;
+const CFG_KERNEL_DATA: u16 = 0x11;
+
+const CFG_DMACTL_DONE: u32 = 0;
+const CFG_DMACTL_ERROR: u32 = 1;
+const CFG_DMACTL_READ: u32 = 2;
+
+#[repr(C)]
+struct DmaTransfer {
+ control: u32,
+ length: u32,
+ address: u64,
+}
+
+impl FwCfg {
+ pub fn from_fdt_node(node: fdt::node::FdtNode) -> Option<FwCfg> {
+ if let Some(mut iter) = node.reg() {
+ iter.next().map(|reg| {
+ let addr = reg.starting_address;
+ unsafe {
+ FwCfg {
+ data: VolBox::<u64, Allow, Deny>::new(addr as *mut u64),
+ selector: VolBox::<u16, Deny, Allow>::new(addr.offset(8) as *mut u16),
+ dmacontrol: VolBox::<u64, Deny, Allow>::new(addr.offset(16) as *mut u64),
+ }
+ }
+ })
+ } else {
+ None
+ }
+ }
+
+ unsafe fn dma_transfer(
+ &mut self,
+ load_address: *mut u8,
+ size: usize,
+ config_item: u16,
+ ) -> Result<(), &str> {
+ let xfer = DmaTransfer {
+ control: u32::to_be(CFG_DMACTL_READ),
+ length: u32::to_be(size as u32),
+ address: u64::to_be(load_address as u64),
+ };
+ self.selector.write(u16::to_be(config_item));
+ self.dmacontrol.write(u64::to_be(&xfer as *const _ as u64));
+
+ let control = VolBox::<u32, Allow, Deny>::new(&xfer.control as *const _ as *mut u32);
+ loop {
+ match control.read() {
+ CFG_DMACTL_DONE => return Ok(()),
+ CFG_DMACTL_ERROR => return Err("fwcfg DMA error"),
+ _ => (), // keep polling
+ }
+ }
+ }
+
+ pub fn get_kernel_size(&mut self) -> usize {
+ self.selector.write(u16::to_be(CFG_KERNEL_SIZE));
+ self.data.read() as usize
+ }
+
+ pub fn load_kernel_image(&mut self, load_address: *mut u8) -> Result<(), &str> {
+ let size = self.get_kernel_size();
+ if size > 0 {
+ unsafe { self.dma_transfer(load_address, size, CFG_KERNEL_DATA) }
+ } else {
+ Err("No kernel image provided by fwcfg")
+ }
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index af58ccc0318d..048d1b4842cb 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8,6 +8,7 @@
mod console;
mod cstring;
+mod fwcfg;
mod pagealloc;
mod paging;
@@ -28,6 +29,8 @@ extern "C" {
static _dtb_end: u8;
}
+const LOAD_ADDRESS: *mut u8 = 0x43210000 as _;
+
#[no_mangle]
extern "C" fn efilite_main(base: usize, mapped: usize, used: usize) {
#[cfg(debug_assertions)]
@@ -79,6 +82,21 @@ extern "C" fn efilite_main(base: usize, mapped: usize, used: usize) {
// Switch to the new ID map so we can use all of DRAM
paging::activate();
+ let compat = ["qemu,fw-cfg-mmio"];
+ let fwcfg_node = fdt
+ .find_compatible(&compat)
+ .expect("QEMU fwcfg node not found");
+
+ info!("QEMU fwcfg node found: {}\n", fwcfg_node.name);
+
+ let mut fwcfg = fwcfg::FwCfg::from_fdt_node(fwcfg_node).expect("Failed to open fwcfg device");
+
+ // TODO allocate fwcfg.get_kernel_size() bytes here instead of using a fixed address
+
+ fwcfg
+ .load_kernel_image(LOAD_ADDRESS)
+ .expect("Failed to load kernel image");
+
// Switch back to the initial ID map so we can remap
// the loaded kernel image with different permissions
paging::deactivate();
--
2.30.2
next prev parent reply other threads:[~2022-03-14 8:27 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-03-14 8:26 [RFC PATCH v0 0/6] Minimal Linux/arm64 VM firmware (written in Rust) ardb
2022-03-14 8:26 ` [RFC PATCH v0 1/6] Implement a bare metal Rust runtime on top of QEMU's mach-virt ardb
2022-03-14 8:26 ` [RFC PATCH v0 2/6] Add DTB processing ardb
2022-03-14 8:26 ` [RFC PATCH v0 3/6] Add paging code to manage the full ID map ardb
2022-03-14 8:26 ` ardb [this message]
2022-03-14 8:26 ` [RFC PATCH v0 5/6] Remap code section of loaded kernel and boot it ardb
2022-03-14 8:26 ` [RFC PATCH v0 6/6] Temporarily pass the kaslr seed via register X1 ardb
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20220314082644.3436071-5-ardb@kernel.org \
--to=ardb@kernel.org \
--cc=ardb@google.com \
--cc=dbrazdil@google.com \
--cc=keescook@chromium.org \
--cc=linux-efi@vger.kernel.org \
--cc=maz@kernel.org \
--cc=qperret@google.com \
--cc=tabba@google.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).