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 int ord_int = dc_get_vmid_use_vector(core_vmid->dc); 61 62 ASSERT(ord_int >= 0 && ord_int <= 0xFFFF); 63 uint16_t ord = (uint16_t)ord_int; 64 65 // At this point any positions with value 0 are unused vmids, evict them 66 for (i = 1; i < core_vmid->num_vmid; i++) { 67 if (!(ord & (1u << i))) 68 clear_entry_from_vmid_table(core_vmid, i); 69 } 70 } 71 72 // Return value of -1 indicates vmid table uninitialized or ptb dne in the table 73 static int get_existing_vmid_for_ptb(struct core_vmid *core_vmid, uint64_t ptb) 74 { 75 int i; 76 77 for (i = 0; i < core_vmid->num_vmid; i++) { 78 if (core_vmid->ptb_assigned_to_vmid[i] == ptb) 79 return i; 80 } 81 82 return -1; 83 } 84 85 // Expected to be called only when there's an available vmid 86 static int get_next_available_vmid(struct core_vmid *core_vmid) 87 { 88 int i; 89 90 for (i = 1; i < core_vmid->num_vmid; i++) { 91 if (core_vmid->ptb_assigned_to_vmid[i] == 0) 92 return i; 93 } 94 95 return -1; 96 } 97 98 uint8_t mod_vmid_get_for_ptb(struct mod_vmid *mod_vmid, uint64_t ptb) 99 { 100 struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); 101 int vmid = 0; 102 103 // Physical address gets vmid 0 104 if (ptb == 0) 105 return 0; 106 107 vmid = get_existing_vmid_for_ptb(core_vmid, ptb); 108 109 if (vmid == -1) { 110 struct dc_virtual_addr_space_config va_config = core_vmid->base_config; 111 112 va_config.page_table_base_addr = ptb; 113 114 if (core_vmid->num_vmids_available == 0) 115 evict_vmids(core_vmid); 116 117 vmid = get_next_available_vmid(core_vmid); 118 if (vmid != -1) { 119 add_ptb_to_table(core_vmid, vmid, ptb); 120 121 dc_setup_vm_context(core_vmid->dc, &va_config, vmid); 122 } else 123 ASSERT(0); 124 } 125 126 ASSERT(vmid >= 0 && vmid <= 0xFF); 127 return (uint8_t)vmid; 128 } 129 130 void mod_vmid_reset(struct mod_vmid *mod_vmid) 131 { 132 struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); 133 134 core_vmid->num_vmids_available = core_vmid->num_vmid - 1; 135 memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID); 136 } 137 138 struct mod_vmid *mod_vmid_create( 139 struct dc *dc, 140 unsigned int num_vmid, 141 struct dc_virtual_addr_space_config *va_config) 142 { 143 struct core_vmid *core_vmid; 144 145 if (num_vmid <= 1) 146 goto fail_no_vm_ctx; 147 148 if (dc == NULL) 149 goto fail_dc_null; 150 151 core_vmid = kzalloc_obj(struct core_vmid); 152 153 if (core_vmid == NULL) 154 goto fail_alloc_context; 155 156 core_vmid->dc = dc; 157 core_vmid->num_vmid = num_vmid; 158 core_vmid->num_vmids_available = num_vmid - 1; 159 core_vmid->base_config = *va_config; 160 161 memset(core_vmid->ptb_assigned_to_vmid, 0, sizeof(core_vmid->ptb_assigned_to_vmid[0]) * MAX_VMID); 162 163 return &core_vmid->public; 164 165 fail_no_vm_ctx: 166 fail_alloc_context: 167 fail_dc_null: 168 return NULL; 169 } 170 171 void mod_vmid_destroy(struct mod_vmid *mod_vmid) 172 { 173 if (mod_vmid != NULL) { 174 struct core_vmid *core_vmid = MOD_VMID_TO_CORE(mod_vmid); 175 176 kfree(core_vmid); 177 } 178 } 179