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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.