From 2ab2cda9372b99a1f359375c172f624fe02d0084 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Mon, 19 Aug 2024 16:49:02 -0700 Subject: [PATCH] Add support for reading hypervisor console (#344) * feature(xencall): add hypervisor SYSCTL_readconsole definitions * feature(hypervisor-dmesg): xencall: add read_console_ring_raw hypercall wrapper * feature(hypervisor-dmesg): protobuf: add ReadHypervisorConsoleRing RPC * feature(hypervisor-dmesg): runtime: add read_hypervisor_console wrapper * feature(hypervisor-dmesg): daemon: add ReadHypervisorConsoleRing rpc implementation * feature(hypervisor-dmesg): ctl: add host hypervisor-messages command to get hypervisor messages * feature(hypervisor-dmesg): cli: rename hypervisor-messages command to hv-console * feature(hypervisor-dmesg): proto: change ReadHypervisorConsoleRing to ReadHypervisorConsole * feature(hypervisor-dmesg): fix up kratactl protobuf calls --- crates/ctl/src/cli/host/hv_console.rs | 24 ++++++++++++++++ crates/ctl/src/cli/host/mod.rs | 5 ++++ crates/daemon/src/control.rs | 11 ++++++++ crates/krata/proto/krata/v1/control.proto | 8 ++++++ crates/runtime/src/lib.rs | 10 +++++++ crates/xen/xencall/examples/console_read.rs | 18 ++++++++++++ crates/xen/xencall/src/lib.rs | 31 +++++++++++++++++++-- crates/xen/xencall/src/sys.rs | 13 +++++++++ 8 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 crates/ctl/src/cli/host/hv_console.rs create mode 100644 crates/xen/xencall/examples/console_read.rs diff --git a/crates/ctl/src/cli/host/hv_console.rs b/crates/ctl/src/cli/host/hv_console.rs new file mode 100644 index 0000000..7942c4e --- /dev/null +++ b/crates/ctl/src/cli/host/hv_console.rs @@ -0,0 +1,24 @@ +use anyhow::Result; +use clap::Parser; +use krata::v1::control::{ + control_service_client::ControlServiceClient, ReadHypervisorConsoleRequest, +}; + +use tonic::{transport::Channel, Request}; + +#[derive(Parser)] +#[command(about = "Display hypervisor diagnostic messages")] +pub struct HostHvConsoleCommand { +} + +impl HostHvConsoleCommand { + pub async fn run(self, mut client: ControlServiceClient) -> Result<()> { + let response = client + .read_hypervisor_console(Request::new(ReadHypervisorConsoleRequest {})) + .await? + .into_inner(); + + print!("{}", response.data); + Ok(()) + } +} diff --git a/crates/ctl/src/cli/host/mod.rs b/crates/ctl/src/cli/host/mod.rs index 9c08dbe..4eaa130 100644 --- a/crates/ctl/src/cli/host/mod.rs +++ b/crates/ctl/src/cli/host/mod.rs @@ -8,10 +8,12 @@ use krata::v1::control::control_service_client::ControlServiceClient; use crate::cli::host::cpu_topology::HostCpuTopologyCommand; use crate::cli::host::identify::HostStatusCommand; use crate::cli::host::idm_snoop::HostIdmSnoopCommand; +use crate::cli::host::hv_console::HostHvConsoleCommand; pub mod cpu_topology; pub mod identify; pub mod idm_snoop; +pub mod hv_console; #[derive(Parser)] #[command(about = "Manage the host of the isolation engine")] @@ -35,6 +37,7 @@ pub enum HostCommands { CpuTopology(HostCpuTopologyCommand), Status(HostStatusCommand), IdmSnoop(HostIdmSnoopCommand), + HvConsole(HostHvConsoleCommand), } impl HostCommands { @@ -49,6 +52,8 @@ impl HostCommands { HostCommands::Status(status) => status.run(client).await, HostCommands::IdmSnoop(snoop) => snoop.run(client, events).await, + + HostCommands::HvConsole(hvconsole) => hvconsole.run(client).await, } } } diff --git a/crates/daemon/src/control.rs b/crates/daemon/src/control.rs index e674cc6..5f3c980 100644 --- a/crates/daemon/src/control.rs +++ b/crates/daemon/src/control.rs @@ -28,6 +28,7 @@ use krata::{ ReadZoneMetricsReply, ReadZoneMetricsRequest, ResolveZoneIdReply, ResolveZoneIdRequest, SnoopIdmReply, SnoopIdmRequest, UpdateZoneResourcesReply, UpdateZoneResourcesRequest, WatchEventsReply, WatchEventsRequest, ZoneConsoleReply, ZoneConsoleRequest, + ReadHypervisorConsoleRequest, ReadHypervisorConsoleReply, }, }, }; @@ -710,4 +711,14 @@ impl ControlService for DaemonControlService { .map_err(ApiError::from)?; Ok(Response::new(UpdateZoneResourcesReply {})) } + + async fn read_hypervisor_console( + &self, + _request: Request, + ) -> Result, Status> { + let data = self.runtime.read_hypervisor_console(false).await.map_err(|error| ApiError { + message: error.to_string(), + })?; + Ok(Response::new(ReadHypervisorConsoleReply { data: data.to_string() })) + } } diff --git a/crates/krata/proto/krata/v1/control.proto b/crates/krata/proto/krata/v1/control.proto index 3197c40..863bf2d 100644 --- a/crates/krata/proto/krata/v1/control.proto +++ b/crates/krata/proto/krata/v1/control.proto @@ -35,6 +35,8 @@ service ControlService { rpc ReadZoneMetrics(ReadZoneMetricsRequest) returns (ReadZoneMetricsReply); rpc WatchEvents(WatchEventsRequest) returns (stream WatchEventsReply); + + rpc ReadHypervisorConsole(ReadHypervisorConsoleRequest) returns (ReadHypervisorConsoleReply); } message HostStatusRequest {} @@ -252,3 +254,9 @@ message UpdateZoneResourcesRequest { } message UpdateZoneResourcesReply {} + +message ReadHypervisorConsoleRequest {} + +message ReadHypervisorConsoleReply { + string data = 1; +} diff --git a/crates/runtime/src/lib.rs b/crates/runtime/src/lib.rs index 9e957e5..f9fe270 100644 --- a/crates/runtime/src/lib.rs +++ b/crates/runtime/src/lib.rs @@ -297,4 +297,14 @@ impl Runtime { let context = RuntimeContext::new().await?; Ok(PowerManagementContext { context }) } + + pub async fn read_hypervisor_console(&self, clear: bool) -> Result> { + let index = 0 as u32; + let (rawbuf, newindex) = self.context + .xen + .call + .read_console_ring_raw(clear, index).await?; + let buf = std::str::from_utf8(&rawbuf[..newindex as usize])?; + Ok(Arc::from(buf)) + } } diff --git a/crates/xen/xencall/examples/console_read.rs b/crates/xen/xencall/examples/console_read.rs new file mode 100644 index 0000000..96a4aae --- /dev/null +++ b/crates/xen/xencall/examples/console_read.rs @@ -0,0 +1,18 @@ +use xencall::error::Result; +use xencall::XenCall; + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::init(); + + let call = XenCall::open(0)?; + let index = 0 as u32; + let (buf, newindex) = call.read_console_ring_raw(false, index).await?; + + match std::str::from_utf8(&buf[..newindex as usize]) { + Ok(v) => print!("{}", v), + _ => panic!("unable to decode Xen console messages"), + }; + + Ok(()) +} diff --git a/crates/xen/xencall/src/lib.rs b/crates/xen/xencall/src/lib.rs index 531122f..a69f989 100644 --- a/crates/xen/xencall/src/lib.rs +++ b/crates/xen/xencall/src/lib.rs @@ -26,12 +26,12 @@ use std::sync::Arc; use std::time::Duration; use sys::{ CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, Sysctl, SysctlCputopo, SysctlCputopoinfo, - SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlSetCpuFreqGov, SysctlValue, + SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlSetCpuFreqGov, SysctlValue, SysctlReadconsole, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP, HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION, XEN_MEM_SET_MEMORY_MAP, XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_MAX_INTERFACE_VERSION, XEN_SYSCTL_MIN_INTERFACE_VERSION, XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP, XEN_SYSCTL_PM_OP_DISABLE_TURBO, - XEN_SYSCTL_PM_OP_ENABLE_TURBO, XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, + XEN_SYSCTL_PM_OP_ENABLE_TURBO, XEN_SYSCTL_PM_OP_SET_CPUFREQ_GOV, XEN_SYSCTL_READCONSOLE, }; use tokio::sync::Semaphore; use tokio::time::sleep; @@ -1087,4 +1087,31 @@ impl XenCall { .await?; Ok(()) } + + pub async fn read_console_ring_raw(&self, clear: bool, index: u32) -> Result<([u8; 16384], u32)> { + let mut u8buf = [0u8; 16384]; + let mut sysctl = Sysctl { + cmd: XEN_SYSCTL_READCONSOLE, + interface_version: self.sysctl_interface_version, + value: SysctlValue { + console: SysctlReadconsole { + clear: clear as u8, + incremental: 1, + pad: 0, + index: index, + buffer: addr_of_mut!(u8buf) as u64, + count: 16384, + }, + }, + }; + self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong) + .await?; + // Safety: We are passing a SysctlReadconsole struct as part of the hypercall, and + // calling the hypercall is known to not change the underlying value outside changing + // the values on some SysctlReadconsole fields. + let newindex = unsafe { + sysctl.value.console.index + }; + Ok((u8buf, newindex)) + } } diff --git a/crates/xen/xencall/src/sys.rs b/crates/xen/xencall/src/sys.rs index 42b1307..9119319 100644 --- a/crates/xen/xencall/src/sys.rs +++ b/crates/xen/xencall/src/sys.rs @@ -752,6 +752,7 @@ pub struct SysctlCputopoinfo { #[repr(C)] pub union SysctlValue { + pub console: SysctlReadconsole, pub cputopoinfo: SysctlCputopoinfo, pub pm_op: SysctlPmOp, pub phys_info: SysctlPhysinfo, @@ -765,6 +766,7 @@ pub struct Sysctl { pub value: SysctlValue, } +pub const XEN_SYSCTL_READCONSOLE: u32 = 1; pub const XEN_SYSCTL_PHYSINFO: u32 = 3; pub const XEN_SYSCTL_PM_OP: u32 = 12; pub const XEN_SYSCTL_CPUTOPOINFO: u32 = 16; @@ -802,3 +804,14 @@ pub struct SysctlPhysinfo { pub max_mfn: u64, pub hw_cap: [u32; 8], } + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct SysctlReadconsole { + pub clear: u8, + pub incremental: u8, + pub pad: u16, + pub index: u32, + pub buffer: u64, + pub count: u32, +}