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(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, 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(core_vmid->dc, &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(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(core_vmid); 173 } 174 } 175