@@ -1057,6 +1057,158 @@ mod test {
1057
1057
pub mod formats {
1058
1058
use super :: Entry ;
1059
1059
use crate :: hw:: pci;
1060
+ use zerocopy:: { Immutable , IntoBytes } ;
1061
+
1062
+ /// A type for a range described in an E820 map entry.
1063
+ ///
1064
+ /// This is canonically defined as the ACPI "Address Range Types", though we
1065
+ /// only define the types we use, which are a subset of the types that EDK2
1066
+ /// is known to care about, which itself is a subset of types that ACPI and
1067
+ /// OEMs define or guest OSes may care about.
1068
+ #[ derive( IntoBytes , Immutable ) ]
1069
+ #[ repr( u32 ) ]
1070
+ enum EfiAcpiMemoryType {
1071
+ Memory = 1 ,
1072
+ Reserved = 2 ,
1073
+ // For reference, though these types are unused.
1074
+ // Acpi = 3,
1075
+ // Nvs = 4,
1076
+ }
1077
+
1078
+ /// One address/length/type entry in the E820 map.
1079
+ ///
1080
+ /// This is... almost defined by ACPI's "Address Range Descriptor Structure"
1081
+ /// table, under "INT 15H, E820". Critically, ACPI defines this structure
1082
+ /// with an additional "Extended Attributes" field which EDK2 does not know
1083
+ /// about and so we do not provide. Consequently the size of this struct is
1084
+ /// 20 bytes as defined in `OvmfPkg/Include/IndustryStandard/E820.h` rather
1085
+ /// than the ACPI definition's 24 bytes.
1086
+ #[ derive( IntoBytes , Immutable ) ]
1087
+ #[ repr( C , packed) ]
1088
+ struct E820Entry64 {
1089
+ base_addr : u64 ,
1090
+ length : u64 ,
1091
+ ty : EfiAcpiMemoryType ,
1092
+ }
1093
+
1094
+ /// A list of E820 memory map entries.
1095
+ ///
1096
+ /// This is not defined by ACPI, but is an EDK2 implementation of a QMEU
1097
+ /// construct to communicate an E820 map to the firmware. It is parsed by
1098
+ /// EDK2 and added to its EFI memory map; it is not, itself, the memory map
1099
+ /// that OVMF presents via UEFI services. It is not required to be sorted,
1100
+ /// and EDK2 ignores entries starting below 4 GiB. Adding additional
1101
+ /// low-memory entries is not harmful, but not valuable to EDK2 either.
1102
+ pub struct E820Table ( Vec < E820Entry64 > ) ;
1103
+ impl E820Table {
1104
+ pub fn new ( ) -> Self {
1105
+ Self ( Vec :: new ( ) )
1106
+ }
1107
+
1108
+ /// Add an address range corresponding to usable memory.
1109
+ pub fn add_mem ( & mut self , base_addr : u64 , length : u64 ) {
1110
+ self . 0 . push ( E820Entry64 {
1111
+ base_addr,
1112
+ length,
1113
+ ty : EfiAcpiMemoryType :: Memory ,
1114
+ } ) ;
1115
+ }
1116
+
1117
+ /// Add a reserved address, not to be used by the guest OS.
1118
+ pub fn add_reserved ( & mut self , base_addr : u64 , length : u64 ) {
1119
+ self . 0 . push ( E820Entry64 {
1120
+ base_addr,
1121
+ length,
1122
+ ty : EfiAcpiMemoryType :: Reserved ,
1123
+ } ) ;
1124
+ }
1125
+
1126
+ pub fn finish ( self ) -> Entry {
1127
+ Entry :: Bytes ( self . 0 . as_bytes ( ) . to_vec ( ) )
1128
+ }
1129
+ }
1130
+
1131
+ #[ cfg( test) ]
1132
+ mod test_e820 {
1133
+ use super :: { E820Entry64 , E820Table } ;
1134
+ use crate :: hw:: qemu:: fwcfg:: Entry ;
1135
+
1136
+ #[ test]
1137
+ fn entry_size_is_correct ( ) {
1138
+ // Compare the size of our definition of an E820 to EDK2's
1139
+ // definition. EDK2 interprets our provided bytes by its definition,
1140
+ // so they must match.
1141
+ assert_eq ! ( std:: mem:: size_of:: <E820Entry64 >( ) , 20 ) ;
1142
+ }
1143
+
1144
+ #[ test]
1145
+ fn basic ( ) {
1146
+ let mut e820_table = E820Table :: new ( ) ;
1147
+
1148
+ // Arbitrary bit patterns here, just to make eyeballing the layout
1149
+ // more straightforward.
1150
+ //
1151
+ // Also note the E820 table itself does not check if ranges overlap.
1152
+ // In practice it is directly constructed from an ASpace, which does
1153
+ // perform those checks.
1154
+ e820_table. add_mem ( 0x0102_0304_0506_0010 , 0x1122_3344_5566_7788 ) ;
1155
+ e820_table
1156
+ . add_reserved ( 0x0102_0304_0506_fff0 , 0xffee_ddcc_bbaa_9988 ) ;
1157
+
1158
+ // We also don't require the E820 map to be ordered. ACPI does not
1159
+ // imply that it should be, nor do EDK2 or guest OSes, even though
1160
+ // entries are often enumerated in address order.
1161
+ e820_table. add_mem ( 0x0102_0304_0506_0000 , 0x1122_3344_5566_7799 ) ;
1162
+
1163
+ // rustfmt::skip here and below because eight bytes per line helps
1164
+ // eyeball with the entries as written above. rustfmt would try to
1165
+ // fit ten bytes per row to pack the 80-column width and that's just
1166
+ // annoying here.
1167
+ #[ rustfmt:: skip]
1168
+ const FIRST_ENTRY : [ u8 ; 20 ] = [
1169
+ 0x10 , 0x00 , 0x06 , 0x05 , 0x04 , 0x03 , 0x02 , 0x01 ,
1170
+ 0x88 , 0x77 , 0x66 , 0x55 , 0x44 , 0x33 , 0x22 , 0x11 ,
1171
+ 0x01 , 0x00 , 0x00 , 0x00 ,
1172
+ ] ;
1173
+
1174
+ #[ rustfmt:: skip]
1175
+ const SECOND_ENTRY : [ u8 ; 20 ] = [
1176
+ 0xf0 , 0xff , 0x06 , 0x05 , 0x04 , 0x03 , 0x02 , 0x01 ,
1177
+ 0x88 , 0x99 , 0xaa , 0xbb , 0xcc , 0xdd , 0xee , 0xff ,
1178
+ 0x02 , 0x00 , 0x00 , 0x00 ,
1179
+ ] ;
1180
+
1181
+ #[ rustfmt:: skip]
1182
+ const THIRD_ENTRY : [ u8 ; 20 ] = [
1183
+ 0x00 , 0x00 , 0x06 , 0x05 , 0x04 , 0x03 , 0x02 , 0x01 ,
1184
+ 0x99 , 0x77 , 0x66 , 0x55 , 0x44 , 0x33 , 0x22 , 0x11 ,
1185
+ 0x01 , 0x00 , 0x00 , 0x00 ,
1186
+ ] ;
1187
+
1188
+ let entry = e820_table. finish ( ) ;
1189
+ let Entry :: Bytes ( bytes) = entry else {
1190
+ panic ! ( "entry did not produce bytes, but instead {:?}" , entry) ;
1191
+ } ;
1192
+
1193
+ let expected_size =
1194
+ FIRST_ENTRY . len ( ) + SECOND_ENTRY . len ( ) + THIRD_ENTRY . len ( ) ;
1195
+ assert_eq ! ( bytes. len( ) , expected_size) ;
1196
+
1197
+ let tests = [
1198
+ ( & bytes[ 0 ..20 ] , & FIRST_ENTRY , "First E820 entry" ) ,
1199
+ ( & bytes[ 20 ..40 ] , & SECOND_ENTRY , "Second E820 entry" ) ,
1200
+ ( & bytes[ 40 ..60 ] , & THIRD_ENTRY , "Third E820 entry" ) ,
1201
+ ] ;
1202
+
1203
+ for ( actual, expected, entry_name) in tests. iter ( ) {
1204
+ assert_eq ! (
1205
+ actual, expected,
1206
+ "{} contents are incorrect" ,
1207
+ entry_name
1208
+ ) ;
1209
+ }
1210
+ }
1211
+ }
1060
1212
1061
1213
/// Collect one or more device elections for use in generating a boot order
1062
1214
/// `fw_cfg` entry, suitable for consumption by OVMF bootrom.
@@ -1117,7 +1269,7 @@ pub mod formats {
1117
1269
}
1118
1270
1119
1271
#[ cfg( test) ]
1120
- mod test {
1272
+ mod test_bootorder {
1121
1273
use super :: BootOrder ;
1122
1274
use crate :: hw:: pci:: BusLocation ;
1123
1275
use crate :: hw:: qemu:: fwcfg;
0 commit comments