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