diff --git a/kernel/src/device/tdxguest/mod.rs b/kernel/src/device/tdxguest/mod.rs index 1f271cd55..8739f3821 100644 --- a/kernel/src/device/tdxguest/mod.rs +++ b/kernel/src/device/tdxguest/mod.rs @@ -1,12 +1,18 @@ // SPDX-License-Identifier: MPL-2.0 -use core::{mem::size_of, time::Duration}; +use core::{ + mem::{offset_of, size_of}, + time::Duration, +}; -use align_ext::AlignExt; use aster_util::{field_ptr, safe_ptr::SafePtr}; use device_id::DeviceId; use ostd::{ - mm::{DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, VmIo, PAGE_SIZE}, + const_assert, + mm::{ + io_util::HasVmReaderWriter, DmaCoherent, FrameAllocOptions, HasPaddr, HasSize, USegment, + VmIo, PAGE_SIZE, + }, sync::WaitQueue, }; use tdx_guest::{ @@ -26,16 +32,6 @@ use crate::{ process::signal::{PollHandle, Pollable}, }; -const TDX_REPORTDATA_LEN: usize = 64; -const TDX_REPORT_LEN: usize = 1024; - -#[derive(Debug, Clone, Copy, Pod)] -#[repr(C)] -pub struct TdxReportRequest { - report_data: [u8; TDX_REPORTDATA_LEN], - tdx_report: [u8; TDX_REPORT_LEN], -} - pub struct TdxGuest; impl Device for TdxGuest { @@ -126,9 +122,12 @@ pub fn tdx_get_quote(inblob: &[u8]) -> Result> { field_ptr!(&report_ptr, TdxQuoteHdr, version).write(&1u64)?; field_ptr!(&report_ptr, TdxQuoteHdr, status).write(&0u64)?; - field_ptr!(&report_ptr, TdxQuoteHdr, in_len).write(&(TDX_REPORT_LEN as u32))?; + field_ptr!(&report_ptr, TdxQuoteHdr, in_len).write(&(size_of::() as u32))?; field_ptr!(&report_ptr, TdxQuoteHdr, out_len).write(&0u32)?; - buf.write_bytes(size_of::(), &report)?; + buf.write( + size_of::(), + report.reader().to_fallible().limit(size_of::()), + )?; // FIXME: The `get_quote` API from the `tdx_guest` crate should have been marked `unsafe` // because it has no way to determine if the input physical address is safe or not. @@ -168,50 +167,118 @@ struct TdxQuoteHdr { out_len: u32, } +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct TdxReportRequest { + report_data: ReportData, + tdx_report: TdReport, +} + +impl TdxReportRequest { + fn report_inblob(&self) -> &[u8] { + self.report_data.as_bytes() + } +} + +/// TDX Report structure (`TDREPORT_STRUCT`) as defined in the Intel TDX Module Specification. +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct TdReport { + report_mac: ReportMac, + _reserved: [u8; 256], + td_info: TdInfo, +} +const_assert!(size_of::() == 1024); + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct ReportMac { + _reserved1: [u8; 128], + report_data: ReportData, + _reserved2: [u8; 64], +} + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct TdInfo { + attributes: [u8; 8], + xfam: [u8; 8], + mrtd: [u8; 48], + mrconfigid: [u8; 48], + mrowner: [u8; 48], + mrownerconfig: [u8; 48], + rtmr1: [u8; 48], + rtmr2: [u8; 48], + rtmr3: [u8; 48], + rtmr4: [u8; 48], + servtd_hash: [u8; 48], + extension: [u8; 64], +} + +#[derive(Debug, Clone, Copy, Pod)] +#[repr(C)] +struct ReportData { + data: [u8; 64], +} + +impl TdReport { + const fn report_data_offset() -> usize { + offset_of!(TdReport, report_mac) + offset_of!(ReportMac, report_data) + } +} + fn handle_get_report(arg: usize) -> Result { let current_task = ostd::task::Task::current().unwrap(); let user_space = CurrentUserSpace::new(current_task.as_thread_local().unwrap()); let user_request: TdxReportRequest = user_space.read_val(arg)?; - let report = tdx_get_report(&user_request.report_data)?; + let report = tdx_get_report(&user_request.report_inblob())?; - let tdx_report_vaddr = arg + TDX_REPORTDATA_LEN; - user_space.write_bytes(tdx_report_vaddr, &mut VmReader::from(report.as_ref()))?; + let tdx_report_vaddr = arg + offset_of!(TdxReportRequest, tdx_report); + user_space.write_bytes( + tdx_report_vaddr, + report.reader().limit(size_of::()), + )?; Ok(0) } -fn tdx_get_report(inblob: &[u8]) -> Result> { - if inblob.len() != TDX_REPORTDATA_LEN { +/// Gets the TDX report given the specified data in `inblob`. +/// +/// The first `size_of::()` bytes of data in the returned `USegment` is the report. +/// The rest in `USegment` should be ignored. +fn tdx_get_report(inblob: &[u8]) -> Result { + if inblob.len() != size_of::() { return_errno_with_message!(Errno::EINVAL, "Invalid inblob length"); } - let segment = FrameAllocOptions::new().alloc_segment(2)?; - let dma_coherent = DmaCoherent::map(segment.into(), false).unwrap(); - dma_coherent.write_bytes(0, &inblob).unwrap(); + let report: USegment = { + const REPORT_SIZE_IN_PAGES: usize = size_of::().div_ceil(PAGE_SIZE); + FrameAllocOptions::new() + .alloc_segment(REPORT_SIZE_IN_PAGES)? + .into() + }; + + // Use `inblob` as the data associated with the report. + let report_data_paddr = { + // From TDX Module Specification, the report structure returned by TDX Module + // places the report data at offset 128, so using the same offset keeps the + // memory layout consistent with the TDX Modules's output format. And we can + // directly call `get_report` on the existing report structure without needing + // to rewrite the report data. + report + .write_bytes(TdReport::report_data_offset(), inblob) + .unwrap(); + report.paddr() + TdReport::report_data_offset() + }; // FIXME: The `get_report` API from the `tdx_guest` crate should have been marked `unsafe` // because it has no way to determine if the input physical address is safe or not. - get_report( - ((dma_coherent.paddr() + 1024) as u64) | SHARED_MASK, - (dma_coherent.paddr() as u64) | SHARED_MASK, - )?; - - // Note: We cannot convert `DmaCoherent` to `USegment` here. When shared memory is converted back - // to private memory in TDX, `TDG.MEM.PAGE.ACCEPT` will zero out all content. - // TDX Module Specification - `TDG.MEM.PAGE.ACCEPT` Leaf: - // "Accept a pending private page and initialize it to all-0 using the TD ephemeral private key." - let mut generated_report = Box::new([0u8; TDX_REPORT_LEN]); - dma_coherent - .read_bytes(1024, generated_report.as_mut()) - .unwrap(); - - Ok(generated_report) + get_report(report.paddr() as u64, report_data_paddr as u64)?; + Ok(report) } fn alloc_dma_buf(buf_len: usize) -> Result { - let aligned_buf_len = buf_len.align_up(PAGE_SIZE); - let segment = FrameAllocOptions::new().alloc_segment(aligned_buf_len / PAGE_SIZE)?; - + let segment = FrameAllocOptions::new().alloc_segment(buf_len.div_ceil(PAGE_SIZE))?; let dma_buf = DmaCoherent::map(segment.into(), false).unwrap(); Ok(dma_buf) }