summaryrefslogtreecommitdiff
path: root/gxp-vd.h
blob: f9f9badb885c22fd6f4efdfa2e5c8d728887bf24 (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
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * GXP virtual device manager.
 *
 * Copyright (C) 2021-2022 Google LLC
 */

#ifndef __GXP_VD_H__
#define __GXP_VD_H__

#include <linux/iommu.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/rbtree.h>
#include <linux/refcount.h>
#include <linux/rwsem.h>
#include <linux/scatterlist.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/wait.h>

#include <gcip/gcip-image-config.h>

#include "gxp-host-device-structs.h"
#include "gxp-internal.h"
#include "gxp-mapping.h"

/* TODO(b/259192112): set to 8 once the runtime has added the credit limit. */
#define GXP_COMMAND_CREDIT_PER_VD 256

/* A special client ID for secure workloads pre-agreed with MCU firmware. */
#define SECURE_CLIENT_ID (3 << 10)

struct mailbox_resp_queue {
	/* Queue of waiting async responses */
	struct list_head wait_queue;
	/* Queue of arrived async responses */
	struct list_head dest_queue;
	/* Lock protecting access to the `queue` */
	spinlock_t lock;
	/* Waitqueue to wait on if the queue is empty */
	wait_queue_head_t waitq;
	/*
	 * If true, the user cannot send requests anymore.
	 * This must be protected by @lock.
	 */
	bool wait_queue_closed;
};

enum gxp_virtual_device_state {
	GXP_VD_OFF,
	GXP_VD_READY,
	GXP_VD_RUNNING,
	GXP_VD_SUSPENDED,
	/*
	 * If the virtual device is in the unavailable state, it won't be changed
	 * back no matter what we do.
	 * Note: this state will only be set on suspend/resume failure.
	 */
	GXP_VD_UNAVAILABLE,
	/*
	 * gxp_vd_release() has been called. VD with this state means it's
	 * waiting for the last reference to be put(). All fields in VD is
	 * invalid in this state.
	 */
	GXP_VD_RELEASED,
};

struct gxp_virtual_device {
	struct gxp_dev *gxp;
	uint num_cores;
	void *fw_app;
	struct gcip_iommu_domain *domain;
	struct mailbox_resp_queue *mailbox_resp_queues;
	struct rb_root mappings_root;
	struct rw_semaphore mappings_semaphore;
	/* Used to save doorbell state on VD resume. */
	uint doorbells_state[GXP_NUM_DOORBELLS_PER_VD];
	enum gxp_virtual_device_state state;
	/*
	 * Record the gxp->power_mgr->blk_switch_count when the vd was
	 * suspended. Use this information to know whether the block has been
	 * restarted and therefore we need to re-program CSRs in the resume
	 * process.
	 */
	u64 blk_switch_count_when_suspended;
	/*
	 * @domain of each virtual device will map a slice of shared buffer. It stores which index
	 * of slice is used by this VD.
	 */
	int slice_index;
	/*
	 * The SG table that holds the regions specified in the image config's
	 * non-secure IOMMU mappings.
	 */
	struct {
		dma_addr_t daddr;
		struct sg_table *sgt;
	} ns_regions[GCIP_IMG_CFG_MAX_NS_IOMMU_MAPPINGS];
	/*
	 * The config regions specified in image config.
	 * core_cfg's size should be a multiple of GXP_NUM_CORES.
	 */
	struct gxp_mapped_resource core_cfg, vd_cfg, sys_cfg;
	uint core_list;
	/*
	 * The ID of DSP client. -1 if it is not allocated.
	 * This is allocated by the DSP kernel driver, but will be set to this variable only when
	 * the client of this vd acquires the block wakelock successfully. (i.e, after the kernel
	 * driver allocates a virtual mailbox with the firmware side successfully by sending the
	 * `allocate_vmbox` KCI command.)
	 */
	int client_id;
	/*
	 * The ID of TPU client. -1 if it is not allocated.
	 * This ID will be fetched from the TPU kernel driver.
	 */
	int tpu_client_id;
	/* Whether DSP KD sent `link_offload_vmbox` KCI successfully to MCU FW or not. */
	bool tpu_linked;
	/*
	 * Protects credit. Use a spin lock because the critical section of
	 * using @credit is pretty small.
	 */
	spinlock_t credit_lock;
	/*
	 * Credits for sending mailbox commands. It's initialized as
	 * GXP_COMMAND_CREDIT_PER_VD. The value is decreased on sending
	 * mailbox commands; increased on receiving mailbox responses.
	 * Mailbox command requests are rejected when this value reaches 0.
	 *
	 * Only used in MCU mode.
	 */
	uint credit;
	/* Whether it's the first time allocating a VMBox for this VD. */
	bool first_open;
	bool is_secure;
	refcount_t refcount;
	/* A constant ID assigned after VD is allocated. For debug only. */
	int vdid;
	struct gcip_image_config_parser cfg_parser;
	/* Protects @dma_fence_list. */
	struct mutex fence_list_lock;
	/* List of GXP DMA fences owned by this VD. */
	struct list_head gxp_fence_list;
	/* Protects changing the state of vd while generating a debug dump. */
	struct mutex debug_dump_lock;
	/* An eventfd which will be triggered when this vd is invalidated. */
	struct gxp_eventfd *invalidate_eventfd;
	/*
	 * If true, the MCU FW communicating with this VD has been crashed and it must not work
	 * with any MCU FW anymore regardless of its state.
	 */
	bool mcu_crashed;
};

/*
 * Initializes the device management subsystem and allocates resources for it.
 * This is expected to be called once per driver lifecycle.
 */
void gxp_vd_init(struct gxp_dev *gxp);

/*
 * Tears down the device management subsystem.
 * This is expected to be called once per driver lifecycle.
 */
void gxp_vd_destroy(struct gxp_dev *gxp);

/**
 * gxp_vd_allocate() - Allocate and initialize a struct gxp_virtual_device
 * @gxp: The GXP device the virtual device will belong to
 * @requested_cores: The number of cores the virtual device will have
 *
 * The state of VD is initialized to GXP_VD_OFF.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * Return: The virtual address of the virtual device or an ERR_PTR on failure
 * * -EINVAL - The number of requested cores was invalid
 * * -ENOMEM - Unable to allocate the virtual device
 * * -EBUSY  - Not enough iommu domains available or insufficient physical
 *	       cores to be assigned to @vd
 * * -ENOSPC - There is no more available shared slices
 */
struct gxp_virtual_device *gxp_vd_allocate(struct gxp_dev *gxp,
					   u16 requested_cores);

/**
 * gxp_vd_release() - Cleanup a struct gxp_virtual_device
 * @vd: The virtual device to be released
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * A virtual device must be stopped before it can be released.
 *
 * If @vd's reference count is 1 before this call, this function frees @vd.
 * Otherwise @vd's state is set to GXP_VD_RELEASED.
 */
void gxp_vd_release(struct gxp_virtual_device *vd);

/**
 * gxp_vd_run() - Run a virtual device on physical cores
 * @vd: The virtual device to run
 *
 * The state of @vd should be GXP_VD_OFF or GXP_VD_READY before calling this
 * function. If this function runs successfully, the state becomes
 * GXP_VD_RUNNING. Otherwise, it would be GXP_VD_UNAVAILABLE.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * Return:
 * * 0         - Success
 * * -EINVAL   - The VD is not in GXP_VD_READY state
 * * Otherwise - Errno returned by firmware running
 */
int gxp_vd_run(struct gxp_virtual_device *vd);

/**
 * gxp_vd_stop() - Stop a running virtual device
 * @vd: The virtual device to stop
 *
 * The state of @vd will be GXP_VD_OFF.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 */
void gxp_vd_stop(struct gxp_virtual_device *vd);

/*
 * Returns the physical core ID for the specified virtual_core belonging to
 * this virtual device or -EINVAL if this virtual core is not running on a
 * physical core.
 *
 * The caller must have locked gxp->vd_semaphore for reading.
 */
int gxp_vd_virt_core_to_phys_core(struct gxp_virtual_device *vd, u16 virt_core);

/**
 * gxp_vd_phys_core_to_virt_core() -Returns the virtual core ID for the specified
 *                                  @phys_core belonging to this virtual device.
 * @vd: The virtual device for which virtual core ID is requested for.
 * @phys_core: Physical core_id corresponding to which virtual core ID is requested.
 *
 * This function works only in direct mode. The caller must have locked
 * vd->debug_dump_lock before calling this function.
 *
 * Return:
 * * -EINVAL   - If no virtual core ID found for @phys_core or if the function
 *               was not invoked in direct mode.
 * * Otherwise - Returns the virtual core ID for the given @phys_core.
 */
int gxp_vd_phys_core_to_virt_core(struct gxp_virtual_device *vd, u32 phys_core);

/**
 * gxp_vd_mapping_store() - Store a mapping in a virtual device's records
 * @vd: The virtual device @map was created for and will be stored in
 * @map: The mapping to store
 *
 * Acquires a reference to @map if it was successfully stored
 *
 * Return:
 * * 0: Success
 * * -EINVAL: @map is already stored in @vd's records
 */
int gxp_vd_mapping_store(struct gxp_virtual_device *vd,
			 struct gxp_mapping *map);

/**
 * gxp_vd_mapping_remove() - Remove a mapping from a virtual device's records
 * @vd: The VD to remove @map from
 * @map: The mapping to remove
 *
 * Releases a reference to @map if it was successfully removed
 */
void gxp_vd_mapping_remove(struct gxp_virtual_device *vd,
			   struct gxp_mapping *map);

/**
 * gxp_vd_mapping_search() - Obtain a reference to the mapping starting at the
 *                           specified device address
 * @vd: The virtual device to search for the mapping
 * @device_address: The starting device address of the mapping to find
 *
 * Obtains a reference to the returned mapping
 *
 * Return: A pointer to the mapping if found; NULL otherwise
 */
struct gxp_mapping *gxp_vd_mapping_search(struct gxp_virtual_device *vd,
					  dma_addr_t device_address);

/**
 * gxp_vd_mapping_search_in_range() - Obtain a reference to the mapping which
 *                                    contains the specified device address
 * @vd: The virtual device to search for the mapping
 * @device_address: A device address contained in the buffer the mapping to
 *                  find describes.
 *
 * Obtains a reference to the returned mapping
 *
 * Return: A pointer to the mapping if found; NULL otherwise
 */
struct gxp_mapping *
gxp_vd_mapping_search_in_range(struct gxp_virtual_device *vd,
			       dma_addr_t device_address);

/**
 * gxp_vd_mapping_search_host() - Obtain a reference to the mapping starting at
 *                                the specified user-space address
 * @vd: The virtual device to search for the mapping
 * @host_address: The starting user-space address of the mapping to find
 *
 * Obtains a reference to the returned mapping
 *
 * Return: A pointer to the mapping if found; NULL otherwise
 */
struct gxp_mapping *gxp_vd_mapping_search_host(struct gxp_virtual_device *vd,
					       u64 host_address);

/**
 * gxp_vd_suspend() - Suspend a running virtual device
 * @vd: The virtual device to suspend
 *
 * The state of @vd should be GXP_VD_RUNNING before calling this function.
 * If the suspension runs successfully on all cores, the state becomes
 * GXP_VD_SUSPENDED. Otherwise, it would be GXP_VD_UNAVAILABLE.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 */
void gxp_vd_suspend(struct gxp_virtual_device *vd);

/**
 * gxp_vd_resume() - Resume a suspended virtual device
 * @vd: The virtual device to resume
 *
 * The state of @vd should be GXP_VD_SUSPENDED before calling this function.
 * If the resumption runs successfully on all cores, the state becomes
 * GXP_VD_RUNNING. Otherwise, it would be GXP_VD_UNAVAILABLE.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * Return:
 * * 0          - Success
 * * -ETIMEDOUT - Fail to power on physical cores
 */
int gxp_vd_resume(struct gxp_virtual_device *vd);

/**
 * gxp_vd_block_ready() - This is called after the block wakelock is acquired.
 * Does required setup for serving VD such as attaching its IOMMU domain.
 *
 * @vd: The virtual device to prepare the resources
 *
 * The state of @vd should be GXP_VD_OFF before calling this function.
 * If this function runs successfully, the state becomes GXP_VD_READY.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * Return:
 * * 0          - Success
 * * -EINVAL    - The VD is not in GXP_VD_OFF state
 * * Otherwise  - Errno returned by IOMMU domain attachment
 */
int gxp_vd_block_ready(struct gxp_virtual_device *vd);

/**
 * gxp_vd_block_unready() - This is called before the block wakelock is going to be released.
 *
 * @vd: The virtual device to release the resources
 *
 * This function must be called only when the client holds the block wakelock and allocated a
 * virtual device. It doesn't have a dependency on the state of @vd, but also doesn't change the
 * state in normal situation. However, if an unexpected error happens, the state can be changed
 * to GXP_VD_UNAVAILABLE.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 */
void gxp_vd_block_unready(struct gxp_virtual_device *vd);

/*
 * Checks whether the virtual device has a positive credit, and use 1 credit when
 * yes.
 *
 * Returns true when there is enough credit, false otherwise.
 */
bool gxp_vd_has_and_use_credit(struct gxp_virtual_device *vd);
/*
 * Releases the credit.
 */
void gxp_vd_release_credit(struct gxp_virtual_device *vd);

/* Increases reference count of @vd by one and returns @vd. */
static inline struct gxp_virtual_device *
gxp_vd_get(struct gxp_virtual_device *vd)
{
	WARN_ON_ONCE(!refcount_inc_not_zero(&vd->refcount));
	return vd;
}

/*
 * Decreases reference count of @vd by one.
 *
 * If @vd->refcount becomes 0, @vd will be freed.
 */
void gxp_vd_put(struct gxp_virtual_device *vd);

/*
 * Change the status of the vd of @client_id to GXP_VD_UNAVAILABLE.
 * Internally, it will discard all pending/unconsumed user commands and call the
 * `gxp_vd_block_unready` function.
 *
 * This function will be called when the `CLIENT_FATAL_ERROR_NOTIFY` RKCI has been sent from the
 * firmware side.
 *
 * @gxp: The GXP device to obtain the handler for
 * @client_id: client_id of the crashed vd.
 * @core_list: A bitfield enumerating the physical cores on which crash is reported from firmware.
 * @release_vmbox: Releases the vmbox of the vd after invalidating it.
 */
void gxp_vd_invalidate_with_client_id(struct gxp_dev *gxp, int client_id, uint core_list,
				      bool release_vmbox);

/*
 * Changes the status of the @vd to GXP_VD_UNAVAILABLE.
 * Internally, it will discard all pending/unconsumed user commands.
 *
 * This function will be called when some unexpected errors happened and cannot proceed requests
 * anymore with this @vd.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * @gxp: The GXP device to obtain the handler for.
 * @vd: The virtual device to be invaliated.
 */
void gxp_vd_invalidate(struct gxp_dev *gxp, struct gxp_virtual_device *vd);

/*
 * Generates a debug dump of @vd which utilizes @core_list cores.
 *
 * This function is usually called in the MCU mode that the kernel driver cannot decide which cores
 * will be used by @vd.
 *
 * The caller must have locked gxp->vd_semaphore for writing.
 *
 * @gxp: The GXP device to obtain the handler for.
 * @vd: The virtual device to be dumped.
 * @core_list: A bitfield enumerating the physical cores on which crash is reported from firmware.
 */
void gxp_vd_generate_debug_dump(struct gxp_dev *gxp,
				struct gxp_virtual_device *vd, uint core_list);

#if GXP_HAS_MCU
/*
 * Releases the vmbox which is allocated to @vd.
 *
 * This function will call the `RELEASE_VMBOX` KCI and will always set @vd->client_id to -1. If the
 * vmbox was linked to the offload vmbox, it will also call the `gxp_vd_unlink_offload_vmbox`
 * function first internally.
 *
 * @gxp: The GXP device to obtain the handler for.
 * @vd: The virtual device to release its vmbox.
 */
void gxp_vd_release_vmbox(struct gxp_dev *gxp, struct gxp_virtual_device *vd);

/*
 * Unlinks the linkage of the vmbox of @vd to the offload chip vmbox.
 *
 * This function will call the `UNLINK_OFFLOAD_VMBOX` KCI to unlink the vmboxes and will always set
 * @vd->tpu_client_id to -1.
 *
 * @gxp: The GXP device to obtain the handler for.
 * @vd: The virtual device to unlink vmboxes.
 * @offload_client_id: The client ID of the offload chip.
 * @offload_chip_type: The type of the offload chip. (See enum gcip_kci_offload_chip_type.)
 */
void gxp_vd_unlink_offload_vmbox(struct gxp_dev *gxp, struct gxp_virtual_device *vd,
				 u32 offload_client_id, u8 offload_chip_type);
#else /* !GXP_HAS_MCU */
#define gxp_vd_release_vmbox(...)
#define gxp_vd_unlink_offload_vmbox(...)
#endif /* GXP_HAS_MCU */

/*
 * An ID between 0~GXP_NUM_CORES-1 and is unique to each VD.
 * Only used in direct mode.
 */
static inline uint gxp_vd_hw_slot_id(struct gxp_virtual_device *vd)
{
	return ffs(vd->core_list) - 1;
}

#endif /* __GXP_VD_H__ */