1
1
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
2
// SPDX-License-Identifier: Apache-2.0
3
3
4
- use std:: { io, result} ;
4
+ use std:: { boxed :: Box , io, result} ;
5
5
6
6
use kvm_ioctls:: { DeviceFd , VmFd } ;
7
7
8
- // Unfortunately bindgen omits defines that are based on other defines.
9
- // See arch/arm64/include/uapi/asm/kvm.h file from the linux kernel.
10
- const SZ_64K : u64 = 0x0001_0000 ;
11
- const KVM_VGIC_V3_DIST_SIZE : u64 = SZ_64K ;
12
- const KVM_VGIC_V3_REDIST_SIZE : u64 = ( 2 * SZ_64K ) ;
8
+ use super :: gicv2:: GICv2 ;
9
+ use super :: gicv3:: GICv3 ;
13
10
14
11
/// Errors thrown while setting up the GIC.
15
12
#[ derive( Debug ) ]
@@ -21,104 +18,130 @@ pub enum Error {
21
18
}
22
19
type Result < T > = result:: Result < T , Error > ;
23
20
24
- /// Create a GICv3 device.
25
- ///
26
- /// Logic from this function is based on virt/kvm/arm/vgic/vgic-kvm-device.c from linux kernel.
27
- pub fn create_gicv3 ( vm : & VmFd , vcpu_count : u8 ) -> Result < DeviceFd > {
28
- /* We are creating a V3 GIC.
29
- As per https://static.docs.arm.com/dai0492/b/GICv3_Software_Overview_Official_Release_B.pdf,
30
- section 3.5 Programmers' model, the register interface of a GICv3 interrupt controller is split
31
- into three groups: distributor, redistributor, CPU.
32
- As per Figure 9 from same section, there is 1 Distributor and multiple redistributors (one per
33
- each CPU).
34
- */
35
- let mut gic_device = kvm_bindings:: kvm_create_device {
36
- type_ : kvm_bindings:: kvm_device_type_KVM_DEV_TYPE_ARM_VGIC_V3,
37
- fd : 0 ,
38
- flags : 0 ,
39
- } ;
40
-
41
- let vgic_fd = vm
42
- . create_device ( & mut gic_device)
43
- . map_err ( Error :: CreateGIC ) ?;
44
-
45
- /* Setting up the distributor attribute.
46
- We are placing the GIC below 1GB so we need to substract the size of the distributor.
47
- */
48
- let dist_attr = kvm_bindings:: kvm_device_attr {
49
- group : kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_ADDR ,
50
- attr : u64:: from ( kvm_bindings:: KVM_VGIC_V3_ADDR_TYPE_DIST ) ,
51
- addr : & get_dist_addr ( ) as * const u64 as u64 ,
52
- flags : 0 ,
53
- } ;
54
- vgic_fd
55
- . set_device_attr ( & dist_attr)
56
- . map_err ( Error :: SetDeviceAttribute ) ?;
57
-
58
- /* Setting up the redistributors' attribute.
59
- We are calculating here the start of the redistributors address. We have one per CPU.
60
- */
61
- let redists_attr = kvm_bindings:: kvm_device_attr {
62
- group : kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_ADDR ,
63
- attr : u64:: from ( kvm_bindings:: KVM_VGIC_V3_ADDR_TYPE_REDIST ) ,
64
- addr : & get_redists_addr ( u64:: from ( vcpu_count) ) as * const u64 as u64 ,
65
- flags : 0 ,
66
- } ;
67
- vgic_fd
68
- . set_device_attr ( & redists_attr)
69
- . map_err ( Error :: SetDeviceAttribute ) ?;
70
-
71
- /* We need to tell the kernel how many irqs to support with this vgic.
72
- See the `layout` module for details.
73
- */
74
- let nr_irqs: u32 = super :: layout:: IRQ_MAX - super :: layout:: IRQ_BASE + 1 ;
75
- let nr_irqs_ptr = & nr_irqs as * const u32 ;
76
- let nr_irqs_attr = kvm_bindings:: kvm_device_attr {
77
- group : kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_NR_IRQS ,
78
- attr : 0 ,
79
- addr : nr_irqs_ptr as u64 ,
80
- flags : 0 ,
81
- } ;
82
- vgic_fd
83
- . set_device_attr ( & nr_irqs_attr)
84
- . map_err ( Error :: SetDeviceAttribute ) ?;
85
-
86
- /* Finalize the GIC.
87
- See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
88
- */
89
- let init_gic_attr = kvm_bindings:: kvm_device_attr {
90
- group : kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_CTRL ,
91
- attr : u64:: from ( kvm_bindings:: KVM_DEV_ARM_VGIC_CTRL_INIT ) ,
92
- addr : 0 ,
93
- flags : 0 ,
94
- } ;
95
- vgic_fd
96
- . set_device_attr ( & init_gic_attr)
97
- . map_err ( Error :: SetDeviceAttribute ) ?;
98
-
99
- Ok ( vgic_fd)
100
- }
21
+ /// Trait for GIC devices.
22
+ pub trait GICDevice {
23
+ /// Returns the file descriptor of the GIC device
24
+ fn device_fd ( & self ) -> & DeviceFd ;
25
+
26
+ /// Returns an array with GIC device properties
27
+ fn device_properties ( & self ) -> & [ u64 ] ;
28
+
29
+ /// Returns the number of vCPUs this GIC handles
30
+ fn vcpu_count ( & self ) -> u64 ;
31
+
32
+ /// Returns the fdt compatibility property of the device
33
+ fn fdt_compatibility ( & self ) -> & str ;
34
+
35
+ /// Returns the maint_irq fdt property of the device
36
+ fn fdt_maint_irq ( & self ) -> u32 ;
37
+
38
+ /// Returns the GIC version of the device
39
+ fn version ( ) -> u32
40
+ where
41
+ Self : Sized ;
42
+
43
+ /// Create the GIC device object
44
+ fn create_device ( fd : DeviceFd , vcpu_count : u64 ) -> Box < dyn GICDevice >
45
+ where
46
+ Self : Sized ;
47
+
48
+ /// Setup the device-specific attributes
49
+ fn init_device_attributes ( gic_device : & Box < dyn GICDevice > ) -> Result < ( ) >
50
+ where
51
+ Self : Sized ;
52
+
53
+ /// Initialize a GIC device
54
+ fn init_device ( vm : & VmFd ) -> Result < DeviceFd >
55
+ where
56
+ Self : Sized ,
57
+ {
58
+ let mut gic_device = kvm_bindings:: kvm_create_device {
59
+ type_ : Self :: version ( ) ,
60
+ fd : 0 ,
61
+ flags : 0 ,
62
+ } ;
63
+
64
+ vm. create_device ( & mut gic_device) . map_err ( Error :: CreateGIC )
65
+ }
101
66
102
- // Auxiliary functions for getting addresses and size of where the distributor and redistributor
103
- // are placed.
104
- /// Get the address of the GIC distributor.
105
- pub fn get_dist_addr ( ) -> u64 {
106
- super :: layout:: MAPPED_IO_START - KVM_VGIC_V3_DIST_SIZE
107
- }
67
+ /// Set a GIC device attribute
68
+ fn set_device_attribute (
69
+ fd : & DeviceFd ,
70
+ group : u32 ,
71
+ attr : u64 ,
72
+ addr : u64 ,
73
+ flags : u32 ,
74
+ ) -> Result < ( ) >
75
+ where
76
+ Self : Sized ,
77
+ {
78
+ let attr = kvm_bindings:: kvm_device_attr {
79
+ group : group,
80
+ attr : attr,
81
+ addr : addr,
82
+ flags : flags,
83
+ } ;
84
+ fd. set_device_attr ( & attr)
85
+ . map_err ( Error :: SetDeviceAttribute ) ?;
86
+
87
+ Ok ( ( ) )
88
+ }
108
89
109
- /// Get the size of the GIC distributor.
110
- pub fn get_dist_size ( ) -> u64 {
111
- KVM_VGIC_V3_DIST_SIZE
112
- }
90
+ /// Finalize the setup of a GIC device
91
+ fn finalize_device ( gic_device : & Box < dyn GICDevice > ) -> Result < ( ) >
92
+ where
93
+ Self : Sized ,
94
+ {
95
+ /* We need to tell the kernel how many irqs to support with this vgic.
96
+ * See the `layout` module for details.
97
+ */
98
+ let nr_irqs: u32 = super :: layout:: IRQ_MAX - super :: layout:: IRQ_BASE + 1 ;
99
+ let nr_irqs_ptr = & nr_irqs as * const u32 ;
100
+ Self :: set_device_attribute (
101
+ gic_device. device_fd ( ) ,
102
+ kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_NR_IRQS ,
103
+ 0 ,
104
+ nr_irqs_ptr as u64 ,
105
+ 0 ,
106
+ ) ?;
107
+
108
+ /* Finalize the GIC.
109
+ * See https://code.woboq.org/linux/linux/virt/kvm/arm/vgic/vgic-kvm-device.c.html#211.
110
+ */
111
+ Self :: set_device_attribute (
112
+ gic_device. device_fd ( ) ,
113
+ kvm_bindings:: KVM_DEV_ARM_VGIC_GRP_CTRL ,
114
+ u64:: from ( kvm_bindings:: KVM_DEV_ARM_VGIC_CTRL_INIT ) ,
115
+ 0 ,
116
+ 0 ,
117
+ ) ?;
118
+
119
+ Ok ( ( ) )
120
+ }
113
121
114
- /// Get the address of the GIC redistributors.
115
- pub fn get_redists_addr ( vcpu_count : u64 ) -> u64 {
116
- get_dist_addr ( ) - get_redists_size ( vcpu_count)
122
+ /// Method to initialize the GIC device
123
+ fn new ( vm : & VmFd , vcpu_count : u64 ) -> Result < Box < dyn GICDevice > >
124
+ where
125
+ Self : Sized ,
126
+ {
127
+ let vgic_fd = Self :: init_device ( vm) ?;
128
+
129
+ let device = Self :: create_device ( vgic_fd, vcpu_count) ;
130
+
131
+ Self :: init_device_attributes ( & device) ?;
132
+
133
+ Self :: finalize_device ( & device) ?;
134
+
135
+ Ok ( device)
136
+ }
117
137
}
118
138
119
- /// Get the size of the GIC redistributors.
120
- pub fn get_redists_size ( vcpu_count : u64 ) -> u64 {
121
- vcpu_count * KVM_VGIC_V3_REDIST_SIZE
139
+ /// Create a GIC device.
140
+ ///
141
+ /// It will try to create by default a GICv3 device. If that fails it will try
142
+ /// to fall-back to a GICv2 device.
143
+ pub fn create_gic ( vm : & VmFd , vcpu_count : u64 ) -> Result < Box < dyn GICDevice > > {
144
+ GICv3 :: new ( vm, vcpu_count) . or_else ( |_| GICv2 :: new ( vm, vcpu_count) )
122
145
}
123
146
124
147
#[ cfg( test) ]
@@ -128,9 +151,9 @@ mod tests {
128
151
use kvm_ioctls:: Kvm ;
129
152
130
153
#[ test]
131
- fn test_create_gicv3 ( ) {
154
+ fn test_create_gic ( ) {
132
155
let kvm = Kvm :: new ( ) . unwrap ( ) ;
133
156
let vm = kvm. create_vm ( ) . unwrap ( ) ;
134
- assert ! ( create_gicv3 ( & vm, 1 ) . is_ok( ) ) ;
157
+ assert ! ( create_gic ( & vm, 1 ) . is_ok( ) ) ;
135
158
}
136
159
}
0 commit comments