fw_cfg.rs22.54%
1
// Copyright 2024 Google LLC2
//3
// Licensed under the Apache License, Version 2.0 (the "License");4
// you may not use this file except in compliance with the License.5
// You may obtain a copy of the License at6
//7
// https://www.apache.org/licenses/LICENSE-2.08
//9
// Unless required by applicable law or agreed to in writing, software10
// distributed under the License is distributed on an "AS IS" BASIS,11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12
// See the License for the specific language governing permissions and13
// limitations under the License.14
15
pub mod acpi;16
17
use std::ffi::CString;18
use std::fmt;19
use std::fs::File;20
use std::io::{ErrorKind, Read, Result, Seek, SeekFrom};21
use std::mem::{size_of, size_of_val};22
use std::os::unix::fs::FileExt;23
use std::path::Path;24
use std::sync::Arc;25
26
use alioth_macros::Layout;27
use bitfield::bitfield;28
use parking_lot::Mutex;29
use serde::de::{self, MapAccess, Visitor};30
use serde::{Deserialize, Deserializer};31
use serde_aco::Help;32
use zerocopy::{FromBytes, Immutable, IntoBytes};33
34
use crate::arch::layout::{35
PORT_FW_CFG_DATA, PORT_FW_CFG_DMA_HI, PORT_FW_CFG_DMA_LO, PORT_FW_CFG_SELECTOR,36
};37
use crate::device::{self, MmioDev, Pause};38
use crate::firmware::acpi::AcpiTable;39
#[cfg(target_arch = "x86_64")]40
use crate::loader::linux::bootparams::{41
BootE820Entry, BootParams, E820_ACPI, E820_PMEM, E820_RAM, E820_RESERVED,42
};43
use crate::mem;44
use crate::mem::emulated::{Action, Mmio};45
use crate::mem::mapped::RamBus;46
use crate::mem::{MemRegionEntry, MemRegionType};47
use crate::utils::endian::{Bu16, Bu32, Bu64, Lu16, Lu32, Lu64};48
49
use self::acpi::create_acpi_loader;50
51
pub const SELECTOR_WR: u16 = 1 << 14;52
53
pub const FW_CFG_SIGNATURE: u16 = 0x00;54
pub const FW_CFG_ID: u16 = 0x01;55
pub const FW_CFG_UUID: u16 = 0x02;56
pub const FW_CFG_RAM_SIZE: u16 = 0x03;57
pub const FW_CFG_NOGRAPHIC: u16 = 0x04;58
pub const FW_CFG_NB_CPUS: u16 = 0x05;59
pub const FW_CFG_MACHINE_ID: u16 = 0x06;60
pub const FW_CFG_KERNEL_ADDR: u16 = 0x07;61
pub const FW_CFG_KERNEL_SIZE: u16 = 0x08;62
pub const FW_CFG_KERNEL_CMDLINE: u16 = 0x09;63
pub const FW_CFG_INITRD_ADDR: u16 = 0x0a;64
pub const FW_CFG_INITRD_SIZE: u16 = 0x0b;65
pub const FW_CFG_BOOT_DEVICE: u16 = 0x0c;66
pub const FW_CFG_NUMA: u16 = 0x0d;67
pub const FW_CFG_BOOT_MENU: u16 = 0x0e;68
pub const FW_CFG_MAX_CPUS: u16 = 0x0f;69
pub const FW_CFG_KERNEL_ENTRY: u16 = 0x10;70
pub const FW_CFG_KERNEL_DATA: u16 = 0x11;71
pub const FW_CFG_INITRD_DATA: u16 = 0x12;72
pub const FW_CFG_CMDLINE_ADDR: u16 = 0x13;73
pub const FW_CFG_CMDLINE_SIZE: u16 = 0x14;74
pub const FW_CFG_CMDLINE_DATA: u16 = 0x15;75
pub const FW_CFG_SETUP_ADDR: u16 = 0x16;76
pub const FW_CFG_SETUP_SIZE: u16 = 0x17;77
pub const FW_CFG_SETUP_DATA: u16 = 0x18;78
pub const FW_CFG_FILE_DIR: u16 = 0x19;79
pub const FW_CFG_KNOWN_ITEMS: usize = 0x20;80
81
pub const FW_CFG_FILE_FIRST: u16 = 0x20;82
pub const FW_CFG_DMA_SIGNATURE: [u8; 8] = *b"QEMU CFG";83
pub const FW_CFG_FEATURE: [u8; 4] = [0b11, 0, 0, 0];84
85
pub const FILE_NAME_SIZE: usize = 56;86
87
fn create_file_name(name: &str) -> [u8; FILE_NAME_SIZE] {88
let mut c_name = [0u8; FILE_NAME_SIZE];89
let c_len = std::cmp::min(FILE_NAME_SIZE - 1, name.len());90
c_name[0..c_len].copy_from_slice(&name.as_bytes()[0..c_len]);91
c_name92
}93
94
#[derive(Debug)]95
pub enum FwCfgContent {96
Bytes(Vec<u8>),97
Slice(&'static [u8]),98
File(u64, File),99
Lu16(Lu16),100
Lu32(Lu32),101
Lu64(Lu64),102
}103
104
struct FwCfgContentAccess<'a> {105
content: &'a FwCfgContent,106
offset: u32,107
}108
109
impl Read for FwCfgContentAccess<'_> {110
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {12x111
match self.content {12x112
FwCfgContent::File(offset, f) => {2x113
Seek::seek(&mut (&*f), SeekFrom::Start(offset + self.offset as u64))?;2x114
Read::read(&mut (&*f), buf)2x115
}116
FwCfgContent::Bytes(b) => match b.get(self.offset as usize..) {2x117
Some(mut s) => s.read(buf),1x118
None => Err(ErrorKind::UnexpectedEof)?,1x119
},120
FwCfgContent::Slice(b) => match b.get(self.offset as usize..) {2x121
Some(mut s) => s.read(buf),1x122
None => Err(ErrorKind::UnexpectedEof)?,1x123
},124
FwCfgContent::Lu16(n) => match n.as_bytes().get(self.offset as usize..) {2x125
Some(mut s) => s.read(buf),2x126
None => Err(ErrorKind::UnexpectedEof)?,127
},128
FwCfgContent::Lu32(n) => match n.as_bytes().get(self.offset as usize..) {2x129
Some(mut s) => s.read(buf),1x130
None => Err(ErrorKind::UnexpectedEof)?,1x131
},132
FwCfgContent::Lu64(n) => match n.as_bytes().get(self.offset as usize..) {2x133
Some(mut s) => s.read(buf),2x134
None => Err(ErrorKind::UnexpectedEof)?,135
},136
}137
}12x138
}139
140
impl Default for FwCfgContent {141
fn default() -> Self {3x142
FwCfgContent::Slice(&[])3x143
}3x144
}145
146
impl FwCfgContent {147
fn size(&self) -> Result<u32> {7x148
let ret = match self {7x149
FwCfgContent::Bytes(v) => v.len(),1x150
FwCfgContent::File(offset, f) => (f.metadata()?.len() - offset) as usize,1x151
FwCfgContent::Slice(s) => s.len(),2x152
FwCfgContent::Lu16(n) => size_of_val(n),1x153
FwCfgContent::Lu32(n) => size_of_val(n),1x154
FwCfgContent::Lu64(n) => size_of_val(n),1x155
};156
u32::try_from(ret).map_err(|_| std::io::ErrorKind::InvalidInput.into())7x157
}7x158
159
fn access(&self, offset: u32) -> FwCfgContentAccess<'_> {12x160
FwCfgContentAccess {12x161
content: self,12x162
offset,12x163
}12x164
}12x165
166
fn read(&self, offset: u32) -> Option<u8> {8x167
match self {8x168
FwCfgContent::Bytes(b) => b.get(offset as usize).copied(),1x169
FwCfgContent::Slice(s) => s.get(offset as usize).copied(),2x170
FwCfgContent::File(o, f) => {2x171
let mut buf = [0u8];2x172
match f.read_exact_at(&mut buf, o + offset as u64) {2x173
Ok(_) => Some(buf[0]),1x174
Err(e) => {1x175
log::error!("fw_cfg: reading {f:?}: {e:?}");1x176
None1x177
}178
}179
}180
FwCfgContent::Lu16(n) => n.as_bytes().get(offset as usize).copied(),1x181
FwCfgContent::Lu32(n) => n.as_bytes().get(offset as usize).copied(),1x182
FwCfgContent::Lu64(n) => n.as_bytes().get(offset as usize).copied(),1x183
}184
}8x185
}186
187
#[derive(Debug, Default)]188
pub struct FwCfgItem {189
pub name: String,190
pub content: FwCfgContent,191
}192
193
/// https://www.qemu.org/docs/master/specs/fw_cfg.html194
#[derive(Debug)]195
pub struct FwCfg {196
selector: u16,197
data_offset: u32,198
dma_address: u64,199
items: Vec<FwCfgItem>, // 0x20 and above200
known_items: [FwCfgContent; FW_CFG_KNOWN_ITEMS], // 0x0 to 0x19201
memory: Arc<RamBus>,202
}203
204
#[repr(C)]205
#[derive(Debug, IntoBytes, FromBytes, Immutable, Layout)]206
struct FwCfgDmaAccess {207
control: Bu32,208
length: Bu32,209
address: Bu64,210
}211
212
bitfield! {213
struct AccessControl(u32);214
impl Debug;215
error, set_error: 0;216
read, _: 1;217
skip, _: 2;218
select, _ : 3;219
write, _ :4;220
selector, _: 31, 16;221
}222
223
#[repr(C)]224
#[derive(Debug, IntoBytes, Immutable)]225
struct FwCfgFilesHeader {226
count: Bu32,227
}228
229
#[repr(C)]230
#[derive(Debug, IntoBytes, Immutable)]231
struct FwCfgFile {232
size: Bu32,233
select: Bu16,234
_reserved: u16,235
name: [u8; FILE_NAME_SIZE],236
}237
238
impl FwCfg {239
pub fn new(memory: Arc<RamBus>, items: Vec<FwCfgItem>) -> Result<Self> {240
const DEFAULT_ITEM: FwCfgContent = FwCfgContent::Slice(&[]);241
let mut known_items = [DEFAULT_ITEM; FW_CFG_KNOWN_ITEMS];242
known_items[FW_CFG_SIGNATURE as usize] = FwCfgContent::Slice(&FW_CFG_DMA_SIGNATURE);243
known_items[FW_CFG_ID as usize] = FwCfgContent::Slice(&FW_CFG_FEATURE);244
let file_buf = Vec::from(FwCfgFilesHeader { count: 0.into() }.as_bytes());245
known_items[FW_CFG_FILE_DIR as usize] = FwCfgContent::Bytes(file_buf);246
247
let mut dev = Self {248
selector: 0,249
data_offset: 0,250
dma_address: 0,251
memory,252
items: vec![],253
known_items,254
};255
for item in items {256
dev.add_item(item)?;257
}258
Ok(dev)259
}260
261
fn get_file_dir_mut(&mut self) -> &mut Vec<u8> {262
let FwCfgContent::Bytes(file_buf) = &mut self.known_items[FW_CFG_FILE_DIR as usize] else {263
unreachable!("fw_cfg: selector {FW_CFG_FILE_DIR:#x} should be FwCfgContent::Byte!")264
};265
file_buf266
}267
268
fn update_count(&mut self) {269
let header = FwCfgFilesHeader {270
count: (self.items.len() as u32).into(),271
};272
self.get_file_dir_mut()[0..4].copy_from_slice(header.as_bytes());273
}274
275
pub fn add_ram_size(&mut self, size: u64) {276
self.known_items[FW_CFG_RAM_SIZE as usize] = FwCfgContent::Lu64(size.into());277
}278
279
pub fn add_cpu_count(&mut self, count: u16) {280
self.known_items[FW_CFG_NB_CPUS as usize] = FwCfgContent::Lu16(count.into());281
}282
283
pub(crate) fn add_e820(&mut self, mem_regions: &[(u64, MemRegionEntry)]) -> Result<()> {284
let mut bytes = vec![];285
for (addr, region) in mem_regions.iter() {286
let type_ = match region.type_ {287
MemRegionType::Ram => E820_RAM,288
MemRegionType::Reserved => E820_RESERVED,289
MemRegionType::Acpi => E820_ACPI,290
MemRegionType::Pmem => E820_PMEM,291
MemRegionType::Hidden => continue,292
};293
let entry = BootE820Entry {294
addr: *addr,295
size: region.size,296
type_,297
};298
bytes.extend_from_slice(entry.as_bytes());299
}300
let item = FwCfgItem {301
name: "etc/e820".to_owned(),302
content: FwCfgContent::Bytes(bytes),303
};304
self.add_item(item)305
}306
307
pub(crate) fn add_acpi(&mut self, acpi_table: AcpiTable) -> Result<()> {308
let [table_loader, acpi_rsdp, apci_tables] = create_acpi_loader(acpi_table);309
self.add_item(table_loader)?;310
self.add_item(acpi_rsdp)?;311
self.add_item(apci_tables)312
}313
314
pub fn add_kernel_data(&mut self, p: &Path) -> Result<()> {315
let file = File::open(p)?;316
let mut buffer = vec![0u8; size_of::<BootParams>()];317
file.read_exact_at(&mut buffer, 0)?;318
let bp = BootParams::mut_from_bytes(&mut buffer).unwrap();319
if bp.hdr.setup_sects == 0 {320
bp.hdr.setup_sects = 4;321
}322
bp.hdr.type_of_loader = 0xff;323
let kernel_start = (bp.hdr.setup_sects as usize + 1) * 512;324
self.known_items[FW_CFG_SETUP_SIZE as usize] =325
FwCfgContent::Lu32((buffer.len() as u32).into());326
self.known_items[FW_CFG_SETUP_DATA as usize] = FwCfgContent::Bytes(buffer);327
self.known_items[FW_CFG_KERNEL_SIZE as usize] =328
FwCfgContent::Lu32((file.metadata()?.len() as u32 - kernel_start as u32).into());329
self.known_items[FW_CFG_KERNEL_DATA as usize] =330
FwCfgContent::File(kernel_start as u64, file);331
Ok(())332
}333
334
pub fn add_initramfs_data(&mut self, p: &Path) -> Result<()> {335
let file = File::open(p)?;336
let initramfs_size = file.metadata()?.len() as u32;337
self.known_items[FW_CFG_INITRD_SIZE as usize] = FwCfgContent::Lu32(initramfs_size.into());338
self.known_items[FW_CFG_INITRD_DATA as usize] = FwCfgContent::File(0, file);339
Ok(())340
}341
342
pub fn add_kernel_cmdline(&mut self, s: CString) {343
let bytes = s.into_bytes_with_nul();344
self.known_items[FW_CFG_CMDLINE_SIZE as usize] =345
FwCfgContent::Lu32((bytes.len() as u32).into());346
self.known_items[FW_CFG_CMDLINE_DATA as usize] = FwCfgContent::Bytes(bytes);347
}348
349
pub fn add_item(&mut self, item: FwCfgItem) -> Result<()> {350
let index = self.items.len();351
let c_name = create_file_name(&item.name);352
let size = item.content.size()?;353
let cfg_file = FwCfgFile {354
size: size.into(),355
select: (FW_CFG_FILE_FIRST + index as u16).into(),356
_reserved: 0,357
name: c_name,358
};359
self.get_file_dir_mut()360
.extend_from_slice(cfg_file.as_bytes());361
self.items.push(item);362
self.update_count();363
Ok(())364
}365
366
fn dma_read_content(367
&self,368
content: &FwCfgContent,369
offset: u32,370
len: u32,371
address: u64,372
) -> Result<u32> {373
let content_size = content.size()?.saturating_sub(offset);374
let op_size = std::cmp::min(content_size, len);375
let r = self376
.memory377
.write_range(address, op_size as u64, content.access(offset));378
match r {379
Err(e) => {380
log::error!("fw_cfg: dam read error: {e:x?}");381
Err(ErrorKind::InvalidInput.into())382
}383
Ok(()) => Ok(op_size),384
}385
}386
387
fn dma_read(&mut self, selector: u16, len: u32, address: u64) -> Result<()> {388
let op_size = if let Some(content) = self.known_items.get(selector as usize) {389
self.dma_read_content(content, self.data_offset, len, address)390
} else if let Some(item) = self.items.get((selector - FW_CFG_FILE_FIRST) as usize) {391
self.dma_read_content(&item.content, self.data_offset, len, address)392
} else {393
log::error!("fw_cfg: selector {selector:#x} does not exist.");394
Err(ErrorKind::NotFound.into())395
}?;396
self.data_offset += op_size;397
Ok(())398
}399
400
fn dma_write(&self, _selector: u16, _len: u32, _address: u64) -> Result<()> {401
unimplemented!()402
}403
404
fn do_dma(&mut self) {405
let dma_address = self.dma_address;406
let dma_access: FwCfgDmaAccess = match self.memory.read_t(dma_address) {407
Ok(access) => access,408
Err(e) => {409
log::error!("fw_cfg: invalid address of dma access {dma_address:#x}: {e:?}");410
return;411
}412
};413
let control = AccessControl(dma_access.control.into());414
if control.select() {415
self.selector = control.select() as u16;416
}417
let len = dma_access.length.to_ne();418
let addr = dma_access.address.to_ne();419
let ret = if control.read() {420
self.dma_read(self.selector, len, addr)421
} else if control.write() {422
self.dma_write(self.selector, len, addr)423
} else if control.skip() {424
self.data_offset += len;425
Ok(())426
} else {427
Err(ErrorKind::InvalidData.into())428
};429
let mut access_resp = AccessControl(0);430
if let Err(e) = ret {431
log::error!("fw_cfg: dma operation {dma_access:x?}: {e:x?}");432
access_resp.set_error(true);433
}434
if let Err(e) = self.memory.write_t(435
dma_address + FwCfgDmaAccess::OFFSET_CONTROL as u64,436
&Bu32::from(access_resp.0),437
) {438
log::error!("fw_cfg: finishing dma: {e:?}")439
}440
}441
442
fn read_data(&mut self) -> u8 {443
let ret = if let Some(content) = self.known_items.get(self.selector as usize) {444
content.read(self.data_offset)445
} else if let Some(item) = self.items.get((self.selector - FW_CFG_FILE_FIRST) as usize) {446
item.content.read(self.data_offset)447
} else {448
log::error!("fw_cfg: selector {:#x} does not exist.", self.selector);449
None450
};451
if let Some(val) = ret {452
self.data_offset += 1;453
val454
} else {455
0456
}457
}458
459
fn write_data(&self, _val: u8) {460
if self.selector & SELECTOR_WR != SELECTOR_WR {461
log::error!("fw_cfg: data is read only");462
return;463
}464
log::warn!("fw_cfg: write data no op.")465
}466
}467
468
impl Mmio for Mutex<FwCfg> {469
fn size(&self) -> u64 {470
16471
}472
473
fn read(&self, offset: u64, size: u8) -> mem::Result<u64> {474
let mut fw_cfg = self.lock();475
let port = offset as u16 + PORT_FW_CFG_SELECTOR;476
let ret = match (port, size) {477
(PORT_FW_CFG_SELECTOR, _) => {478
log::error!("fw_cfg: selector registerīis write-only.");479
0480
}481
(PORT_FW_CFG_DATA, 1) => fw_cfg.read_data() as u64,482
(PORT_FW_CFG_DMA_HI, 4) => {483
let addr = fw_cfg.dma_address;484
let addr_hi = (addr >> 32) as u32;485
addr_hi.to_be() as u64486
}487
(PORT_FW_CFG_DMA_LO, 4) => {488
let addr = fw_cfg.dma_address;489
let addr_lo = (addr & 0xffff_ffff) as u32;490
addr_lo.to_be() as u64491
}492
_ => {493
log::error!("fw_cfg: read unknown port {port:#x} with size {size}.");494
0495
}496
};497
Ok(ret)498
}499
500
fn write(&self, offset: u64, size: u8, val: u64) -> mem::Result<Action> {501
let mut fw_cfg = self.lock();502
let port = offset as u16 + PORT_FW_CFG_SELECTOR;503
match (port, size) {504
(PORT_FW_CFG_SELECTOR, 2) => {505
fw_cfg.selector = val as u16;506
fw_cfg.data_offset = 0;507
}508
(PORT_FW_CFG_DATA, 1) => fw_cfg.write_data(val as u8),509
(PORT_FW_CFG_DMA_HI, 4) => {510
fw_cfg.dma_address &= 0xffff_ffff;511
fw_cfg.dma_address |= (u32::from_be(val as u32) as u64) << 32;512
}513
(PORT_FW_CFG_DMA_LO, 4) => {514
fw_cfg.dma_address &= !0xffff_ffff;515
fw_cfg.dma_address |= u32::from_be(val as u32) as u64;516
fw_cfg.do_dma();517
}518
_ => log::error!(519
"fw_cfg: write 0x{val:0width$x} to unknown port {port:#x}.",520
width = 2 * size as usize,521
),522
};523
Ok(Action::None)524
}525
}526
527
impl Pause for Mutex<FwCfg> {528
fn pause(&self) -> device::Result<()> {529
Ok(())530
}531
532
fn resume(&self) -> device::Result<()> {533
Ok(())534
}535
}536
537
impl MmioDev for Mutex<FwCfg> {}538
539
#[derive(Debug, PartialEq, Eq, Deserialize, Help)]540
pub enum FwCfgContentParam {541
/// Path to a file with binary contents.542
#[serde(alias = "file")]543
File(Box<Path>),544
/// A UTF-8 encoded string.545
#[serde(alias = "string")]546
String(String),547
}548
549
#[derive(Debug, PartialEq, Eq, Help)]550
pub struct FwCfgItemParam {551
/// Selector key of an item.552
pub name: String,553
/// Item content.554
#[serde_aco(flatten)]555
pub content: FwCfgContentParam,556
}557
558
impl<'de> Deserialize<'de> for FwCfgItemParam {559
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>2x560
where2x561
D: Deserializer<'de>,2x562
{563
#[derive(Deserialize)]564
#[serde(field_identifier, rename_all = "lowercase")]565
enum Field {566
Name,567
File,568
String,569
}570
571
struct ParamVisitor;572
573
impl<'de> Visitor<'de> for ParamVisitor {574
type Value = FwCfgItemParam;575
576
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {577
formatter.write_str("struct FwCfgItemParam")578
}579
580
fn visit_map<V>(self, mut map: V) -> std::result::Result<Self::Value, V::Error>2x581
where2x582
V: MapAccess<'de>,2x583
{584
let mut name = None;2x585
let mut content = None;2x586
while let Some(key) = map.next_key()? {6x587
match key {4x588
Field::Name => {589
if name.is_some() {2x590
return Err(de::Error::duplicate_field("file"));591
}2x592
name = Some(map.next_value()?);2x593
}594
Field::String => {595
if content.is_some() {1x596
return Err(de::Error::duplicate_field("string,file"));597
}1x598
content = Some(FwCfgContentParam::String(map.next_value()?));1x599
}600
Field::File => {601
if content.is_some() {1x602
return Err(de::Error::duplicate_field("string,file"));603
}1x604
content = Some(FwCfgContentParam::File(map.next_value()?));1x605
}606
}607
}608
let name = name.ok_or_else(|| de::Error::missing_field("name"))?;2x609
let content = content.ok_or_else(|| de::Error::missing_field("file,string"))?;2x610
Ok(FwCfgItemParam { name, content })2x611
}2x612
}613
614
const FIELDS: &[&str] = &["name", "file", "string"];615
deserializer.deserialize_struct("FwCfgItemParam", FIELDS, ParamVisitor)2x616
}2x617
}618
619
impl FwCfgItemParam {620
pub fn build(self) -> Result<FwCfgItem> {621
match self.content {622
FwCfgContentParam::File(file) => {623
let f = File::open(file)?;624
Ok(FwCfgItem {625
name: self.name,626
content: FwCfgContent::File(0, f),627
})628
}629
FwCfgContentParam::String(string) => Ok(FwCfgItem {630
name: self.name,631
content: FwCfgContent::Bytes(string.into()),632
}),633
}634
}635
}636
637
#[cfg(test)]638
#[path = "fw_cfg_test.rs"]639
mod tests;640