summaryrefslogtreecommitdiffstats
path: root/kernel/dynamic_rpc_dispatcher.c
blob: ca4268ddbfbce9deabdf8f87deba00cba9b92cdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
/*
 * Dynamic RPC Dispatcher - Version-aware RPC dispatch
 * Uses discovered firmware addresses for native handlers,
 * falls back to emulation for missing handlers
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/list.h>
#include <linux/errno.h>

#include "dynamic_rpc_dispatcher.h"
#include "gsp_rpc_client.h"
#include "firmware_analyzer.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Open-IOV");
MODULE_DESCRIPTION("Dynamic RPC Dispatcher");

/* Dispatcher context */
struct dynamic_dispatcher {
    struct rpc_interface *current_interface;
    struct fw_analysis_result fw_analysis;
    spinlock_t lock;
    bool initialized;
};

static struct dynamic_dispatcher g_dispatcher = {
    .lock = __SPIN_LOCK_UNLOCKED(g_dispatcher.lock),
    .initialized = false,
};

/* Version 570 interface (native firmware handlers) */
static int rpc_570_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
                         u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
{
    return gsp_rpc_alloc(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
}

static int rpc_570_free(struct gsp_client *client, u64 handle)
{
    return gsp_rpc_free(client, handle);
}

static int rpc_570_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
                       u32 prot, u32 flags, u64 offset, u64 *mapped_va)
{
    return gsp_rpc_map(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
}

static int rpc_570_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
{
    return gsp_rpc_unmap(client, handle, gpu_va, size);
}

static int rpc_570_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
                       u32 flags, u64 *dma_addr, u32 *num_pages)
{
    return gsp_rpc_pin(client, handle, offset, size, flags, dma_addr, num_pages);
}

static int rpc_570_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
{
    return gsp_rpc_unpin(client, handle, offset, size);
}

static int rpc_570_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
                            u32 priority, u32 flags)
{
    return gsp_rpc_schedule(client, work_handle, callback, arg, priority, flags);
}

static int rpc_570_yield(struct gsp_client *client)
{
    return gsp_rpc_yield(client);
}

static int rpc_570_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
                            u64 value, u32 bar_num, u64 *ret_value)
{
    return gsp_rpc_bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
}

static int rpc_570_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
{
    return gsp_rpc_migrate_save(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
}

static int rpc_570_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                   u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
{
    return gsp_rpc_migrate_restore(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
}

static int rpc_570_notify_register(struct gsp_client *client, u32 type,
                                   void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
                                   void *ctx)
{
    return gsp_rpc_notify_register(client, type, callback, ctx);
}

static void rpc_570_notify_unregister(struct gsp_client *client, u32 type)
{
    gsp_rpc_notify_unregister(client, type);
}

static struct rpc_interface interface_570 = {
    .version = "570.144W",
    .alloc = rpc_570_alloc,
    .free = rpc_570_free,
    .map = rpc_570_map,
    .unmap = rpc_570_unmap,
    .pin = rpc_570_pin,
    .unpin = rpc_570_unpin,
    .schedule = rpc_570_schedule,
    .yield = rpc_570_yield,
    .bar_trap = rpc_570_bar_trap,
    .migrate_save = rpc_570_migrate_save,
    .migrate_restore = rpc_570_migrate_restore,
    .notify_register = rpc_570_notify_register,
    .notify_unregister = rpc_570_notify_unregister,
    .use_emulation = {0},
};

/* Emulation interface (fallback) */
static int rpc_emu_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
                         u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
{
    return gsp_rpc_alloc_emul(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
}

static int rpc_emu_free(struct gsp_client *client, u64 handle)
{
    return gsp_rpc_free_emul(client, handle);
}

static int rpc_emu_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
                       u32 prot, u32 flags, u64 offset, u64 *mapped_va)
{
    return gsp_rpc_map_emul(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
}

static int rpc_emu_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
{
    return gsp_rpc_unmap_emul(client, handle, gpu_va, size);
}

static int rpc_emu_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
                       u32 flags, u64 *dma_addr, u32 *num_pages)
{
    return gsp_rpc_pin_emul(client, handle, offset, size, flags, dma_addr, num_pages);
}

static int rpc_emu_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
{
    return gsp_rpc_unpin_emul(client, handle, offset, size);
}

static int rpc_emu_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
                            u32 priority, u32 flags)
{
    return 0; /* Emulation always succeeds */
}

static int rpc_emu_yield(struct gsp_client *client)
{
    return 0;
}

static int rpc_emu_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
                            u64 value, u32 bar_num, u64 *ret_value)
{
    return gsp_rpc_bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
}

static int rpc_emu_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
{
    return 0;
}

static int rpc_emu_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                   u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
{
    return 0;
}

static int rpc_emu_notify_register(struct gsp_client *client, u32 type,
                                   void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
                                   void *ctx)
{
    return gsp_rpc_notify_register(client, type, callback, ctx);
}

static void rpc_emu_notify_unregister(struct gsp_client *client, u32 type)
{
    gsp_rpc_notify_unregister(client, type);
}

static struct rpc_interface interface_emu = {
    .version = "emulation",
    .alloc = rpc_emu_alloc,
    .free = rpc_emu_free,
    .map = rpc_emu_map,
    .unmap = rpc_emu_unmap,
    .pin = rpc_emu_pin,
    .unpin = rpc_emu_unpin,
    .schedule = rpc_emu_schedule,
    .yield = rpc_emu_yield,
    .bar_trap = rpc_emu_bar_trap,
    .migrate_save = rpc_emu_migrate_save,
    .migrate_restore = rpc_emu_migrate_restore,
    .notify_register = rpc_emu_notify_register,
    .notify_unregister = rpc_emu_notify_unregister,
    .use_emulation = {1},
};

int dynamic_dispatcher_init(const char *fw_path)
{
    struct fw_analysis_result result;
    int ret;

    spin_lock(&g_dispatcher.lock);

    if (g_dispatcher.initialized) {
        spin_unlock(&g_dispatcher.lock);
        return 0;
    }

    /* Analyze firmware */
    ret = fw_analyzer_load_and_analyze(fw_path, &result);
    if (ret) {
        pr_warn("DYN_DISPATCH: Firmware analysis failed, using emulation: %d\n", ret);
        g_dispatcher.current_interface = &interface_emu;
        g_dispatcher.initialized = true;
        spin_unlock(&g_dispatcher.lock);
        return 0;
    }

    g_dispatcher.fw_analysis = result;

    /* Select interface based on firmware version */
    if (result.matched && strcmp(result.version, "570.144W") == 0) {
        interface_570.fw_addrs = result.addresses;
        g_dispatcher.current_interface = &interface_570;
        pr_info("DYN_DISPATCH: Using native 570.144W interface\n");
    } else {
        g_dispatcher.current_interface = &interface_emu;
        pr_info("DYN_DISPATCH: Using emulation interface (version: %s)\n", result.version);
    }

    g_dispatcher.initialized = true;
    spin_unlock(&g_dispatcher.lock);

    fw_analyzer_print_result(&result);
    return 0;
}

void dynamic_dispatcher_fini(void)
{
    spin_lock(&g_dispatcher.lock);
    g_dispatcher.initialized = false;
    g_dispatcher.current_interface = NULL;
    spin_unlock(&g_dispatcher.lock);
}

struct rpc_interface *dynamic_dispatcher_get_interface(void)
{
    struct rpc_interface *iface;

    spin_lock(&g_dispatcher.lock);
    iface = g_dispatcher.current_interface;
    spin_unlock(&g_dispatcher.lock);

    return iface;
}

bool dynamic_dispatcher_use_emulation(enum gsp_rpc_type type)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface)
        return true;

    if (type >= GSP_RPC_MAX_TYPE)
        return true;

    return iface->use_emulation[type];
}

int dynamic_dispatcher_alloc(struct gsp_client *client, u64 size, u64 alignment, u32 mem_type,
                             u32 flags, u64 addr_hint, u32 pid, u64 *gpu_va, u64 *handle, u64 *actual_size)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->alloc)
        return -ENODEV;
    return iface->alloc(client, size, alignment, mem_type, flags, addr_hint, pid, gpu_va, handle, actual_size);
}

int dynamic_dispatcher_free(struct gsp_client *client, u64 handle)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->free)
        return -ENODEV;
    return iface->free(client, handle);
}

int dynamic_dispatcher_map(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size,
                           u32 prot, u32 flags, u64 offset, u64 *mapped_va)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->map)
        return -ENODEV;
    return iface->map(client, handle, gpu_va, size, prot, flags, offset, mapped_va);
}

int dynamic_dispatcher_unmap(struct gsp_client *client, u64 handle, u64 gpu_va, u64 size)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->unmap)
        return -ENODEV;
    return iface->unmap(client, handle, gpu_va, size);
}

int dynamic_dispatcher_pin(struct gsp_client *client, u64 handle, u64 offset, u64 size,
                           u32 flags, u64 *dma_addr, u32 *num_pages)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->pin)
        return -ENODEV;
    return iface->pin(client, handle, offset, size, flags, dma_addr, num_pages);
}

int dynamic_dispatcher_unpin(struct gsp_client *client, u64 handle, u64 offset, u64 size)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->unpin)
        return -ENODEV;
    return iface->unpin(client, handle, offset, size);
}

int dynamic_dispatcher_schedule(struct gsp_client *client, u64 work_handle, u64 callback, u64 arg,
                                u32 priority, u32 flags)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->schedule)
        return -ENODEV;
    return iface->schedule(client, work_handle, callback, arg, priority, flags);
}

int dynamic_dispatcher_yield(struct gsp_client *client)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->yield)
        return -ENODEV;
    return iface->yield(client);
}

int dynamic_dispatcher_bar_trap(struct gsp_client *client, u64 bar_addr, u32 access_type, u32 size,
                                u64 value, u32 bar_num, u64 *ret_value)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->bar_trap)
        return -ENODEV;
    return iface->bar_trap(client, bar_addr, access_type, size, value, bar_num, ret_value);
}

int dynamic_dispatcher_migrate_save(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                    u64 memory_bitmap, u64 page_table_ptr, u64 *checkpoint)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->migrate_save)
        return -ENODEV;
    return iface->migrate_save(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
}

int dynamic_dispatcher_migrate_restore(struct gsp_client *client, u64 src_gpu_id, u64 dst_gpu_id,
                                       u64 memory_bitmap, u64 page_table_ptr, u64 checkpoint)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->migrate_restore)
        return -ENODEV;
    return iface->migrate_restore(client, src_gpu_id, dst_gpu_id, memory_bitmap, page_table_ptr, checkpoint);
}

int dynamic_dispatcher_notify_register(struct gsp_client *client, u32 type,
                                       void (*callback)(struct gsp_client *, struct gsp_rpc_response *, void *),
                                       void *ctx)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (!iface || !iface->notify_register)
        return -ENODEV;
    return iface->notify_register(client, type, callback, ctx);
}

void dynamic_dispatcher_notify_unregister(struct gsp_client *client, u32 type)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    if (iface && iface->notify_unregister)
        iface->notify_unregister(client, type);
}

const char *dynamic_dispatcher_get_version(void)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    return iface ? iface->version : "unknown";
}

const struct fw_addresses *dynamic_dispatcher_get_fw_addresses(void)
{
    struct rpc_interface *iface = dynamic_dispatcher_get_interface();
    return iface ? &iface->fw_addrs : NULL;
}

EXPORT_SYMBOL_GPL(dynamic_dispatcher_init);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_fini);
// EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_interface); // Internal function, not exported
EXPORT_SYMBOL_GPL(dynamic_dispatcher_use_emulation);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_alloc);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_free);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_map);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_unmap);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_pin);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_unpin);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_schedule);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_yield);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_bar_trap);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_migrate_save);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_migrate_restore);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_notify_register);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_notify_unregister);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_version);
EXPORT_SYMBOL_GPL(dynamic_dispatcher_get_fw_addresses);

MODULE_DESCRIPTION("Dynamic RPC Dispatcher");
MODULE_LICENSE("GPL");