@@ -514,86 +514,196 @@ fn create_dbg2_table(base_address: u64) -> Sdt {
514514 dbg2
515515}
516516
517+ #[ cfg( target_arch = "aarch64" ) ]
518+ #[ allow( dead_code) ]
519+ #[ repr( C , packed) ]
520+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
521+ struct IortBodyBase {
522+ pub num_nodes : u32 ,
523+ pub offset_first_node : u32 ,
524+ _reserved : u32 ,
525+ }
526+
527+ #[ cfg( target_arch = "aarch64" ) ]
528+ #[ allow( dead_code) ]
529+ #[ repr( C , packed) ]
530+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
531+ struct IortNodeCommon {
532+ pub type_ : u8 ,
533+ pub length : u16 ,
534+ pub revision : u8 ,
535+ pub node_id : u32 ,
536+ pub num_id_mappings : u32 ,
537+ pub id_mappings_array_offset : u32 ,
538+ }
539+
540+ #[ cfg( target_arch = "aarch64" ) ]
541+ #[ allow( dead_code) ]
542+ #[ repr( C , packed) ]
543+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
544+ struct IortIdMapping {
545+ pub input_base : u32 ,
546+ pub num_ids : u32 ,
547+ pub output_base : u32 ,
548+ pub output_reference : u32 ,
549+ pub flags : u32 ,
550+ }
551+
552+ #[ cfg( target_arch = "aarch64" ) ]
553+ #[ allow( dead_code) ]
554+ #[ repr( C , packed) ]
555+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
556+ struct IortMemoryAccessProperties {
557+ pub cca : u32 ,
558+ pub ah : u8 ,
559+ _reserved : u16 ,
560+ pub maf : u8 ,
561+ }
562+
563+ #[ cfg( target_arch = "aarch64" ) ]
564+ #[ allow( dead_code) ]
565+ #[ repr( C , packed) ]
566+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
567+ struct IortItsGroupBase {
568+ pub common : IortNodeCommon ,
569+ pub its_count : u32 ,
570+ // GIC ITS identifiers follow: array of `u32`
571+ }
572+
573+ #[ cfg( target_arch = "aarch64" ) ]
574+ #[ allow( dead_code) ]
575+ #[ repr( C , packed) ]
576+ #[ derive( Default , IntoBytes , Immutable , FromBytes ) ]
577+ struct IortPciRootComplexBase {
578+ pub common : IortNodeCommon ,
579+ pub mem_access_props : IortMemoryAccessProperties ,
580+ pub ats_attribute : u32 ,
581+ pub pci_segment_number : u32 ,
582+ pub memory_address_size_limit : u8 ,
583+ _reserved : [ u8 ; 3 ] ,
584+ // ID mappings follow: array of `struct IortIdMapping`
585+ }
586+
587+ #[ cfg( target_arch = "aarch64" ) ]
588+ #[ inline]
589+ fn align_to_8_bytes ( len : usize ) -> usize {
590+ ( 8 - ( len % 8 ) ) % 8
591+ }
592+
517593#[ cfg( target_arch = "aarch64" ) ]
518594// Generate IORT table based on Spec Revision E.b:
519595// https://developer.arm.com/documentation/den0049/eb/?lang=en
520596fn create_iort_table ( pci_segments : & [ PciSegment ] ) -> Sdt {
597+ const ACPI_IORT_HEADER_SIZE : u32 = 36 ;
598+ const ACPI_IORT_REVISION : u8 = 3 ;
521599 const ACPI_IORT_NODE_ITS_GROUP : u8 = 0x00 ;
522600 const ACPI_IORT_NODE_PCI_ROOT_COMPLEX : u8 = 0x02 ;
523- const ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET : usize = 72 ;
524- const ACPI_IORT_NODE_ROOT_COMPLEX_SIZE : usize = 60 ;
601+
602+ // IORT header
603+ let mut iort = Sdt :: new (
604+ * b"IORT" ,
605+ ACPI_IORT_HEADER_SIZE ,
606+ ACPI_IORT_REVISION ,
607+ * b"CLOUDH" ,
608+ * b"CHIORT " ,
609+ 1 ,
610+ ) ;
611+ assert_eq ! ( iort. len( ) , ACPI_IORT_HEADER_SIZE as usize ) ;
525612
526613 // The IORT table contains:
527- // - Header (size = 40)
528- // - 1 x ITS Group Node (size = 24)
529- // - N x Root Complex Node (N = number of pci segments, size = 60 x N)
530- let iort_table_size: u32 = ( ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET
531- + ACPI_IORT_NODE_ROOT_COMPLEX_SIZE * pci_segments. len ( ) )
532- as u32 ;
533- let mut iort = Sdt :: new ( * b"IORT" , iort_table_size, 3 , * b"CLOUDH" , * b"CHIORT " , 1 ) ;
534- iort. write ( 36 , ( ( 1 + pci_segments. len ( ) ) as u32 ) . to_le ( ) ) ;
535- iort. write ( 40 , ( 48u32 ) . to_le ( ) ) ;
536-
537- // ITS group node
538- iort. write ( 48 , ACPI_IORT_NODE_ITS_GROUP ) ;
539- // Length of the ITS group node in bytes
540- iort. write ( 49 , ( 24u16 ) . to_le ( ) ) ;
541- // Revision
542- iort. write ( 51 , ( 1u8 ) . to_le ( ) ) ;
543- // ITS counts
544- iort. write ( 64 , ( 1u32 ) . to_le ( ) ) ;
545- // GIC ITS Identity Array
546- iort. write ( 68 , ( 0u32 ) . to_le ( ) ) ; // Value must match what's defined in MADT
547-
548- // Root Complex Nodes
549- for ( i, segment) in pci_segments. iter ( ) . enumerate ( ) {
550- let node_offset: usize =
551- ACPI_IORT_NODE_ROOT_COMPLEX_OFFSET + i * ACPI_IORT_NODE_ROOT_COMPLEX_SIZE ;
552- iort. write ( node_offset, ACPI_IORT_NODE_PCI_ROOT_COMPLEX ) ;
553- // Length of the root complex node in bytes
554- iort. write (
555- node_offset + 1 ,
556- ( ACPI_IORT_NODE_ROOT_COMPLEX_SIZE as u16 ) . to_le ( ) ,
557- ) ;
558- // Revision
559- iort. write ( node_offset + 3 , ( 3u8 ) . to_le ( ) ) ;
560- // Node ID
561- iort. write ( node_offset + 4 , ( segment. id as u32 ) . to_le ( ) ) ;
562- // Mapping counts
563- iort. write ( node_offset + 8 , ( 1u32 ) . to_le ( ) ) ;
564- // Offset from the start of the RC node to the start of its Array of ID mappings
565- iort. write ( node_offset + 12 , ( 36u32 ) . to_le ( ) ) ;
566- // Fully coherent device
567- iort. write ( node_offset + 16 , ( 1u32 ) . to_le ( ) ) ;
568- // CCA = CPM = DCAS = 1
569- iort. write ( node_offset + 23 , 3u8 ) ;
570- // PCI segment number
571- iort. write ( node_offset + 28 , ( segment. id as u32 ) . to_le ( ) ) ;
572- // Memory address size limit
573- iort. write ( node_offset + 32 , ( 64u8 ) . to_le ( ) ) ;
574-
575- // From offset 32 onward is the space for ID mappings Array.
576- // Now we have only one mapping.
577- let mapping_offset: usize = node_offset + 36 ;
578- // The lowest value in the input range
579- iort. write ( mapping_offset, ( 0u32 ) . to_le ( ) ) ;
580- // The number of IDs in the range minus one:
581- // This should cover all the devices of a segment:
582- // 1 (bus) x 32 (devices) x 8 (functions) = 256
583- // Note: Currently only 1 bus is supported in a segment.
584- iort. write ( mapping_offset + 4 , ( 255_u32 ) . to_le ( ) ) ;
585- // Output base maps to ITS device IDs which must match the
586- // device ID encoding used in KVM MSI routing setup, which
587- // shares the same limitation - only 1 bus per segment and
588- // up to 256 segments.
589- // See: https://github.com/cloud-hypervisor/cloud-hypervisor/commit/c9374d87ac453d49185aa7b734df089444166484
614+ // - IortBodyBase
615+ // - 1 x ITS Group Node
616+ // - N x PCI Root Complex Node (N = number of pci segments)
617+ let num_nodes = ( 1 + pci_segments. len ( ) ) as u32 ;
618+ // First node is the ITS Group Node located right after the IORT Body Base
619+ let offset_its_node = iort. len ( ) + std:: mem:: size_of :: < IortBodyBase > ( ) ;
620+ assert ! ( align_to_8_bytes( offset_its_node) == 0 ) ; // Ensure the ITS node is 8-byte aligned
621+ iort. append ( IortBodyBase {
622+ num_nodes,
623+ offset_first_node : offset_its_node as u32 ,
624+ _reserved : 0 ,
625+ } ) ;
626+ assert ! ( iort. len( ) == offset_its_node) ;
627+
628+ // ITS Group Node contains:
629+ // - IortItsGroupBase
630+ // - ITS Identifiers Array: Array of u32 ITS IDs
631+ // Currently contains a single ITS with ID 0, which matches the
632+ // `translation_id` field of the `GisIts`` structure in the MADT table.
633+ let its_id_array = [ 0u32 ; 1 ] ;
634+ let its_count = its_id_array. len ( ) ;
635+ let its_group_node_size =
636+ std:: mem:: size_of :: < IortItsGroupBase > ( ) + its_count * std:: mem:: size_of :: < u32 > ( ) ;
637+ let padding = align_to_8_bytes ( iort. len ( ) + its_group_node_size) ;
638+ iort. append ( IortItsGroupBase {
639+ common : IortNodeCommon {
640+ type_ : ACPI_IORT_NODE_ITS_GROUP ,
641+ length : ( its_group_node_size + padding) as u16 ,
642+ revision : 1 ,
643+ node_id : 0 , // todo
644+ num_id_mappings : 0 ,
645+ id_mappings_array_offset : 0 ,
646+ } ,
647+ its_count : its_count as u32 ,
648+ } ) ;
649+ iort. append ( its_id_array) ;
650+ iort. append_slice ( & vec ! [ 0u8 ; padding] ) ; // Add padding to align to 8 bytes
651+
652+ // Create PCI Root Complex Node for each PCI segment
653+ for segment in pci_segments. iter ( ) {
654+ assert ! ( align_to_8_bytes( iort. len( ) ) == 0 ) ; // Ensure each node is 8-byte aligned
655+
656+ // Each PCI Root Complex Node contains:
657+ // - IortPciRootComplexBase
658+ // - ID mapping Array: Array of IortIdMapping
659+ // Currently contains a single mapping that maps all device IDs
660+ // in the segment to the ITS Group Node.
661+ let num_id_mappings = 1 ;
662+ let node_size = std:: mem:: size_of :: < IortPciRootComplexBase > ( )
663+ + num_id_mappings * std:: mem:: size_of :: < IortIdMapping > ( ) ;
664+ let padding = align_to_8_bytes ( iort. len ( ) + node_size) ;
665+ iort. append ( IortPciRootComplexBase {
666+ common : IortNodeCommon {
667+ type_ : ACPI_IORT_NODE_PCI_ROOT_COMPLEX ,
668+ length : ( node_size + padding) as u16 ,
669+ revision : 3 ,
670+ node_id : segment. id as u32 , // todo to avoid conflict with ITS node IDs
671+ num_id_mappings : num_id_mappings as u32 ,
672+ // ID mapping array starts right after `IortPciRootComplexBase`
673+ id_mappings_array_offset : std:: mem:: size_of :: < IortPciRootComplexBase > ( ) as u32 ,
674+ } ,
675+ mem_access_props : IortMemoryAccessProperties {
676+ cca : 1 , // Fully coherent device
677+ ah : 0 ,
678+ _reserved : 0 ,
679+ maf : 3 , // CPM = DCAS = 1
680+ } ,
681+ ats_attribute : 0 ,
682+ pci_segment_number : segment. id as u32 ,
683+ memory_address_size_limit : 64u8 ,
684+ _reserved : [ 0 ; 3 ] ,
685+ } ) ;
686+ // ID Mapping for this Root Complex
687+ // Maps 256 device IDs (1 bus × 32 devices × 8 functions)
590688 assert ! ( segment. id < 256 , "Up to 256 PCI segments are supported." ) ;
591- iort. write ( mapping_offset + 8 , ( ( 256 * segment. id ) as u32 ) . to_le ( ) ) ;
592- // id_mapping_array_output_reference should be
593- // the ITS group node (the first node) if no SMMU
594- iort. write ( mapping_offset + 12 , ( 48u32 ) . to_le ( ) ) ;
595- // Flags
596- iort. write ( mapping_offset + 16 , ( 0u32 ) . to_le ( ) ) ;
689+ iort. append ( IortIdMapping {
690+ input_base : 0 ,
691+ // The number of IDs in the range minus one:
692+ // This should cover all the devices of a segment:
693+ // 1 (bus) x 32 (devices) x 8 (functions) = 256
694+ // Note: Currently only 1 bus is supported in a segment.
695+ num_ids : 255 ,
696+ // Output base maps to ITS device IDs which must match the
697+ // device ID encoding used in KVM MSI routing setup, which
698+ // shares the same limitation - only 1 bus per segment and
699+ // up to 256 segments.
700+ // See: https://github.com/cloud-hypervisor/cloud-hypervisor/commit/c9374d87ac453d49185aa7b734df089444166484
701+ output_base : ( 256 * segment. id ) as u32 ,
702+ // Output reference node is the ITS group node as there is no SMMU node
703+ output_reference : offset_its_node as u32 ,
704+ flags : 0 ,
705+ } ) ;
706+ iort. append_slice ( & vec ! [ 0u8 ; padding] ) ; // Add padding to align to 8 bytes
597707 }
598708
599709 iort. update_checksum ( ) ;
0 commit comments