patina 21.1.1

Common types and functionality used in UEFI development.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
//! DXE Services
//!
//! Services used in the DXE boot phase:
//! - **Global Coherency Domain (GCD) Services** - The Global Coherency Domain (GCD) Services are used to manage the
//!   memory and I/O resources visible to the boot processor.
//! - **Dispatcher Services** - Used during preboot to schedule drivers for execution.
//!
//! See <https://uefi.org/specs/PI/1.8A/V2_Services_DXE_Services.html#services-dxe-services>.
//!
//! ## License
//!
//! Copyright (c) Microsoft Corporation.
//!
//! SPDX-License-Identifier: Apache-2.0
//!

use core::{
    cmp::{max, min},
    default::Default,
    ffi::c_void,
    ops::Range,
};

use r_efi::{
    efi::{Guid, Handle, PhysicalAddress, Status},
    system::TableHeader,
};

/// DXE Services Table GUID identifier
///
/// This GUID identifies the DXE Services Table in the EFI System Table
/// Configuration Table array. The DXE Services Table provides services for
/// managing the Global Coherency Domain memory and I/O space maps,
/// and dispatcher functions for managing driver execution dependencies.
pub const DXE_SERVICES_TABLE_GUID: crate::BinaryGuid =
    crate::BinaryGuid::from_string("05AD34BA-6F02-4214-952E-4DA0398E2BB9");

/// Adds memory or memory-mapped I/O resources to the Global Coherency Domain (GCD)
///
/// This service adds reserved memory, system memory, or memory-mapped I/O resources
/// to the Global Coherency Domain of the processor. The memory space being added
/// must not overlap with any existing memory space in the GCD.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.1
pub type AddMemorySpace = extern "efiapi" fn(GcdMemoryType, PhysicalAddress, u64, u64) -> Status;

/// Allocates memory space from the Global Coherency Domain (GCD)
///
/// This service allocates nonexistent memory, reserved memory, system memory,
/// or memory-mapped I/O resources from the Global Coherency Domain of the processor.
/// The allocation strategy is determined by the GcdAllocateType parameter.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.2
pub type AllocateMemorySpace =
    extern "efiapi" fn(GcdAllocateType, GcdMemoryType, usize, u64, *mut PhysicalAddress, Handle, Handle) -> Status;

/// Frees memory space from the Global Coherency Domain (GCD)
///
/// This service frees nonexistent memory, reserved memory, system memory,
/// or memory-mapped I/O resources from the Global Coherency Domain of the processor.
/// The freed memory becomes available for future allocation.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.3
pub type FreeMemorySpace = extern "efiapi" fn(PhysicalAddress, u64) -> Status;

/// Removes memory space from the Global Coherency Domain (GCD)
///
/// This service removes reserved memory, system memory, or memory-mapped I/O
/// resources from the Global Coherency Domain of the processor. The removed
/// region must not be currently allocated to any image or have any capabilities set.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.4
pub type RemoveMemorySpace = extern "efiapi" fn(PhysicalAddress, u64) -> Status;

/// Retrieves the memory space descriptor for a specified address from the
/// Global Coherency Domain (GCD)
///
/// This service retrieves the descriptor for a memory region containing a
/// specified address from the Global Coherency Domain memory space map.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.5
pub type GetMemorySpaceDescriptor = extern "efiapi" fn(PhysicalAddress, *mut MemorySpaceDescriptor) -> Status;

/// Sets memory space attributes in the Global Coherency Domain (GCD)
///
/// This service modifies the attributes for a memory region in the global
/// coherency domain of the processor. Attributes control caching behavior
/// and access permissions for the memory region.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.6
pub type SetMemorySpaceAttributes = extern "efiapi" fn(PhysicalAddress, u64, u64) -> Status;

/// Sets memory space capabilities in the Global Coherency Domain (GCD)
///
/// This service modifies the capabilities for a memory region in the global
/// coherency domain of the processor. Capabilities define which attributes
/// are allowed to be set for the memory region.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.7
pub type SetMemorySpaceCapabilities = extern "efiapi" fn(PhysicalAddress, u64, u64) -> Status;

/// Returns the Global Coherency Domain (GCD) memory space map
///
/// This service returns a map of all memory resources in the global coherency
/// domain of the processor, including their types, attributes, and allocation status.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.8
pub type GetMemorySpaceMap = extern "efiapi" fn(*mut usize, *mut *mut MemorySpaceDescriptor) -> Status;

/// Adds I/O space to the Global Coherency Domain (GCD)
///
/// This service adds reserved I/O or I/O resources to the global coherency
/// domain of the processor. The I/O space being added must not overlap
/// with any existing I/O space in the Global Coherency Domain.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.9
pub type AddIoSpace = extern "efiapi" fn(GcdIoType, PhysicalAddress, u64) -> Status;

/// Allocates I/O space from the Global Coherency Domain (GCD)
///
/// This service allocates nonexistent I/O, reserved I/O, or I/O resources
/// from the Global Coherency Domain of the processor. The allocation strategy
/// is determined by the GcdAllocateType parameter.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.10
pub type AllocateIoSpace =
    extern "efiapi" fn(GcdAllocateType, GcdIoType, usize, u64, *mut PhysicalAddress, Handle, Handle) -> Status;

/// Frees I/O space from the Global Coherency Domain (GCD)
///
/// This service frees nonexistent I/O, reserved I/O, or I/O resources
/// from the Global Coherency Domain of the processor. The freed I/O space
/// becomes available for future allocation.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.11
pub type FreeIoSpace = extern "efiapi" fn(PhysicalAddress, u64) -> Status;

/// Removes I/O space from the Global Coherency Domain (GCD)
///
/// This service removes reserved I/O or I/O resources from the global coherency
/// domain of the processor. The removed I/O region must not be currently allocated.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.12
pub type RemoveIoSpace = extern "efiapi" fn(PhysicalAddress, u64) -> Status;

/// This service retrieves the descriptor for an I/O region containing a specified address.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.13
pub type GetIoSpaceDescriptor = extern "efiapi" fn(PhysicalAddress, *mut IoSpaceDescriptor) -> Status;

/// Returns a map of the I/O resources in the Global Coherency Domain (GCD).
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.2.4.14
pub type GetIoSpaceMap = extern "efiapi" fn(*mut usize, *mut *mut IoSpaceDescriptor) -> Status;

/// Executes DXE drivers from firmware volumes
///
/// This service loads and executes DXE drivers from firmware volumes.
/// The dispatcher uses dependency expressions to determine driver execution order.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.3.1
pub type Dispatch = extern "efiapi" fn() -> Status;

/// Schedules a firmware file for dispatch
///
/// This service clears the Schedule on Request (SOR) flag for a component
/// that is stored in a firmware volume, allowing it to be dispatched.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.3.2
pub type Schedule = extern "efiapi" fn(Handle, *const Guid) -> Status;

/// Promotes a firmware file from untrusted to trusted state
///
/// This service promotes a file stored in a firmware volume from the untrusted
/// to the trusted state. Only the Security Architectural Protocol can place a file
/// in the untrusted state initially.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.3.3
pub type Trust = extern "efiapi" fn(Handle, *const Guid) -> Status;

/// Creates a firmware volume handle from system memory
///
/// This service creates a firmware volume handle for a firmware volume
/// that is present in system memory, making it available to the DXE dispatcher.
///
/// # Documentation
/// UEFI Platform Initialization Specification, Release 1.8, Section II-7.3.II-59 (This one does not have a section)
pub type ProcessFirmwareVolume = extern "efiapi" fn(*const c_void, usize, *mut Handle) -> Status;

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
/// Global Coherency Domain (GCD) memory types
///
/// Defines the types of memory regions that can exist in the Global Coherency Domain.
/// Each memory region has a specific type that determines how it can be used by the system.
pub enum GcdMemoryType {
    /// Memory region with no active decoder
    ///
    /// A memory region that is visible to the boot processor but has no system
    /// components currently decoding this memory region.
    #[default]
    NonExistent = 0,
    /// Reserved memory region
    ///
    /// A memory region being decoded by a system component, but not considered
    /// to be either system memory or memory-mapped I/O.
    Reserved,
    /// System memory region
    ///
    /// A memory region decoded by a memory controller that produces tested
    /// system memory available to the memory services.
    SystemMemory,
    /// Memory-mapped I/O region
    ///
    /// A memory region currently being decoded as memory-mapped I/O that can
    /// be used to access I/O devices in the platform.
    MemoryMappedIo,
    /// Persistent memory region
    ///
    /// A memory region that supports byte-addressable non-volatility,
    /// such as non-volatile dual in-line memory modules (NVDIMM).
    Persistent,
    /// High-reliability memory region
    ///
    /// A memory region that provides higher reliability relative to other memory
    /// in the system. Used when memory has varying reliability characteristics.
    MoreReliable,
    /// Unaccepted memory region
    ///
    /// A memory region that is unaccepted and must be accepted before it can
    /// be converted to system memory. Used in confidential computing environments.
    Unaccepted,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, Default)]
/// Global Coherency Domain (GCD) allocation strategies
///
/// Defines the allocation strategies that can be used when allocating memory
/// or I/O space from the Global Coherency Domain.
pub enum GcdAllocateType {
    #[default]
    /// Allocate any available address searching bottom-up
    ///
    /// Search for available space starting from the lowest addresses
    AnySearchBottomUp,
    /// Allocate below a maximum address searching bottom-up
    ///
    /// Search for available space below a specified maximum address, starting from the bottom
    MaxAddressSearchBottomUp,
    /// Allocate at a specific address
    ///
    /// Allocate at the exact address specified by the caller
    Address,
    /// Search for memory from top down
    AnySearchTopDown,
    /// Search for memory from specified max address top down
    MaxAddressSearchTopDown,
    /// Maximum allocate type value
    MaxAllocateType,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// `EFI_GCD_MEMORY_SPACE_DESCRIPTOR` in specification.
pub struct MemorySpaceDescriptor {
    /// The physical address of the first byte in the memory region.
    pub base_address: PhysicalAddress,
    /// The number of bytes in the memory region.
    pub length: u64,
    /// The bit mask of attributes that the memory region is capable of supporting.
    pub capabilities: u64,
    /// The bit mask of attributes that the memory region is currently using.
    pub attributes: u64,
    /// Type of the memory region.
    pub memory_type: GcdMemoryType,
    /// The image handle of the agent that allocated the memory resource described by PhysicalStart and NumberOfBytes.
    ///
    /// If this field is NULL, then the memory resource is not currently allocated.
    pub image_handle: Handle,
    /// The device handle for which the memory resource has been allocated.
    ///
    /// If ImageHandle is NULL, then the memory resource is not currently allocated.
    ///
    /// If this field is NULL, then the memory resource is not associated with a device that is described by a device handle.
    pub device_handle: Handle,
}

impl MemorySpaceDescriptor {
    /// Returns the overlapping range from a user provided range and the range described by this descriptor.
    /// If there is no overlap, returns an empty range (start == end).
    ///
    /// This is used when iterating over descriptors to determine how much of the descriptor falls within a given range.
    pub fn get_range_overlap_with_desc(&self, range: &Range<PhysicalAddress>) -> Range<PhysicalAddress> {
        let desc_end = self.base_address.saturating_add(self.length);

        if self.base_address >= range.end || desc_end <= range.start {
            return 0..0;
        }

        let overlap_start = max(self.base_address, range.start);
        let overlap_end = min(desc_end, range.end);

        overlap_start..overlap_end
    }

    /// Determines if this memory descriptor should be included in the EFI memory map.
    ///
    /// Only descriptors that meet UEFI requirements and represent allocatable or special memory
    /// types are included in the EFI memory map.
    ///
    /// # Returns
    /// * `Some(memory_type)` if the descriptor should be included in the EFI memory map
    /// * `None` if the descriptor should be excluded
    pub fn is_efi_memory_map_descriptor(&self) -> Option<r_efi::efi::MemoryType> {
        use crate::base::{UEFI_PAGE_MASK, UEFI_PAGE_SIZE};

        // Validate page alignment and size
        let number_of_pages = ((self.length as usize + UEFI_PAGE_MASK) / UEFI_PAGE_SIZE) as u64;
        if number_of_pages == 0 {
            debug_assert!(false, "GCD returned a memory descriptor smaller than a page.");
            return None; // skip entries for things smaller than a page
        }
        if !self.base_address.is_multiple_of(UEFI_PAGE_SIZE as u64) {
            debug_assert!(false, "GCD returned a non-page-aligned memory descriptor.");
            return None; // skip entries not page aligned
        }

        // Note: For allocator-tracked memory types, this should be called by the DXE core
        // after checking memory_type_for_handle(self.image_handle)
        match self.memory_type {
            // Free memory not tracked by any allocator.
            GcdMemoryType::SystemMemory => Some(r_efi::efi::CONVENTIONAL_MEMORY),

            // Note: there could also be MMIO tracked by the allocators which would not hit this case.
            GcdMemoryType::MemoryMappedIo => {
                // we should only be returning runtime MMIO here
                if self.attributes & r_efi::efi::MEMORY_RUNTIME == 0 {
                    None
                } else {
                    Some(r_efi::efi::MEMORY_MAPPED_IO)
                }
            }

            // Persistent. Note: this type is not allocatable, but might be created by agents other than the core directly
            // in the GCD.
            GcdMemoryType::Persistent => Some(r_efi::efi::PERSISTENT_MEMORY),

            // Unaccepted. Note: this type is not allocatable, but might be created by agents other than the core directly
            // in the GCD.
            GcdMemoryType::Unaccepted => Some(r_efi::efi::UNACCEPTED_MEMORY_TYPE),

            // Reserved.
            GcdMemoryType::Reserved => Some(r_efi::efi::RESERVED_MEMORY_TYPE),

            // Other memory types are ignored for purposes of the memory map
            _ => None,
        }
    }
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
/// Global Coherency Domain (GCD) I/O space types
///
/// Defines the types of I/O regions that can exist in the Global Coherency Domain.
/// Each I/O region has a specific type that determines how it can be accessed.
pub enum GcdIoType {
    /// I/O region with no active decoder
    ///
    /// An I/O region that is visible to the boot processor but has no system
    /// components currently decoding this I/O region.
    #[default]
    NonExistent = 0,
    /// Reserved I/O region
    ///
    /// An I/O region currently being decoded by a system component, but the I/O
    /// region cannot be used to access I/O devices.
    Reserved,
    /// Active I/O region
    ///
    /// An I/O region currently being decoded by a system component that produces
    /// I/O ports that can be used to access I/O devices.
    Io,
    /// Maximum value for GcdIoType enumeration
    Maximum,
}

#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// `EFI_GCD_IO_SPACE_DESCRIPTOR` in specification.
pub struct IoSpaceDescriptor {
    /// Physical address of the first byte in the I/O region.
    pub base_address: PhysicalAddress,
    /// Number of bytes in the I/O region.
    pub length: u64,
    /// Type of the I/O region.
    pub io_type: GcdIoType,
    /// The image handle of the agent that allocated the I/O resource described by PhysicalStart and NumberOfBytes.
    ///
    /// If this field is NULL, then the I/O resource is not currently allocated.
    pub image_handle: Handle,
    /// The device handle for which the I/O resource has been allocated.
    ///
    /// If ImageHandle is NULL , then the I/O resource is not currently allocated.
    ///
    /// If this field is NULL, then the I/O resource is not associated with a device that is described by a device handle.
    pub device_handle: Handle,
}

#[repr(C)]
/// Contains a table header and pointers to all of the DXE-specific services.
///
/// See <https://uefi.org/specs/PI/1.8A/V2_UEFI_System_Table.html#dxe-services-table>.
pub struct DxeServicesTable {
    /// Standard UEFI table header
    pub header: TableHeader,

    //
    // Global Coherency Domain (GCD)
    //
    /// Add memory space to GCD
    pub add_memory_space: AddMemorySpace,
    /// Allocate memory space from GCD
    pub allocate_memory_space: AllocateMemorySpace,
    /// Free memory space in GCD
    pub free_memory_space: FreeMemorySpace,
    /// Remove memory space from GCD
    pub remove_memory_space: RemoveMemorySpace,
    /// Get memory space descriptor
    pub get_memory_space_descriptor: GetMemorySpaceDescriptor,
    /// Set memory space attributes
    pub set_memory_space_attributes: SetMemorySpaceAttributes,
    /// Get memory space map
    pub get_memory_space_map: GetMemorySpaceMap,
    /// Add I/O space to GCD
    pub add_io_space: AddIoSpace,
    /// Allocate I/O space from GCD
    pub allocate_io_space: AllocateIoSpace,
    /// Free I/O space in GCD
    pub free_io_space: FreeIoSpace,
    /// Remove I/O space from GCD
    pub remove_io_space: RemoveIoSpace,
    /// Get I/O space descriptor
    pub get_io_space_descriptor: GetIoSpaceDescriptor,
    /// Get I/O space map
    pub get_io_space_map: GetIoSpaceMap,

    //
    // Dispatcher Services
    //
    /// Dispatch drivers
    pub dispatch: Dispatch,
    /// Schedule drivers for execution
    pub schedule: Schedule,
    /// Establish trust for drivers
    pub trust: Trust,

    //
    // Service to process a single firmware volume found in
    // a capsule
    //
    /// Process firmware volume from capsule
    pub process_firmware_volume: ProcessFirmwareVolume,

    //
    // Extension to Global Coherency Domain (GCD) Services
    //
    /// Set memory space capabilities
    pub set_memory_space_capabilities: SetMemorySpaceCapabilities,
}

impl Default for MemorySpaceDescriptor {
    fn default() -> Self {
        Self {
            base_address: Default::default(),
            length: Default::default(),
            capabilities: Default::default(),
            attributes: Default::default(),
            memory_type: Default::default(),
            image_handle: 0 as Handle,
            device_handle: 0 as Handle,
        }
    }
}

impl Default for IoSpaceDescriptor {
    fn default() -> Self {
        Self {
            base_address: Default::default(),
            length: Default::default(),
            io_type: Default::default(),
            image_handle: 0 as Handle,
            device_handle: 0 as Handle,
        }
    }
}

#[cfg(test)]
#[coverage(off)]
mod tests {
    use super::*;

    #[test]
    fn test_get_range_overlap_with_desc() {
        let desc = MemorySpaceDescriptor { base_address: 0x10000, length: 0x5000, ..Default::default() };

        // No overlap (before)
        let range = 0x0000..0x0FFF;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0..0);

        // No overlap (after)
        let range = 0x20000..0x2FFFF;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0..0);

        // Partial overlap (start)
        let range = 0x9000..0x13000;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0x10000..0x13000);

        // Partial overlap (end)
        let range = 0x11000..0x25000;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0x11000..0x15000);

        // Descriptor fully within range
        let range = 0x10000..0x20000;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0x10000..0x15000);

        // Range fully within descriptor
        let range = 0x11000..0x12000;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0x11000..0x12000);

        // range == descriptor
        let range = 0x10000..0x15000;
        let overlap = desc.get_range_overlap_with_desc(&range);
        assert_eq!(overlap, 0x10000..0x15000);
    }
}