mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-11-04 19:11:11 +00:00
vmm: Delegate device related ACPI DSDT table work to DeviceManager
Move the code for handling the creation of the DSDT entries for devices into the DeviceManager. This will make it easier to handle device hotplug and also in the future remove some hardcoded ACPI constants. Signed-off-by: Rob Bradford <robert.bradford@intel.com>
This commit is contained in:
parent
60e6609011
commit
9b1ba14f2d
244
vmm/src/acpi.rs
244
vmm/src/acpi.rs
@ -3,7 +3,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
use acpi_tables::{
|
||||
aml,
|
||||
aml::Aml,
|
||||
rsdp::RSDP,
|
||||
sdt::{GenericAddress, SDT},
|
||||
@ -16,6 +15,7 @@ use std::convert::TryInto;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::cpu::CpuManager;
|
||||
use crate::device_manager::DeviceManager;
|
||||
use arch::layout;
|
||||
|
||||
#[repr(packed)]
|
||||
@ -79,127 +79,14 @@ struct IortIdMapping {
|
||||
pub flags: u32,
|
||||
}
|
||||
|
||||
fn create_ged_device() -> Vec<u8> {
|
||||
aml::Device::new(
|
||||
"_SB_.GED_".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
||||
true, true, false, false, 5,
|
||||
)]),
|
||||
),
|
||||
&aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1),
|
||||
&aml::Field::new(
|
||||
"GDST".into(),
|
||||
aml::FieldAccessType::Byte,
|
||||
aml::FieldUpdateRule::WriteAsZeroes,
|
||||
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
||||
),
|
||||
&aml::Method::new(
|
||||
"_EVT".into(),
|
||||
1,
|
||||
true,
|
||||
vec![&aml::If::new(
|
||||
&aml::Equal::new(&aml::Path::new("GDAT"), &aml::ONE),
|
||||
vec![&aml::MethodCall::new("\\_SB_.CPUS.CTFY".into(), vec![])],
|
||||
)],
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes()
|
||||
}
|
||||
|
||||
pub fn create_dsdt_table(
|
||||
serial_enabled: bool,
|
||||
start_of_device_area: GuestAddress,
|
||||
end_of_device_area: GuestAddress,
|
||||
device_manager: &DeviceManager,
|
||||
cpu_manager: &Arc<Mutex<CpuManager>>,
|
||||
) -> SDT {
|
||||
let pci_dsdt_data = aml::Device::new(
|
||||
"_SB_.PCI0".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08")),
|
||||
&aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0A03")),
|
||||
&aml::Name::new("_ADR".into(), &aml::ZERO),
|
||||
&aml::Name::new("_SEG".into(), &aml::ZERO),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new("SUPP".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![
|
||||
&aml::AddressSpace::new_bus_number(0x0u16, 0xffu16),
|
||||
&aml::IO::new(0xcf8, 0xcf8, 1, 0x8),
|
||||
&aml::AddressSpace::new_io(0x0u16, 0xcf7u16),
|
||||
&aml::AddressSpace::new_io(0xd00u16, 0xffffu16),
|
||||
&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
layout::MEM_32BIT_DEVICES_START.0 as u32,
|
||||
(layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1)
|
||||
as u32,
|
||||
),
|
||||
&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
start_of_device_area.0,
|
||||
end_of_device_area.0,
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let mbrd_dsdt_data = aml::Device::new(
|
||||
"_SB_.MBRD".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0C02")),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::Memory32Fixed::new(
|
||||
true,
|
||||
layout::PCI_MMCONFIG_START.0 as u32,
|
||||
layout::PCI_MMCONFIG_SIZE as u32,
|
||||
)]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let com1_dsdt_data = aml::Device::new(
|
||||
"_SB_.COM1".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0501")),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![
|
||||
&aml::Interrupt::new(true, true, false, false, 4),
|
||||
&aml::IO::new(0x3f8, 0x3f8, 0, 0x8),
|
||||
]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let s5_sleep_data =
|
||||
aml::Name::new("_S5_".into(), &aml::Package::new(vec![&5u8])).to_aml_bytes();
|
||||
|
||||
let ged_data = create_ged_device();
|
||||
|
||||
// DSDT
|
||||
let mut dsdt = SDT::new(*b"DSDT", 36, 6, *b"CLOUDH", *b"CHDSDT ", 1);
|
||||
dsdt.append_slice(pci_dsdt_data.as_slice());
|
||||
dsdt.append_slice(mbrd_dsdt_data.as_slice());
|
||||
if serial_enabled {
|
||||
dsdt.append_slice(com1_dsdt_data.as_slice());
|
||||
}
|
||||
dsdt.append_slice(s5_sleep_data.as_slice());
|
||||
dsdt.append_slice(ged_data.as_slice());
|
||||
|
||||
dsdt.append_slice(device_manager.to_aml_bytes().as_slice());
|
||||
dsdt.append_slice(cpu_manager.lock().unwrap().to_aml_bytes().as_slice());
|
||||
|
||||
dsdt
|
||||
@ -207,10 +94,7 @@ pub fn create_dsdt_table(
|
||||
|
||||
pub fn create_acpi_tables(
|
||||
guest_mem: &GuestMemoryMmap,
|
||||
serial_enabled: bool,
|
||||
start_of_device_area: GuestAddress,
|
||||
end_of_device_area: GuestAddress,
|
||||
virt_iommu: Option<(u32, &[u32])>,
|
||||
device_manager: &DeviceManager,
|
||||
cpu_manager: &Arc<Mutex<CpuManager>>,
|
||||
) -> GuestAddress {
|
||||
// RSDP is at the EBDA
|
||||
@ -218,12 +102,7 @@ pub fn create_acpi_tables(
|
||||
let mut tables: Vec<u64> = Vec::new();
|
||||
|
||||
// DSDT
|
||||
let dsdt = create_dsdt_table(
|
||||
serial_enabled,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
cpu_manager,
|
||||
);
|
||||
let dsdt = create_dsdt_table(device_manager, cpu_manager);
|
||||
let dsdt_offset = rsdp_offset.checked_add(RSDP::len() as u64).unwrap();
|
||||
guest_mem
|
||||
.write_slice(dsdt.as_slice(), dsdt_offset)
|
||||
@ -288,64 +167,65 @@ pub fn create_acpi_tables(
|
||||
.expect("Error writing MCFG table");
|
||||
tables.push(mcfg_offset.0);
|
||||
|
||||
let (prev_tbl_len, prev_tbl_off) = if let Some((iommu_id, dev_ids)) = &virt_iommu {
|
||||
// IORT
|
||||
let mut iort = SDT::new(*b"IORT", 36, 1, *b"CLOUDH", *b"CHIORT ", 1);
|
||||
// IORT number of nodes
|
||||
iort.append(2u32);
|
||||
// IORT offset to array of IORT nodes
|
||||
iort.append(48u32);
|
||||
// IORT reserved 4 bytes
|
||||
iort.append(0u32);
|
||||
// IORT paravirtualized IOMMU node
|
||||
iort.append(IortParavirtIommuNode {
|
||||
type_: 128,
|
||||
length: 56,
|
||||
revision: 0,
|
||||
num_id_mappings: 0,
|
||||
ref_id_mappings: 56,
|
||||
device_id: *iommu_id,
|
||||
model: 1,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let num_entries = dev_ids.len();
|
||||
let length: u16 = (36 + (20 * num_entries)).try_into().unwrap();
|
||||
|
||||
// IORT PCI root complex node
|
||||
iort.append(IortPciRootComplexNode {
|
||||
type_: 2,
|
||||
length,
|
||||
revision: 0,
|
||||
num_id_mappings: num_entries as u32,
|
||||
ref_id_mappings: 36,
|
||||
ats_attr: 0,
|
||||
pci_seg_num: 0,
|
||||
mem_addr_size_limit: 255,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for dev_id in dev_ids.iter() {
|
||||
// IORT ID mapping
|
||||
iort.append(IortIdMapping {
|
||||
input_base: *dev_id,
|
||||
num_of_ids: 1,
|
||||
ouput_base: *dev_id,
|
||||
output_ref: 48,
|
||||
flags: 0,
|
||||
let (prev_tbl_len, prev_tbl_off) =
|
||||
if let Some((iommu_id, dev_ids)) = &device_manager.virt_iommu() {
|
||||
// IORT
|
||||
let mut iort = SDT::new(*b"IORT", 36, 1, *b"CLOUDH", *b"CHIORT ", 1);
|
||||
// IORT number of nodes
|
||||
iort.append(2u32);
|
||||
// IORT offset to array of IORT nodes
|
||||
iort.append(48u32);
|
||||
// IORT reserved 4 bytes
|
||||
iort.append(0u32);
|
||||
// IORT paravirtualized IOMMU node
|
||||
iort.append(IortParavirtIommuNode {
|
||||
type_: 128,
|
||||
length: 56,
|
||||
revision: 0,
|
||||
num_id_mappings: 0,
|
||||
ref_id_mappings: 56,
|
||||
device_id: *iommu_id,
|
||||
model: 1,
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
let iort_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
|
||||
guest_mem
|
||||
.write_slice(iort.as_slice(), iort_offset)
|
||||
.expect("Error writing IORT table");
|
||||
tables.push(iort_offset.0);
|
||||
let num_entries = dev_ids.len();
|
||||
let length: u16 = (36 + (20 * num_entries)).try_into().unwrap();
|
||||
|
||||
(iort.len(), iort_offset)
|
||||
} else {
|
||||
(mcfg.len(), mcfg_offset)
|
||||
};
|
||||
// IORT PCI root complex node
|
||||
iort.append(IortPciRootComplexNode {
|
||||
type_: 2,
|
||||
length,
|
||||
revision: 0,
|
||||
num_id_mappings: num_entries as u32,
|
||||
ref_id_mappings: 36,
|
||||
ats_attr: 0,
|
||||
pci_seg_num: 0,
|
||||
mem_addr_size_limit: 255,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for dev_id in dev_ids.iter() {
|
||||
// IORT ID mapping
|
||||
iort.append(IortIdMapping {
|
||||
input_base: *dev_id,
|
||||
num_of_ids: 1,
|
||||
ouput_base: *dev_id,
|
||||
output_ref: 48,
|
||||
flags: 0,
|
||||
});
|
||||
}
|
||||
|
||||
let iort_offset = mcfg_offset.checked_add(mcfg.len() as u64).unwrap();
|
||||
guest_mem
|
||||
.write_slice(iort.as_slice(), iort_offset)
|
||||
.expect("Error writing IORT table");
|
||||
tables.push(iort_offset.0);
|
||||
|
||||
(iort.len(), iort_offset)
|
||||
} else {
|
||||
(mcfg.len(), mcfg_offset)
|
||||
};
|
||||
|
||||
// XSDT
|
||||
let mut xsdt = SDT::new(*b"XSDT", 36, 1, *b"CLOUDH", *b"CHXSDT ", 1);
|
||||
|
@ -9,9 +9,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
|
||||
//
|
||||
|
||||
use crate::config::ConsoleOutputMode;
|
||||
use crate::config::{ConsoleOutputMode, VmConfig};
|
||||
use crate::vm::VmInfo;
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
use acpi_tables::{aml, aml::Aml};
|
||||
use arch::layout;
|
||||
use devices::{ioapic, HotPlugNotificationType};
|
||||
use kvm_bindings::{kvm_irq_routing_entry, kvm_userspace_memory_region};
|
||||
use kvm_ioctls::*;
|
||||
@ -393,6 +395,13 @@ pub struct DeviceManager {
|
||||
// ACPI GED notification device
|
||||
#[cfg(feature = "acpi")]
|
||||
ged_notification_device: Option<Arc<Mutex<devices::AcpiGEDDevice>>>,
|
||||
|
||||
// Dimensions of the device area
|
||||
start_of_device_area: GuestAddress,
|
||||
end_of_device_area: GuestAddress,
|
||||
|
||||
// VM configuration
|
||||
config: Arc<Mutex<VmConfig>>,
|
||||
}
|
||||
|
||||
impl DeviceManager {
|
||||
@ -474,6 +483,10 @@ impl DeviceManager {
|
||||
)?;
|
||||
}
|
||||
|
||||
let start_of_device_area = vm_info.start_of_device_area;
|
||||
let end_of_device_area = vm_info.end_of_device_area;
|
||||
let config = vm_info.vm_cfg.clone();
|
||||
|
||||
Ok(DeviceManager {
|
||||
address_manager,
|
||||
console,
|
||||
@ -483,6 +496,9 @@ impl DeviceManager {
|
||||
virt_iommu,
|
||||
#[cfg(feature = "acpi")]
|
||||
ged_notification_device,
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
@ -1540,3 +1556,125 @@ impl Drop for DeviceManager {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
fn create_ged_device() -> Vec<u8> {
|
||||
aml::Device::new(
|
||||
"_SB_.GED_".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &"ACPI0013"),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::Interrupt::new(
|
||||
true, true, false, false, 5,
|
||||
)]),
|
||||
),
|
||||
&aml::OpRegion::new("GDST".into(), aml::OpRegionSpace::SystemIO, 0xb000, 0x1),
|
||||
&aml::Field::new(
|
||||
"GDST".into(),
|
||||
aml::FieldAccessType::Byte,
|
||||
aml::FieldUpdateRule::WriteAsZeroes,
|
||||
vec![aml::FieldEntry::Named(*b"GDAT", 8)],
|
||||
),
|
||||
&aml::Method::new(
|
||||
"_EVT".into(),
|
||||
1,
|
||||
true,
|
||||
vec![&aml::If::new(
|
||||
&aml::Equal::new(&aml::Path::new("GDAT"), &aml::ONE),
|
||||
vec![&aml::MethodCall::new("\\_SB_.CPUS.CTFY".into(), vec![])],
|
||||
)],
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes()
|
||||
}
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
impl Aml for DeviceManager {
|
||||
fn to_aml_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
let pci_dsdt_data = aml::Device::new(
|
||||
"_SB_.PCI0".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0A08")),
|
||||
&aml::Name::new("_CID".into(), &aml::EISAName::new("PNP0A03")),
|
||||
&aml::Name::new("_ADR".into(), &aml::ZERO),
|
||||
&aml::Name::new("_SEG".into(), &aml::ZERO),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new("SUPP".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![
|
||||
&aml::AddressSpace::new_bus_number(0x0u16, 0xffu16),
|
||||
&aml::IO::new(0xcf8, 0xcf8, 1, 0x8),
|
||||
&aml::AddressSpace::new_io(0x0u16, 0xcf7u16),
|
||||
&aml::AddressSpace::new_io(0xd00u16, 0xffffu16),
|
||||
&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
layout::MEM_32BIT_DEVICES_START.0 as u32,
|
||||
(layout::MEM_32BIT_DEVICES_START.0 + layout::MEM_32BIT_DEVICES_SIZE - 1)
|
||||
as u32,
|
||||
),
|
||||
&aml::AddressSpace::new_memory(
|
||||
aml::AddressSpaceCachable::NotCacheable,
|
||||
true,
|
||||
self.start_of_device_area.0,
|
||||
self.end_of_device_area.0,
|
||||
),
|
||||
]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let mbrd_dsdt_data = aml::Device::new(
|
||||
"_SB_.MBRD".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0C02")),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![&aml::Memory32Fixed::new(
|
||||
true,
|
||||
layout::PCI_MMCONFIG_START.0 as u32,
|
||||
layout::PCI_MMCONFIG_SIZE as u32,
|
||||
)]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let com1_dsdt_data = aml::Device::new(
|
||||
"_SB_.COM1".into(),
|
||||
vec![
|
||||
&aml::Name::new("_HID".into(), &aml::EISAName::new("PNP0501")),
|
||||
&aml::Name::new("_UID".into(), &aml::ZERO),
|
||||
&aml::Name::new(
|
||||
"_CRS".into(),
|
||||
&aml::ResourceTemplate::new(vec![
|
||||
&aml::Interrupt::new(true, true, false, false, 4),
|
||||
&aml::IO::new(0x3f8, 0x3f8, 0, 0x8),
|
||||
]),
|
||||
),
|
||||
],
|
||||
)
|
||||
.to_aml_bytes();
|
||||
|
||||
let s5_sleep_data =
|
||||
aml::Name::new("_S5_".into(), &aml::Package::new(vec![&5u8])).to_aml_bytes();
|
||||
|
||||
let ged_data = create_ged_device();
|
||||
|
||||
bytes.extend_from_slice(pci_dsdt_data.as_slice());
|
||||
bytes.extend_from_slice(mbrd_dsdt_data.as_slice());
|
||||
if self.config.lock().unwrap().serial.mode != ConsoleOutputMode::Off {
|
||||
bytes.extend_from_slice(com1_dsdt_data.as_slice());
|
||||
}
|
||||
bytes.extend_from_slice(s5_sleep_data.as_slice());
|
||||
bytes.extend_from_slice(ged_data.as_slice());
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
@ -152,6 +152,8 @@ pub struct VmInfo<'a> {
|
||||
pub memory: &'a Arc<RwLock<GuestMemoryMmap>>,
|
||||
pub vm_fd: &'a Arc<VmFd>,
|
||||
pub vm_cfg: Arc<Mutex<VmConfig>>,
|
||||
pub start_of_device_area: GuestAddress,
|
||||
pub end_of_device_area: GuestAddress,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
|
||||
@ -433,6 +435,14 @@ impl Vm {
|
||||
.ok_or(Error::MemoryRangeAllocation)?;
|
||||
}
|
||||
|
||||
let end_of_device_area = GuestAddress((1 << get_host_cpu_phys_bits()) - 1);
|
||||
let mem_end = guest_memory.end_addr();
|
||||
let start_of_device_area = if mem_end < arch::layout::MEM_32BIT_RESERVED_START {
|
||||
arch::layout::RAM_64BIT_START
|
||||
} else {
|
||||
mem_end.unchecked_add(1)
|
||||
};
|
||||
|
||||
// Convert the guest memory into an Arc. The point being able to use it
|
||||
// anywhere in the code, no matter which thread might use it.
|
||||
// Add the RwLock aspect to guest memory as we might want to perform
|
||||
@ -443,6 +453,8 @@ impl Vm {
|
||||
memory: &guest_memory,
|
||||
vm_fd: &fd,
|
||||
vm_cfg: config.clone(),
|
||||
start_of_device_area,
|
||||
end_of_device_area,
|
||||
};
|
||||
|
||||
let device_manager = DeviceManager::new(
|
||||
@ -526,26 +538,11 @@ impl Vm {
|
||||
|
||||
#[cfg(feature = "acpi")]
|
||||
{
|
||||
rsdp_addr = Some({
|
||||
let end_of_range = GuestAddress((1 << get_host_cpu_phys_bits()) - 1);
|
||||
|
||||
let mem_end = mem.end_addr();
|
||||
let start_of_device_area = if mem_end < arch::layout::MEM_32BIT_RESERVED_START {
|
||||
arch::layout::RAM_64BIT_START
|
||||
} else {
|
||||
mem_end.unchecked_add(1)
|
||||
};
|
||||
|
||||
use crate::config::ConsoleOutputMode;
|
||||
crate::acpi::create_acpi_tables(
|
||||
&mem,
|
||||
self.config.lock().unwrap().serial.mode != ConsoleOutputMode::Off,
|
||||
start_of_device_area,
|
||||
end_of_range,
|
||||
self.devices.virt_iommu(),
|
||||
&self.cpu_manager,
|
||||
)
|
||||
});
|
||||
rsdp_addr = Some(crate::acpi::create_acpi_tables(
|
||||
&mem,
|
||||
&self.devices,
|
||||
&self.cpu_manager,
|
||||
));
|
||||
}
|
||||
|
||||
match entry_addr.setup_header {
|
||||
|
Loading…
Reference in New Issue
Block a user