use libacpica::*;
use core::fmt;
use core::mem;
use core::ptr;
use crate::alloc::alloc;
use crate::alloc::vec::Vec;
use core::alloc::Layout;
use crate::acpi_types::*;
use cstr_core::CStr;
use log::{debug, info, trace, warn};
use x86::current::paging::BASE_PAGE_SIZE;
const ACPI_FULL_PATHNAME: u32 = 0;
const ACPI_TYPE_INTEGER: u32 = 0x01;
#[allow(unused)]
fn acpi_get_integer(handle: ACPI_HANDLE, name: *const i8, reg: &mut ACPI_INTEGER) -> ACPI_STATUS {
unsafe {
let mut object: ACPI_OBJECT = mem::zeroed();
let mut namebuf: ACPI_BUFFER = ACPI_BUFFER {
Length: mem::size_of::<ACPI_OBJECT>() as u64,
Pointer: &mut object as *mut _ as *mut libacpica::c_void,
};
let ret = AcpiEvaluateObjectTyped(
handle,
name as *mut i8,
ptr::null_mut(),
&mut namebuf,
ACPI_TYPE_INTEGER,
);
if ret == AE_OK {
*reg = object.Integer.Value;
}
ret
}
}
#[allow(unused)]
pub fn process_pcie() {
unsafe {
let pcie_exp = CStr::from_bytes_with_nul_unchecked(b"PNP0A03\0");
unsafe extern "C" fn call_back(
handle: ACPI_HANDLE,
_nexting: u32,
_context: *mut libacpica::c_void,
_return_value: *mut *mut libacpica::c_void,
) -> u32 {
let mut namebuf: ACPI_BUFFER = ACPI_BUFFER {
Length: 256,
Pointer: alloc::alloc(Layout::from_size_align_unchecked(128, 0x1))
as *mut libacpica::c_void,
};
let _ret = AcpiGetName(handle, ACPI_FULL_PATHNAME, &mut namebuf);
let name = CStr::from_ptr(namebuf.Pointer as *const i8)
.to_str()
.unwrap_or("");
let mut address: ACPI_INTEGER = 0x0;
let adr_cstr = CStr::from_bytes_with_nul_unchecked(b"_ADR\0");
acpi_get_integer(handle, adr_cstr.as_ptr() as *const i8, &mut address);
let mut bus_number: ACPI_INTEGER = 0x0;
let adr_cstr = CStr::from_bytes_with_nul_unchecked(b"_BBN\0");
let bbn_ret = acpi_get_integer(handle, adr_cstr.as_ptr() as *const i8, &mut bus_number);
let bus = if bbn_ret == AE_OK {
bus_number as u16
} else {
0u16
};
let device: u16 = (address >> 16) as u16 & 0xffff;
let function: u16 = address as u16 & 0xffff;
info!(
"PCIe bridge name={} bus={} device={} function={}",
name, bus, device, function
);
AE_OK
}
let _ret = AcpiGetDevices(
pcie_exp.as_ptr() as *mut cstr_core::c_char,
Some(call_back),
ptr::null_mut(),
ptr::null_mut(),
);
}
}
pub fn process_srat() -> (
Vec<LocalApicAffinity>,
Vec<LocalX2ApicAffinity>,
Vec<MemoryAffinity>,
) {
let mut apic_affinity = Vec::with_capacity(24);
let mut x2apic_affinity = Vec::with_capacity(24);
let mut mem_affinity = Vec::with_capacity(8);
unsafe {
let mut table_header: *mut ACPI_TABLE_HEADER = ptr::null_mut();
let ret = AcpiGetTable(
ACPI_SIG_SRAT.as_ptr() as *mut cstr_core::c_char,
1,
&mut table_header,
);
if ret == AE_OK {
let srat_tbl_ptr = table_header as *const ACPI_TABLE_SRAT;
let srat_table_len = (*srat_tbl_ptr).Header.Length as usize;
let srat_table_end = (srat_tbl_ptr as *const c_void).add(srat_table_len);
debug!(
"SRAT Table: Rev={} Len={} OemID={:?}",
(*srat_tbl_ptr).Header.Revision,
srat_table_len,
(*srat_tbl_ptr).Header.OemId
);
let mut iterator =
(srat_tbl_ptr as *const c_void).add(mem::size_of::<ACPI_TABLE_SRAT>());
while iterator < srat_table_end {
let entry: *const ACPI_SUBTABLE_HEADER = iterator as *const ACPI_SUBTABLE_HEADER;
let entry_type: AcpiSratType = mem::transmute((*entry).Type as i32);
match entry_type {
AcpiSratType::ACPI_SRAT_TYPE_CPU_AFFINITY => {
const ACPI_SRAT_ENABLED: u32 = 0x1;
let local_apic_affinity: *const ACPI_SRAT_CPU_AFFINITY =
entry as *const ACPI_SRAT_CPU_AFFINITY;
let apic_id = (*local_apic_affinity).ApicId;
let sapic_eid = (*local_apic_affinity).LocalSapicEid;
let proximity_domain: u32 = (*local_apic_affinity).ProximityDomainLo as u32
| (((*local_apic_affinity).ProximityDomainHi[0] as u32) << 8)
| (((*local_apic_affinity).ProximityDomainHi[1] as u32) << 16)
| (((*local_apic_affinity).ProximityDomainHi[2] as u32) << 24);
let clock_domain = (*local_apic_affinity).ClockDomain;
let enabled = (*local_apic_affinity).Flags & ACPI_SRAT_ENABLED > 0;
let parsed_entry = LocalApicAffinity {
apic_id,
sapic_eid,
proximity_domain,
clock_domain,
enabled,
};
trace!("SRAT entry: {:?}", parsed_entry);
if enabled {
apic_affinity.push(parsed_entry);
}
debug_assert_eq!((*entry).Length, 16);
}
AcpiSratType::ACPI_SRAT_TYPE_MEMORY_AFFINITY => {
const ACPI_SRAT_ENABLED: u32 = 0x1;
const ACPI_SRAT_HOTPLUGGABLE: u32 = 0x1 << 1;
const ACPI_SRAT_NON_VOLATILE: u32 = 0x1 << 2;
let mem_affinity_entry: *const ACPI_SRAT_MEM_AFFINITY =
entry as *const ACPI_SRAT_MEM_AFFINITY;
let proximity_domain = (*mem_affinity_entry).ProximityDomain;
let base_address = (*mem_affinity_entry).BaseAddress;
let length = (*mem_affinity_entry).Length;
let enabled = (*mem_affinity_entry).Flags & ACPI_SRAT_ENABLED > 0;
let hotplug_capable =
(*mem_affinity_entry).Flags & ACPI_SRAT_HOTPLUGGABLE > 0;
let non_volatile = (*mem_affinity_entry).Flags & ACPI_SRAT_NON_VOLATILE > 0;
let parsed_entry = MemoryAffinity {
proximity_domain,
base_address,
length,
enabled,
hotplug_capable,
non_volatile,
};
trace!("SRAT entry: {:?}", parsed_entry);
if enabled {
mem_affinity.push(parsed_entry);
}
debug_assert_eq!((*entry).Length, 40);
}
AcpiSratType::ACPI_SRAT_TYPE_X2APIC_CPU_AFFINITY => {
const ACPI_SRAT_ENABLED: u32 = 0x1;
let x2apic_affinity_entry: *const ACPI_SRAT_X2APIC_CPU_AFFINITY =
entry as *const ACPI_SRAT_X2APIC_CPU_AFFINITY;
let x2apic_id: u32 = (*x2apic_affinity_entry).ApicId;
let proximity_domain: u32 = (*x2apic_affinity_entry).ProximityDomain;
let clock_domain: u32 = (*x2apic_affinity_entry).ClockDomain;
let enabled: bool = (*x2apic_affinity_entry).Flags & ACPI_SRAT_ENABLED > 0;
let parsed_entry = LocalX2ApicAffinity {
x2apic_id,
proximity_domain,
clock_domain,
enabled,
};
trace!("SRAT entry: {:?}", parsed_entry);
if enabled {
x2apic_affinity.push(parsed_entry);
}
debug_assert_eq!((*entry).Length, 24);
}
_ => trace!("Unhandled SRAT entry"),
}
assert!((*entry).Length > 0);
iterator = iterator.add((*entry).Length as usize);
}
} else {
debug!("ACPI SRAT Table not found.");
}
}
(apic_affinity, x2apic_affinity, mem_affinity)
}
pub fn process_madt() -> (Vec<LocalApic>, Vec<LocalX2Apic>, Vec<IoApic>) {
let mut cores = Vec::with_capacity(24);
let mut x2apic_cores = Vec::with_capacity(24);
let mut io_apics = Vec::with_capacity(24);
unsafe {
let mut table_header: *mut ACPI_TABLE_HEADER = ptr::null_mut();
let ret = AcpiGetTable(
ACPI_SIG_MADT.as_ptr() as *mut cstr_core::c_char,
1,
&mut table_header,
);
assert_eq!(ret, AE_OK);
let madt_tbl_ptr = table_header as *const ACPI_TABLE_MADT;
let madt_table_len = (*madt_tbl_ptr).Header.Length as usize;
let madt_table_end = (madt_tbl_ptr as *const c_void).add(madt_table_len);
trace!(
"MADT Table: Rev={} Len={} OemID={:?}",
(*madt_tbl_ptr).Header.Revision,
madt_table_len,
(*madt_tbl_ptr).Header.OemId
);
let mut iterator = (madt_tbl_ptr as *const c_void).add(mem::size_of::<ACPI_TABLE_MADT>());
while iterator < madt_table_end {
let entry: *const ACPI_SUBTABLE_HEADER = iterator as *const ACPI_SUBTABLE_HEADER;
let entry_type: AcpiMadtType = mem::transmute((*entry).Type as i32);
const ACPI_MADT_ENABLED: u32 = 0x1;
match entry_type {
AcpiMadtType::ACPI_MADT_TYPE_LOCAL_APIC => {
let local_apic: *const ACPI_MADT_LOCAL_APIC =
entry as *const ACPI_MADT_LOCAL_APIC;
let processor_id = (*local_apic).ProcessorId;
let apic_id = (*local_apic).Id;
let enabled: bool = (*local_apic).LapicFlags & ACPI_MADT_ENABLED > 0;
if enabled {
let core = LocalApic {
processor_id,
apic_id,
enabled,
};
trace!("MADT Entry: {:?}", core);
cores.push(core);
}
}
AcpiMadtType::ACPI_MADT_TYPE_LOCAL_X2APIC => {
let local_x2apic: *const ACPI_MADT_LOCAL_X2APIC =
entry as *const ACPI_MADT_LOCAL_X2APIC;
let processor_id = (*local_x2apic).Uid;
let apic_id = (*local_x2apic).LocalApicId;
let enabled: bool = (*local_x2apic).LapicFlags & ACPI_MADT_ENABLED > 0;
if enabled {
let core = LocalX2Apic {
processor_id,
apic_id,
enabled,
};
trace!("MADT Entry: {:?}", core);
x2apic_cores.push(core);
}
}
AcpiMadtType::ACPI_MADT_TYPE_IO_APIC => {
let io_apic: *const ACPI_MADT_IO_APIC = entry as *const ACPI_MADT_IO_APIC;
let apic = IoApic {
id: (*io_apic).Id,
address: (*io_apic).Address as u32,
global_irq_base: (*io_apic).GlobalIrqBase as u32,
};
trace!("MADT Entry: {:?}", apic);
io_apics.push(apic);
}
_ => trace!("Unhandled MADT entry"),
}
assert!((*entry).Length > 0, "Length is 0?");
iterator = iterator.add((*entry).Length as usize);
}
}
(cores, x2apic_cores, io_apics)
}
pub fn process_msct() -> (
MaximumSystemCharacteristics,
Vec<MaximumProximityDomainInfo>,
) {
unsafe {
let mut table_header: *mut ACPI_TABLE_HEADER = ptr::null_mut();
let ret = AcpiGetTable(
ACPI_SIG_MSCT.as_ptr() as *mut cstr_core::c_char,
1,
&mut table_header,
);
if ret != AE_OK {
return (Default::default(), Vec::new());
}
let msct_tbl_ptr = table_header as *const ACPI_TABLE_MSCT;
let msct_table_len = (*msct_tbl_ptr).Header.Length as usize;
let msct_table_end = (msct_tbl_ptr as *const c_void).add(msct_table_len);
let msc = MaximumSystemCharacteristics {
proximity_offset: (*msct_tbl_ptr).ProximityOffset,
max_proximity_domain: (*msct_tbl_ptr).MaxProximityDomains,
max_clock_domains: (*msct_tbl_ptr).MaxClockDomains,
max_address: (*msct_tbl_ptr).MaxAddress,
};
debug!(
"MSCT Table: Rev={} Len={} OemID={:?} Characteristics {:?}",
(*msct_tbl_ptr).Header.Revision,
msct_table_len,
(*msct_tbl_ptr).Header.OemId,
msc
);
let mut max_prox_domains = Vec::with_capacity(24);
let mut iterator = (msct_tbl_ptr as *const c_void).add(mem::size_of::<ACPI_TABLE_MSCT>());
while iterator < msct_table_end {
let entry: *const ACPI_MSCT_PROXIMITY = iterator as *const ACPI_MSCT_PROXIMITY;
let mpdi = MaximumProximityDomainInfo {
range_start: (*entry).RangeEnd,
range_end: (*entry).RangeStart,
processor_capacity: (*entry).ProcessorCapacity,
memory_capacity: (*entry).MemoryCapacity,
};
trace!("MSCT entry: {:?}", mpdi);
max_prox_domains.push(mpdi);
assert_eq!((*entry).Length, 22);
iterator = iterator.add((*entry).Length as usize);
}
(msc, max_prox_domains)
}
}
macro_rules! read {
($address: expr) => {
ptr::addr_of!($address).read_unaligned()
};
}
pub fn process_nfit() -> Vec<MemoryDescriptor> {
let mut pmem_descriptors = Vec::with_capacity(8);
unsafe {
let mut table_header: *mut ACPI_TABLE_HEADER = ptr::null_mut();
let ret = AcpiGetTable(
ACPI_SIG_NFIT.as_ptr() as *mut cstr_core::c_char,
1,
&mut table_header,
);
if ret == AE_OK {
let nfit_tbl_ptr = table_header as *const ACPI_TABLE_NFIT;
let nfit_table_len = (*nfit_tbl_ptr).Header.Length as usize;
let nfit_table_end = (nfit_tbl_ptr as *const c_void).add(nfit_table_len);
debug!("Discoverd NVDIMM(s), table length {:?}", nfit_table_len);
let mut iterator =
(nfit_tbl_ptr as *const c_void).add(mem::size_of::<ACPI_TABLE_NFIT>());
while iterator < nfit_table_end {
let header = iterator as *const ACPI_NFIT_HEADER;
let entry_type: AcpiNfitType = mem::transmute((*header).Type as i32);
match entry_type {
AcpiNfitType::ACPI_NFIT_TYPE_SYSTEM_ADDRESS => {
let entry = iterator as *const ACPI_NFIT_SYSTEM_ADDRESS;
let mem_desc = parse_nfit_spa_range_structure(entry);
pmem_descriptors.push(mem_desc);
}
AcpiNfitType::ACPI_NFIT_TYPE_MEMORY_MAP => {
let entry = iterator as *const ACPI_NFIT_MEMORY_MAP;
log_nfit_region_mapping_structure(entry);
}
AcpiNfitType::ACPI_NFIT_TYPE_INTERLEAVE => {
let entry = iterator as *const ACPI_NFIT_INTERLEAVE;
log_nfit_interleave_structure(entry);
}
AcpiNfitType::ACPI_NFIT_TYPE_SMBIOS => {
warn!("Unable to handle ACPI_NFIT_SMBIOS table")
}
AcpiNfitType::ACPI_NFIT_TYPE_CONTROL_REGION => {
let entry = iterator as *const ACPI_NFIT_CONTROL_REGION;
log_nfit_control_region_structure(entry);
}
AcpiNfitType::ACPI_NFIT_TYPE_DATA_REGION => {
warn!("Unable to handle ACPI_NFIT_DATA_REGION table")
}
AcpiNfitType::ACPI_NFIT_TYPE_FLUSH_ADDRESS => {
let entry = iterator as *const ACPI_NFIT_FLUSH_ADDRESS;
log_nfit_flush_hint_structure(entry);
}
AcpiNfitType::ACPI_NFIT_TYPE_CAPABILITIES => {
let entry = iterator as *const ACPI_NFIT_CAPABILITIES;
log_nfit_platform_capabilities_structure(entry);
}
_ => unreachable!(),
}
iterator = iterator.add((*header).Length as usize);
}
} else {
debug!("ACPI NFIT Table not found.");
}
pmem_descriptors
}
}
#[repr(C, packed)]
struct Guid {
data1: u32,
data2: u16,
data3: u16,
index8: u8,
index9: u8,
index10: u8,
index11: u8,
index12: u8,
index13: u8,
index14: u8,
index15: u8,
}
impl fmt::Debug for Guid {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{{ {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X}, {:#X} }}",
unsafe { read!(self.data1) },
unsafe { read!(self.data2) },
unsafe { read!(self.data3) },
self.index8,
self.index9,
self.index10,
self.index11,
self.index12,
self.index13,
self.index14,
self.index15
)
}
}
impl From<&[u8; 16]> for Guid {
fn from(slice: &[u8; 16]) -> Guid {
static_assertions::assert_eq_size!([u8; 16], Guid);
static_assertions::assert_eq_align!([u8; 16], Guid);
let p: *const [u8; core::mem::size_of::<Guid>()] =
slice.as_ptr() as *const [u8; core::mem::size_of::<Guid>()];
unsafe { core::mem::transmute(*p) }
}
}
fn parse_nfit_spa_range_structure(entry: *const ACPI_NFIT_SYSTEM_ADDRESS) -> MemoryDescriptor {
unsafe {
assert_eq!(read!((*entry).Header.Type), 0);
debug!(
"ACPI_NFIT_SYSTEM_ADDRESS - Type {}, Length {},
RangeIndex: {},
Flags: {},
ProximityDomain: {},
RangeGuid: {:x?},
Address: 0x{:x},
Length: {} Bytes,
MemoryMapping: {:?}",
read!((*entry).Header.Type),
read!((*entry).Header.Length),
read!((*entry).RangeIndex),
read!((*entry).Flags),
read!((*entry).ProximityDomain),
Guid::from(&(*entry).RangeGuid),
read!((*entry).Address),
read!((*entry).Length),
MemoryAttribute::from((*entry).MemoryMapping)
);
assert_eq!(
(*entry).Length % BASE_PAGE_SIZE as u64,
0,
"Not multiple of page-size."
);
MemoryDescriptor {
ty: MemoryType::PERSISTENT_MEMORY,
padding: 0,
phys_start: (*entry).Address,
virt_start: 0,
page_count: (*entry).Length / BASE_PAGE_SIZE as u64,
att: MemoryAttribute::from((*entry).MemoryMapping),
}
}
}
fn log_nfit_region_mapping_structure(entry: *const ACPI_NFIT_MEMORY_MAP) {
unsafe {
assert_eq!(read!((*entry).Header.Type), 1);
debug!(
"ACPI_NFIT_TYPE_MEMORY_MAP - Type {}, Length {}
NfitDeviceHandle: 0x{:x},
NfitDeviceHandle.DimmNumber: 0x{:x},
NfitDeviceHandle.MemChannel: 0x{:x},
NfitDeviceHandle.MemControllerId: 0x{:x},
NfitDeviceHandle.SocketId: 0x{:x},
NfitDeviceHandle.NodeControllerId: 0x{:x},
PhysicalId: {},
RegionId: {},
RangeIndex: {},
RegionIndex: {},
RegionSize: {},
RegionOffset: {},
Address: {},
InterleaveIndex: {},
InterleaveWays: {},
Flags: {}",
read!((*entry).Header.Type),
read!((*entry).Header.Length),
read!((*entry).DeviceHandle),
(*entry).DeviceHandle & ACPI_NFIT_DIMM_NUMBER_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_CHANNEL_NUMBER_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_MEMORY_ID_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_SOCKET_ID_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_NODE_ID_OFFSET,
read!((*entry).PhysicalId),
read!((*entry).RegionId),
read!((*entry).RangeIndex),
read!((*entry).RegionIndex),
read!((*entry).RegionSize),
read!((*entry).RegionOffset),
read!((*entry).Address),
read!((*entry).InterleaveIndex),
read!((*entry).InterleaveWays),
read!((*entry).Flags),
);
}
}
fn log_nfit_interleave_structure(entry: *const ACPI_NFIT_INTERLEAVE) {
unsafe {
assert_eq!(read!((*entry).Header.Type), 2);
debug!(
"ACPI_NFIT_TYPE_INTERLEAVE - Type {}, Length {}
InterleaveStructureIndex: {:#x}
NumberOfLinesDescribed: {:#x}
LineOffset: {:#x}
LineSize: {:#x}",
read!((*entry).Header.Type),
read!((*entry).Header.Length),
read!((*entry).InterleaveIndex),
read!((*entry).LineCount),
read!((*entry).LineOffset[0]),
read!((*entry).LineSize)
);
}
}
fn log_nfit_control_region_structure(entry: *const ACPI_NFIT_CONTROL_REGION) {
unsafe {
assert_eq!(read!((*entry).Header.Type), 4);
debug!(
"ACPI_NFIT_TYPE_CONTROL_REGION - Type {}, Length {}
RegionIndex: {},
VendorId: {},
DeviceId: {},
RevisionId: {},
SubsystemVendorId: {},
SubsystemDeviceId: {},
SubsystemRevisionId: {},
Reserved: {:?},
SerialNumber: {},
Code: {},
Windows: {},
WindowSize: {},
CommandOffset: {},
CommandSize: {},
StatusOffset: {},
StatusSize: {},
Flags: {}",
read!((*entry).Header.Type),
read!((*entry).Header.Length),
read!((*entry).RegionIndex),
read!((*entry).VendorId),
read!((*entry).DeviceId),
read!((*entry).RevisionId),
read!((*entry).SubsystemVendorId),
read!((*entry).SubsystemDeviceId),
read!((*entry).SubsystemRevisionId),
(*entry).Reserved,
read!((*entry).SerialNumber),
read!((*entry).Code),
read!((*entry).Windows),
read!((*entry).WindowSize),
read!((*entry).CommandOffset),
read!((*entry).CommandSize),
read!((*entry).StatusOffset),
read!((*entry).StatusSize),
read!((*entry).Flags),
);
}
}
fn log_nfit_flush_hint_structure(entry: *const ACPI_NFIT_FLUSH_ADDRESS) {
unsafe {
assert_eq!(read!((*entry).Header.Type), 6);
debug!(
"ACPI_NFIT_TYPE_FLUSH_ADDRESS - Type {}, Length {}
NfitDeviceHandle: {:#x}
NfitDeviceHandle.DimmNumber: {:#x}
NfitDeviceHandle.MemChannel: {:#x}
NfitDeviceHandle.MemControllerId: {:#x}
NfitDeviceHandle.SocketId: {:#x}
NfitDeviceHandle.NodeControllerId: {:#x}
NumberOfFlushHintAddresses: {:#x}
FlushHintAddress 0: {:#x}",
read!((*entry).Header.Type),
read!((*entry).Header.Length),
read!((*entry).DeviceHandle),
(*entry).DeviceHandle & ACPI_NFIT_DIMM_NUMBER_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_CHANNEL_NUMBER_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_MEMORY_ID_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_SOCKET_ID_OFFSET,
(*entry).DeviceHandle & ACPI_NFIT_NODE_ID_OFFSET,
read!((*entry).HintCount),
read!((*entry).HintAddress[0]),
);
}
}
fn log_nfit_platform_capabilities_structure(entry: *const ACPI_NFIT_CAPABILITIES) {
unsafe {
assert_eq!(read!((*entry).Header.Type), 7);
debug!(
"Capability {{ eADR: {}, ADR: {}, Mirroring: {} }}",
(*entry).Capabilities & ACPI_NFIT_CAPABILITY_CACHE_FLUSH > 0,
(*entry).Capabilities & ACPI_NFIT_CAPABILITY_MEM_FLUSH > 0,
(*entry).Capabilities & ACPI_NFIT_CAPABILITY_MEM_MIRRORING > 0
);
}
}