xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/amd/display/modules/vmid/vmid.c (revision 41ec02673d281bbb3d38e6c78504ce6e30c228c1)
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 
add_ptb_to_table(struct core_vmid * core_vmid,unsigned int vmid,uint64_t ptb)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 
clear_entry_from_vmid_table(struct core_vmid * core_vmid,unsigned int vmid)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 
evict_vmids(struct core_vmid * core_vmid)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
get_existing_vmid_for_ptb(struct core_vmid * core_vmid,uint64_t ptb)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
get_next_available_vmid(struct core_vmid * core_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 
mod_vmid_get_for_ptb(struct mod_vmid * mod_vmid,uint64_t ptb)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 
mod_vmid_reset(struct mod_vmid * mod_vmid)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 
mod_vmid_create(struct dc * dc,unsigned int num_vmid,struct dc_virtual_addr_space_config * va_config)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 
mod_vmid_destroy(struct mod_vmid * mod_vmid)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