summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/nvgpu/include/nvgpu/posix/io.h114
-rw-r--r--drivers/gpu/nvgpu/os/posix/io.c316
-rw-r--r--drivers/gpu/nvgpu/os/posix/os_posix.h26
3 files changed, 440 insertions, 16 deletions
diff --git a/drivers/gpu/nvgpu/include/nvgpu/posix/io.h b/drivers/gpu/nvgpu/include/nvgpu/posix/io.h
new file mode 100644
index 00000000..98be4d00
--- /dev/null
+++ b/drivers/gpu/nvgpu/include/nvgpu/posix/io.h
@@ -0,0 +1,114 @@
1/*
2 * Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#ifndef NVGPU_POSIX_IO_H
24#define NVGPU_POSIX_IO_H
25
26#include <nvgpu/types.h>
27#include <nvgpu/list.h>
28
29struct gk20a;
30
31/**
32 * Here lies the interface for a unit test module to interact with the nvgpu IO
33 * accessors. This interface provides the ability for a module to react to nvgpu
34 * calling nvgpu IO accessors so that nvgpu can handle various HW sequences even
35 * when run in unit testing mode.
36 *
37 * The primary interface is simply callbacks to the unit test module which the
38 * module can handle how ever it wishes.
39 */
40
41struct nvgpu_reg_access {
42 /*
43 * Address of the register write relative to the base of the register
44 * space. I.e you can compare this against values in the HW headers
45 * directly to check what register is being read/written to/from.
46 */
47 u32 addr;
48
49 /*
50 * Writes: this is the value being written.
51 * Reads: populate with the value to return.
52 */
53 u32 value;
54};
55
56struct nvgpu_posix_io_callbacks {
57 void (*writel)(struct gk20a *g, struct nvgpu_reg_access *access);
58 void (*writel_check)(struct gk20a *g, struct nvgpu_reg_access *access);
59 void (*__readl)(struct gk20a *g, struct nvgpu_reg_access *access);
60 void (*readl)(struct gk20a *g, struct nvgpu_reg_access *access);
61 void (*bar1_writel)(struct gk20a *g, struct nvgpu_reg_access *access);
62 void (*bar1_readl)(struct gk20a *g, struct nvgpu_reg_access *access);
63 void (*usermode_writel)(struct gk20a *g,
64 struct nvgpu_reg_access *access);
65};
66
67struct nvgpu_posix_io_callbacks *nvgpu_posix_register_io(
68 struct gk20a *g,
69 struct nvgpu_posix_io_callbacks *io_callbacks);
70
71struct nvgpu_posix_io_reg_space {
72 u32 base;
73 u32 size;
74 u32 *data;
75 struct nvgpu_list_node link;
76};
77
78static inline struct nvgpu_posix_io_reg_space *
79nvgpu_posix_io_reg_space_from_link(struct nvgpu_list_node *node)
80{
81 return (struct nvgpu_posix_io_reg_space *)
82 ((uintptr_t)node - offsetof(struct nvgpu_posix_io_reg_space, link));
83};
84
85void nvgpu_posix_io_init_reg_space(struct gk20a *g);
86int nvgpu_posix_io_get_error_code(struct gk20a *g);
87void nvgpu_posix_io_reset_error_code(struct gk20a *g);
88int nvgpu_posix_io_add_reg_space(struct gk20a *g, u32 base, u32 size);
89struct nvgpu_posix_io_reg_space *nvgpu_posix_io_get_reg_space(struct gk20a *g,
90 u32 addr);
91void nvgpu_posix_io_delete_reg_space(struct gk20a *g, u32 base);
92void nvgpu_posix_io_writel_reg_space(struct gk20a *g, u32 addr, u32 data);
93u32 nvgpu_posix_io_readl_reg_space(struct gk20a *g, u32 addr);
94
95struct nvgpu_posix_io_reg_access {
96 struct nvgpu_reg_access access;
97 struct nvgpu_list_node link;
98};
99
100static inline struct nvgpu_posix_io_reg_access *
101nvgpu_posix_io_reg_access_from_link(struct nvgpu_list_node *node)
102{
103 return (struct nvgpu_posix_io_reg_access *)
104 ((uintptr_t)node - offsetof(struct nvgpu_posix_io_reg_access, link));
105};
106
107void nvgpu_posix_io_start_recorder(struct gk20a *g);
108void nvgpu_posix_io_reset_recorder(struct gk20a *g);
109void nvgpu_posix_io_record_access(struct gk20a *g,
110 struct nvgpu_reg_access *access);
111bool nvgpu_posix_io_check_sequence(struct gk20a *g,
112 struct nvgpu_reg_access *sequence, u32 size, bool strict);
113
114#endif
diff --git a/drivers/gpu/nvgpu/os/posix/io.c b/drivers/gpu/nvgpu/os/posix/io.c
index 7bab8af6..8369e73f 100644
--- a/drivers/gpu/nvgpu/os/posix/io.c
+++ b/drivers/gpu/nvgpu/os/posix/io.c
@@ -24,15 +24,45 @@
24#include <nvgpu/io_usermode.h> 24#include <nvgpu/io_usermode.h>
25#include <nvgpu/bug.h> 25#include <nvgpu/bug.h>
26 26
27#include <nvgpu/posix/io.h>
28
29#include "os_posix.h"
30
31
27/* 32/*
28 * For now none of these make sense to execute in userspace. Eventually we 33 * This function sets the IO callbacks to the passed set of callbacks. It
29 * may want to use these to verify certain register read/write sequences 34 * returns the value of the old IO callback struct pointer. This function
30 * but for now, just hang. 35 * cannot fail.
36 *
37 * This is expected to be called from modules to set up their IO interaction.
31 */ 38 */
39struct nvgpu_posix_io_callbacks *nvgpu_posix_register_io(
40 struct gk20a *g,
41 struct nvgpu_posix_io_callbacks *io_callbacks)
42{
43 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
44 struct nvgpu_posix_io_callbacks *old_io = p->callbacks;
45
46 p->callbacks = io_callbacks;
47
48 return old_io;
49}
32 50
33void nvgpu_writel(struct gk20a *g, u32 r, u32 v) 51void nvgpu_writel(struct gk20a *g, u32 r, u32 v)
34{ 52{
35 BUG(); 53 struct nvgpu_posix_io_callbacks *callbacks =
54 nvgpu_os_posix_from_gk20a(g)->callbacks;
55
56 struct nvgpu_reg_access access = {
57 .addr = r,
58 .value = v
59 };
60
61 if (callbacks == NULL || callbacks->writel == NULL) {
62 BUG();
63 }
64
65 callbacks->writel(g, &access);
36} 66}
37 67
38void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v) 68void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v)
@@ -42,16 +72,21 @@ void nvgpu_writel_relaxed(struct gk20a *g, u32 r, u32 v)
42 72
43u32 nvgpu_readl(struct gk20a *g, u32 r) 73u32 nvgpu_readl(struct gk20a *g, u32 r)
44{ 74{
45 BUG(); 75 struct nvgpu_posix_io_callbacks *callbacks =
76 nvgpu_os_posix_from_gk20a(g)->callbacks;
46 77
47 return 0; 78 struct nvgpu_reg_access access = {
48} 79 .addr = r,
80 .value = 0L
81 };
49 82
50u32 __nvgpu_readl(struct gk20a *g, u32 r) 83 if (callbacks == NULL || callbacks->readl == NULL) {
51{ 84 BUG();
52 BUG(); 85 }
53 86
54 return 0; 87 callbacks->readl(g, &access);
88
89 return access.value;
55} 90}
56 91
57void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v) 92void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v)
@@ -59,16 +94,76 @@ void nvgpu_writel_loop(struct gk20a *g, u32 r, u32 v)
59 BUG(); 94 BUG();
60} 95}
61 96
97u32 __nvgpu_readl(struct gk20a *g, u32 r)
98{
99 struct nvgpu_posix_io_callbacks *callbacks =
100 nvgpu_os_posix_from_gk20a(g)->callbacks;
101
102 struct nvgpu_reg_access access = {
103 .addr = r,
104 .value = 0L
105 };
106
107 if (callbacks == NULL || callbacks->__readl == NULL) {
108 BUG();
109 }
110
111 callbacks->__readl(g, &access);
112
113 return access.value;
114}
115
62void nvgpu_bar1_writel(struct gk20a *g, u32 b, u32 v) 116void nvgpu_bar1_writel(struct gk20a *g, u32 b, u32 v)
63{ 117{
64 BUG(); 118 struct nvgpu_posix_io_callbacks *callbacks =
119 nvgpu_os_posix_from_gk20a(g)->callbacks;
120
121 struct nvgpu_reg_access access = {
122 .addr = b,
123 .value = v
124 };
125
126 if (callbacks == NULL || callbacks->bar1_writel == NULL) {
127 BUG();
128 }
129
130 callbacks->bar1_writel(g, &access);
65} 131}
66 132
67u32 nvgpu_bar1_readl(struct gk20a *g, u32 b) 133u32 nvgpu_bar1_readl(struct gk20a *g, u32 b)
68{ 134{
69 BUG(); 135 struct nvgpu_posix_io_callbacks *callbacks =
136 nvgpu_os_posix_from_gk20a(g)->callbacks;
70 137
71 return 0; 138 struct nvgpu_reg_access access = {
139 .addr = b,
140 .value = 0L
141 };
142
143 if (callbacks == NULL || callbacks->bar1_readl == NULL) {
144 BUG();
145 }
146
147 callbacks->bar1_readl(g, &access);
148
149 return access.value;
150}
151
152void nvgpu_usermode_writel(struct gk20a *g, u32 r, u32 v)
153{
154 struct nvgpu_posix_io_callbacks *callbacks =
155 nvgpu_os_posix_from_gk20a(g)->callbacks;
156
157 struct nvgpu_reg_access access = {
158 .addr = r,
159 .value = v
160 };
161
162 if (callbacks == NULL || callbacks->usermode_writel == NULL) {
163 BUG();
164 }
165
166 callbacks->usermode_writel(g, &access);
72} 167}
73 168
74bool nvgpu_io_exists(struct gk20a *g) 169bool nvgpu_io_exists(struct gk20a *g)
@@ -81,7 +176,196 @@ bool nvgpu_io_valid_reg(struct gk20a *g, u32 r)
81 return false; 176 return false;
82} 177}
83 178
84void nvgpu_usermode_writel(struct gk20a *g, u32 r, u32 v) 179void nvgpu_posix_io_init_reg_space(struct gk20a *g)
85{ 180{
86 BUG(); 181 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
182
183 p->recording = false;
184 p->error_code = 0;
185 nvgpu_init_list_node(&p->reg_space_head);
186 nvgpu_init_list_node(&p->recorder_head);
187}
188
189int nvgpu_posix_io_get_error_code(struct gk20a *g)
190{
191 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
192
193 return p->error_code;
194}
195
196void nvgpu_posix_io_reset_error_code(struct gk20a *g)
197{
198 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
199
200 p->error_code = 0;
201}
202
203/*
204 * Add a new register space to the list of spaces, defined by a base
205 * address and a size.
206 */
207int nvgpu_posix_io_add_reg_space(struct gk20a *g, u32 base, u32 size)
208{
209 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
210 struct nvgpu_posix_io_reg_space *new_reg_space =
211 nvgpu_kzalloc(g, sizeof(struct nvgpu_posix_io_reg_space));
212
213 if (new_reg_space == NULL) {
214 return -ENOMEM;
215 }
216
217 new_reg_space->base = base;
218 new_reg_space->size = size;
219
220 new_reg_space->data = nvgpu_vzalloc(g, size);
221 if (new_reg_space->data == NULL) {
222 return -ENOMEM;
223 }
224
225 nvgpu_list_add_tail(&new_reg_space->link, &p->reg_space_head);
226 return 0;
227}
228
229void nvgpu_posix_io_delete_reg_space(struct gk20a *g, u32 base)
230{
231 struct nvgpu_posix_io_reg_space *reg_space =
232 nvgpu_posix_io_get_reg_space(g, base);
233 if (reg_space == NULL) {
234 /* Invalid space, or already de-allocated */
235 return;
236 }
237 nvgpu_list_del(&reg_space->link);
238 nvgpu_vfree(g, reg_space->data);
239 nvgpu_kfree(g, reg_space);
240}
241
242/*
243 * Lookup a register space from a given address. If no register space is found
244 * this is a bug similar to a translation fault.
245 */
246struct nvgpu_posix_io_reg_space *nvgpu_posix_io_get_reg_space(struct gk20a *g,
247 u32 addr)
248{
249 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
250 struct nvgpu_posix_io_reg_space *reg_space;
251
252 nvgpu_list_for_each_entry(reg_space, &p->reg_space_head,
253 nvgpu_posix_io_reg_space, link) {
254 u32 offset = addr - reg_space->base;
255
256 if ((addr >= reg_space->base) && (offset <= reg_space->size)) {
257 return reg_space;
258 }
259 }
260 p->error_code = -EFAULT;
261 nvgpu_err(g, "ABORT for address 0x%x", addr);
262 return NULL;
263}
264
265void nvgpu_posix_io_writel_reg_space(struct gk20a *g, u32 addr, u32 data)
266{
267 struct nvgpu_posix_io_reg_space *space =
268 nvgpu_posix_io_get_reg_space(g, addr);
269
270 if (space != NULL) {
271 u32 offset = (addr - space->base) / ((u32) sizeof(u32));
272
273 *(space->data + offset) = data;
274 }
275}
276
277u32 nvgpu_posix_io_readl_reg_space(struct gk20a *g, u32 addr)
278{
279 struct nvgpu_posix_io_reg_space *space =
280 nvgpu_posix_io_get_reg_space(g, addr);
281
282 if (space != NULL) {
283 u32 offset = (addr - space->base) / ((u32) sizeof(u32));
284
285 return *(space->data + offset);
286 } else {
287 return 0;
288 }
289}
290
291/*
292 * Start recording register writes. If this function is called again,
293 * it will free all previously recorded events.
294 */
295void nvgpu_posix_io_start_recorder(struct gk20a *g)
296{
297 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
298 struct nvgpu_posix_io_reg_access *ptr;
299
300 /* If list already has events, delete them all */
301 if (p->recording == true) {
302 while (!nvgpu_list_empty(&p->recorder_head)) {
303 ptr = nvgpu_list_first_entry(&p->recorder_head,
304 nvgpu_posix_io_reg_access, link);
305 nvgpu_list_del(&ptr->link);
306 nvgpu_kfree(g, ptr);
307 }
308 }
309 p->recording = true;
310}
311
312void nvgpu_posix_io_record_access(struct gk20a *g,
313 struct nvgpu_reg_access *access)
314{
315 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
316
317 if (p->recording == true) {
318 struct nvgpu_posix_io_reg_access *new_event = nvgpu_kzalloc(g,
319 sizeof(struct nvgpu_posix_io_reg_access));
320 (void) memcpy(&(new_event->access), access,
321 sizeof(struct nvgpu_reg_access));
322 nvgpu_list_add_tail(&new_event->link, &p->recorder_head);
323 }
324}
325
326/*
327 * Take an array of accesses and compare to the recorded sequence. Returns true
328 * if the array matches the recorded sequence.
329 * If strict mode is false, this function allows extra accesses to be present
330 * in the recording.
331 */
332bool nvgpu_posix_io_check_sequence(struct gk20a *g,
333 struct nvgpu_reg_access *sequence, u32 size, bool strict)
334{
335 struct nvgpu_os_posix *p = nvgpu_os_posix_from_gk20a(g);
336 struct nvgpu_posix_io_reg_access *ptr;
337 u32 i = 0;
338
339 if (p->recording == false) {
340 return false;
341 }
342
343 nvgpu_list_for_each_entry(ptr, &p->recorder_head,
344 nvgpu_posix_io_reg_access, link) {
345 if ((sequence[i].addr == ptr->access.addr) &&
346 (sequence[i].value == ptr->access.value)) {
347 i++;
348 } else {
349 if (strict == true) {
350 return false;
351 }
352 }
353 }
354
355 if (i != size) {
356 /* Either missing or too many accesses */
357 return false;
358 }
359
360 if (&ptr->link == &p->recorder_head) {
361 /* Identical match */
362 return true;
363 }
364
365 /* Not an identical match */
366 if (strict) {
367 return false;
368 } else {
369 return true;
370 }
87} 371}
diff --git a/drivers/gpu/nvgpu/os/posix/os_posix.h b/drivers/gpu/nvgpu/os/posix/os_posix.h
index 955186ef..ff8c9817 100644
--- a/drivers/gpu/nvgpu/os/posix/os_posix.h
+++ b/drivers/gpu/nvgpu/os/posix/os_posix.h
@@ -25,8 +25,34 @@
25 25
26#include "gk20a/gk20a.h" 26#include "gk20a/gk20a.h"
27 27
28struct nvgpu_posix_io_callbacks;
29
28struct nvgpu_os_posix { 30struct nvgpu_os_posix {
29 struct gk20a g; 31 struct gk20a g;
32
33
34 /*
35 * IO callbacks for handling the nvgpu IO accessors.
36 */
37 struct nvgpu_posix_io_callbacks *callbacks;
38
39 /*
40 * Memory-mapped register space for unit tests.
41 */
42 struct nvgpu_list_node reg_space_head;
43 int error_code;
44
45
46 /*
47 * List to record sequence of register writes.
48 */
49 struct nvgpu_list_node recorder_head;
50 bool recording;
30}; 51};
31 52
53static inline struct nvgpu_os_posix *nvgpu_os_posix_from_gk20a(struct gk20a *g)
54{
55 return container_of(g, struct nvgpu_os_posix, g);
56}
57
32#endif 58#endif