1 | /* |
2 | * Copyright 2019 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is 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 |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | #include "mod_vmid.h" |
27 | |
28 | struct core_vmid { |
29 | struct mod_vmid public; |
30 | struct dc *dc; |
31 | |
32 | unsigned int num_vmid; |
33 | unsigned int num_vmids_available; |
34 | uint64_t ptb_assigned_to_vmid[MAX_VMID]; |
35 | struct dc_virtual_addr_space_config base_config; |
36 | }; |
37 | |
38 | #define MOD_VMID_TO_CORE(mod_vmid)\ |
39 | container_of(mod_vmid, struct core_vmid, public) |
40 | |
41 | static void add_ptb_to_table(struct core_vmid *core_vmid, unsigned int vmid, uint64_t ptb) |
42 | { |
43 | if (vmid < MAX_VMID) { |
44 | core_vmid->ptb_assigned_to_vmid[vmid] = ptb; |
45 | core_vmid->num_vmids_available--; |
46 | } |
47 | } |
48 | |
49 | static void clear_entry_from_vmid_table(struct core_vmid *core_vmid, unsigned int vmid) |
50 | { |
51 | if (vmid < MAX_VMID) { |
52 | core_vmid->ptb_assigned_to_vmid[vmid] = 0; |
53 | core_vmid->num_vmids_available++; |
54 | } |
55 | } |
56 | |
57 | static void evict_vmids(struct core_vmid *core_vmid) |
58 | { |
59 | int i; |
60 | uint16_t ord = dc_get_vmid_use_vector(dc: core_vmid->dc); |
61 | |
62 | // At this point any positions with value 0 are unused vmids, evict them |
63 | for (i = 1; i < core_vmid->num_vmid; i++) { |
64 | if (!(ord & (1u << i))) |
65 | clear_entry_from_vmid_table(core_vmid, vmid: i); |
66 | } |
67 | } |
68 | |
69 | // Return value of -1 indicates vmid table uninitialized or ptb dne in the table |
70 | static int get_existing_vmid_for_ptb(struct core_vmid *core_vmid, uint64_t ptb) |
71 | { |
72 | int i; |
73 | |
74 | for (i = 0; i < core_vmid->num_vmid; i++) { |
75 | if (core_vmid->ptb_assigned_to_vmid[i] == ptb) |
76 | return i; |
77 | } |
78 | |
79 | return -1; |
80 | } |
81 | |
82 | // Expected to be called only when there's an available vmid |
83 | static int get_next_available_vmid(struct core_vmid *core_vmid) |
84 | { |
85 | int i; |
86 | |
87 | for (i = 1; i < core_vmid->num_vmid; i++) { |
88 | if (core_vmid->ptb_assigned_to_vmid[i] == 0) |
89 | return i; |
90 | } |
91 | |
92 | return -1; |
93 | } |
94 | |
95 | uint8_t mod_vmid_get_for_ptb(struct mod_vmid *mod_vmid, uint64_t ptb) |
96 | { |
97 | struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); |
98 | int vmid = 0; |
99 | |
100 | // Physical address gets vmid 0 |
101 | if (ptb == 0) |
102 | return 0; |
103 | |
104 | vmid = get_existing_vmid_for_ptb(core_vmid, ptb); |
105 | |
106 | if (vmid == -1) { |
107 | struct dc_virtual_addr_space_config va_config = core_vmid->base_config; |
108 | |
109 | va_config.page_table_base_addr = ptb; |
110 | |
111 | if (core_vmid->num_vmids_available == 0) |
112 | evict_vmids(core_vmid); |
113 | |
114 | vmid = get_next_available_vmid(core_vmid); |
115 | if (vmid != -1) { |
116 | add_ptb_to_table(core_vmid, vmid, ptb); |
117 | |
118 | dc_setup_vm_context(dc: core_vmid->dc, va_config: &va_config, vmid); |
119 | } else |
120 | ASSERT(0); |
121 | } |
122 | |
123 | return vmid; |
124 | } |
125 | |
126 | void mod_vmid_reset(struct mod_vmid *mod_vmid) |
127 | { |
128 | struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); |
129 | |
130 | core_vmid->num_vmids_available = core_vmid->num_vmid - 1; |
131 | memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID); |
132 | } |
133 | |
134 | struct mod_vmid *mod_vmid_create( |
135 | struct dc *dc, |
136 | unsigned int num_vmid, |
137 | struct dc_virtual_addr_space_config *va_config) |
138 | { |
139 | struct core_vmid *core_vmid; |
140 | |
141 | if (num_vmid <= 1) |
142 | goto fail_no_vm_ctx; |
143 | |
144 | if (dc == NULL) |
145 | goto fail_dc_null; |
146 | |
147 | core_vmid = kzalloc(size: sizeof(struct core_vmid), GFP_KERNEL); |
148 | |
149 | if (core_vmid == NULL) |
150 | goto fail_alloc_context; |
151 | |
152 | core_vmid->dc = dc; |
153 | core_vmid->num_vmid = num_vmid; |
154 | core_vmid->num_vmids_available = num_vmid - 1; |
155 | core_vmid->base_config = *va_config; |
156 | |
157 | memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID); |
158 | |
159 | return &core_vmid->public; |
160 | |
161 | fail_no_vm_ctx: |
162 | fail_alloc_context: |
163 | fail_dc_null: |
164 | return NULL; |
165 | } |
166 | |
167 | void mod_vmid_destroy(struct mod_vmid *mod_vmid) |
168 | { |
169 | if (mod_vmid != NULL) { |
170 | struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); |
171 | |
172 | kfree(objp: core_vmid); |
173 | } |
174 | } |
175 | |