1+ /*
2+ * Copyright (c) 2025 Advanced Micro Devices, Inc. All rights reserved.
3+ *
4+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5+ * of this software and associated documentation files (the "Software"), to deal
6+ * in the Software without restriction, including without limitation the rights
7+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8+ * copies of the Software, and to permit persons to whom the Software is
9+ * furnished to do so, subject to the following conditions:
10+ *
11+ * The above copyright notice and this permission notice shall be included in
12+ * all copies or substantial portions of the Software.
13+ *
14+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20+ * THE SOFTWARE
21+ */
22+
23+ #include <linux/version.h>
24+
25+ #include <linux/init.h>
26+ #include <linux/module.h>
27+ #if defined(SUPPORT_LIVE_MIGRATION )
28+ #include <linux/pci.h>
29+ #include <linux/iommu.h>
30+ #include <linux/vfio_pci_core.h>
31+ #include "gim_vfio_pci.h"
32+
33+ #define get_mig_info_symbol "gim_get_mig_info"
34+
35+ typedef int (* get_mig_info_t )(struct device * dev , struct gim_mig_info * mig_info );
36+
37+ static int amd_vfio_pci_get_mig_info (struct device * dev , struct gim_mig_info * mig_info )
38+ {
39+ get_mig_info_t get_mig_info ;
40+ int ret ;
41+
42+ get_mig_info = __symbol_get (get_mig_info_symbol );
43+
44+ if (get_mig_info == NULL ) {
45+ pr_err ("Failed to get symbol: %s!" , get_mig_info_symbol );
46+ return - EINVAL ;
47+ }
48+
49+ ret = get_mig_info (dev , mig_info );
50+ if (ret ) {
51+ __symbol_put (get_mig_info_symbol );
52+ return ret ;
53+ }
54+
55+ if (!mig_info -> mig_ops || !mig_info -> mig_ops -> migration_get_data_size ||
56+ !mig_info -> mig_ops -> migration_get_state ||
57+ !mig_info -> mig_ops -> migration_set_state ) {
58+ pr_err ("Incomplete mig_ops detected\n" );
59+ mig_info -> flags = 0 ;
60+ mig_info -> mig_ops = NULL ;
61+ ret = - EINVAL ;
62+ __symbol_put (get_mig_info_symbol );
63+ }
64+
65+ return ret ;
66+ };
67+
68+ static struct file * amd_vfio_pci_migration_set_state (struct vfio_device * device ,
69+ enum vfio_device_mig_state new_state )
70+ {
71+ int ret ;
72+ struct file * filp = NULL ;
73+ struct gim_mig_info mig_info ;
74+
75+ ret = amd_vfio_pci_get_mig_info (device -> dev , & mig_info );
76+ if (ret )
77+ return ERR_PTR (ret );
78+
79+ filp = mig_info .mig_ops -> migration_set_state (device , new_state );
80+ __symbol_put (get_mig_info_symbol );
81+ return filp ;
82+ }
83+
84+ static int amd_vfio_pci_migration_get_state (struct vfio_device * device ,
85+ enum vfio_device_mig_state * curr_state )
86+ {
87+ struct gim_mig_info mig_info ;
88+ int ret ;
89+
90+ ret = amd_vfio_pci_get_mig_info (device -> dev , & mig_info );
91+ if (ret )
92+ return ret ;
93+
94+ ret = mig_info .mig_ops -> migration_get_state (device , curr_state );
95+ __symbol_put (get_mig_info_symbol );
96+ return ret ;
97+ }
98+
99+ static int amd_vfio_pci_migration_get_data_size (struct vfio_device * device ,
100+ unsigned long * stop_copy_length )
101+ {
102+ struct gim_mig_info mig_info ;
103+ int ret ;
104+
105+ ret = amd_vfio_pci_get_mig_info (device -> dev , & mig_info );
106+ if (ret )
107+ return ret ;
108+
109+ ret = mig_info .mig_ops -> migration_get_data_size (device , stop_copy_length );
110+ __symbol_put (get_mig_info_symbol );
111+ return ret ;
112+ }
113+
114+ static struct vfio_migration_ops amd_vfio_migration_ops = {
115+ .migration_get_state = amd_vfio_pci_migration_get_state ,
116+ .migration_set_state = amd_vfio_pci_migration_set_state ,
117+ .migration_get_data_size = amd_vfio_pci_migration_get_data_size ,
118+ };
119+
120+ static int amd_vfio_pci_open_device (struct vfio_device * vdev )
121+ {
122+ int ret ;
123+ struct gim_mig_info mig_info ;
124+ struct vfio_pci_core_device * core_dev =
125+ container_of (vdev , struct vfio_pci_core_device , vdev );
126+ struct pci_dev * pdev = to_pci_dev (vdev -> dev );
127+
128+ if (device_iommu_capable (& pdev -> dev , IOMMU_CAP_DIRTY_TRACKING )) {
129+ ret = amd_vfio_pci_get_mig_info (vdev -> dev , & mig_info );
130+ if (ret ) {
131+ dev_err (& pdev -> dev ,
132+ "Failed to get migration info, live migration disabled\n" );
133+ } else {
134+ if (mig_info .mig_ops -> migration_set_state (vdev , VFIO_DEVICE_STATE_RUNNING )) {
135+ dev_err (& pdev -> dev ,
136+ "Failed to set migration state to RUNNING, live migration disabled\n" );
137+ } else {
138+ vdev -> migration_flags = mig_info .flags ;
139+ vdev -> mig_ops = & amd_vfio_migration_ops ;
140+ dev_info (& pdev -> dev , "Live migration enabled\n" );
141+ }
142+
143+ __symbol_put (get_mig_info_symbol );
144+ }
145+ } else {
146+ dev_info (& pdev -> dev , "IOMMU dirty page tracking is disabled\n" );
147+ }
148+
149+ ret = vfio_pci_core_enable (core_dev );
150+ if (ret )
151+ return ret ;
152+
153+ vfio_pci_core_finish_enable (core_dev );
154+
155+ return 0 ;
156+ }
157+
158+ static const struct pci_device_id amd_vfio_pci_table [] = { { 0x1002 , 0x74B6 , PCI_ANY_ID ,
159+ PCI_ANY_ID , 0 , 0 , 11 },
160+ {} };
161+
162+ static const struct vfio_device_ops amd_vfio_pci_ops = {
163+ .name = "amd-vfio-pci" ,
164+ .init = vfio_pci_core_init_dev ,
165+ .release = vfio_pci_core_release_dev ,
166+ .open_device = amd_vfio_pci_open_device ,
167+ .close_device = vfio_pci_core_close_device ,
168+ .ioctl = vfio_pci_core_ioctl ,
169+ .device_feature = vfio_pci_core_ioctl_feature ,
170+ .read = vfio_pci_core_read ,
171+ .write = vfio_pci_core_write ,
172+ .mmap = vfio_pci_core_mmap ,
173+ .request = vfio_pci_core_request ,
174+ .match = vfio_pci_core_match ,
175+ .bind_iommufd = vfio_iommufd_physical_bind ,
176+ .unbind_iommufd = vfio_iommufd_physical_unbind ,
177+ .attach_ioas = vfio_iommufd_physical_attach_ioas ,
178+ .detach_ioas = vfio_iommufd_physical_detach_ioas ,
179+ };
180+
181+ static int amd_vfio_pci_probe (struct pci_dev * pdev , const struct pci_device_id * id )
182+ {
183+ struct vfio_pci_core_device * core_dev ;
184+ int ret ;
185+
186+ core_dev =
187+ vfio_alloc_device (vfio_pci_core_device , vdev , & pdev -> dev , & amd_vfio_pci_ops );
188+ if (IS_ERR (core_dev )) {
189+ return PTR_ERR (core_dev );
190+ }
191+ dev_set_drvdata (& pdev -> dev , core_dev );
192+
193+ ret = vfio_pci_core_register_device (core_dev );
194+ if (ret ) {
195+ vfio_put_device (& core_dev -> vdev );
196+ return ret ;
197+ }
198+
199+ return ret ;
200+ }
201+
202+ static void amd_vfio_pci_remove (struct pci_dev * pdev )
203+ {
204+ struct vfio_pci_core_device * core_dev = dev_get_drvdata (& pdev -> dev );
205+
206+ vfio_pci_core_unregister_device (core_dev );
207+ vfio_put_device (& core_dev -> vdev );
208+ }
209+
210+ static struct pci_driver amd_vfio_pci_driver = {
211+ .name = "amd-vfio-pci" ,
212+ .id_table = amd_vfio_pci_table ,
213+ .probe = amd_vfio_pci_probe ,
214+ .remove = amd_vfio_pci_remove ,
215+ .err_handler = & vfio_pci_core_err_handlers ,
216+ .driver_managed_dma = true,
217+ };
218+ #endif
219+
220+ static int __init amd_vfio_pci_init (void )
221+ {
222+ #if defined(SUPPORT_LIVE_MIGRATION )
223+ int ret ;
224+
225+ /* Register and scan for devices */
226+ ret = pci_register_driver (& amd_vfio_pci_driver );
227+ if (ret )
228+ return ret ;
229+ pr_info ("AMD VFIO PCI driver initialized successfully.\n" );
230+ return 0 ;
231+
232+ #endif
233+ pr_warn ("Kernel version too old for AMD VFIO PCI live migration support.Please upgrade to kernel 6.8 or later.\n" );
234+ return - ENODEV ;
235+ };
236+
237+ static void __exit amd_vfio_pci_cleanup (void )
238+ {
239+ #if defined(SUPPORT_LIVE_MIGRATION )
240+ pci_unregister_driver (& amd_vfio_pci_driver );
241+ pr_info ("AMD VFIO PCI driver cleaned up successfully.\n" );
242+ #endif
243+ }
244+
245+ module_init (amd_vfio_pci_init );
246+ module_exit (amd_vfio_pci_cleanup );
247+
248+ MODULE_AUTHOR ("Advanced Micro Devices, Inc." );
249+ MODULE_DESCRIPTION ("AMD VFIO PCI MODULE" );
250+ MODULE_LICENSE ("Dual MIT/GPL" );
0 commit comments