Skip to content

Commit 30deb51

Browse files
jingzhangosMarc Zyngier
authored andcommitted
KVM: arm64: vgic-its: Add debugfs interface to expose ITS tables
This commit introduces a debugfs interface to display the contents of the VGIC Interrupt Translation Service (ITS) tables. The ITS tables map Device/Event IDs to Interrupt IDs and target processors. Exposing this information through debugfs allows for easier inspection and debugging of the interrupt routing configuration. The debugfs interface presents the ITS table data in a tabular format: Device ID: 0x0, Event ID Range: [0 - 31] EVENT_ID INTID HWINTID TARGET COL_ID HW ----------------------------------------------- 0 8192 0 0 0 0 1 8193 0 0 0 0 2 8194 0 2 2 0 Device ID: 0x18, Event ID Range: [0 - 3] EVENT_ID INTID HWINTID TARGET COL_ID HW ----------------------------------------------- 0 8225 0 0 0 0 1 8226 0 1 1 0 2 8227 0 3 3 0 Device ID: 0x10, Event ID Range: [0 - 7] EVENT_ID INTID HWINTID TARGET COL_ID HW ----------------------------------------------- 0 8229 0 3 3 1 1 8230 0 0 0 1 2 8231 0 1 1 1 3 8232 0 2 2 1 4 8233 0 3 3 1 The output is generated using the seq_file interface, allowing for efficient handling of potentially large ITS tables. This interface is read-only and does not allow modification of the ITS tables. It is intended for debugging and informational purposes only. Signed-off-by: Jing Zhang <jingzhangos@google.com> Link: https://lore.kernel.org/r/20250220224247.2017205-1-jingzhangos@google.com Signed-off-by: Marc Zyngier <maz@kernel.org>
1 parent fed55f4 commit 30deb51

File tree

3 files changed

+265
-31
lines changed

3 files changed

+265
-31
lines changed

arch/arm64/kvm/vgic/vgic-debug.c

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,3 +320,227 @@ void vgic_debug_init(struct kvm *kvm)
320320
void vgic_debug_destroy(struct kvm *kvm)
321321
{
322322
}
323+
324+
/**
325+
* struct vgic_its_iter - Iterator for traversing VGIC ITS device tables.
326+
* @dev: Pointer to the current its_device being processed.
327+
* @ite: Pointer to the current its_ite within the device being processed.
328+
*
329+
* This structure is used to maintain the current position during iteration
330+
* over the ITS device tables. It holds pointers to both the current device
331+
* and the current ITE within that device.
332+
*/
333+
struct vgic_its_iter {
334+
struct its_device *dev;
335+
struct its_ite *ite;
336+
};
337+
338+
/**
339+
* end_of_iter - Checks if the iterator has reached the end.
340+
* @iter: The iterator to check.
341+
*
342+
* When the iterator completed processing the final ITE in the last device
343+
* table, it was marked to indicate the end of iteration by setting its
344+
* device and ITE pointers to NULL.
345+
* This function checks whether the iterator was marked as end.
346+
*
347+
* Return: True if the iterator is marked as end, false otherwise.
348+
*/
349+
static inline bool end_of_iter(struct vgic_its_iter *iter)
350+
{
351+
return !iter->dev && !iter->ite;
352+
}
353+
354+
/**
355+
* vigc_its_iter_next - Advances the iterator to the next entry in the ITS tables.
356+
* @its: The VGIC ITS structure.
357+
* @iter: The iterator to advance.
358+
*
359+
* This function moves the iterator to the next ITE within the current device,
360+
* or to the first ITE of the next device if the current ITE is the last in
361+
* the device. If the current device is the last device, the iterator is set
362+
* to indicate the end of iteration.
363+
*/
364+
static void vgic_its_iter_next(struct vgic_its *its, struct vgic_its_iter *iter)
365+
{
366+
struct its_device *dev = iter->dev;
367+
struct its_ite *ite = iter->ite;
368+
369+
if (!ite || list_is_last(&ite->ite_list, &dev->itt_head)) {
370+
if (list_is_last(&dev->dev_list, &its->device_list)) {
371+
dev = NULL;
372+
ite = NULL;
373+
} else {
374+
dev = list_next_entry(dev, dev_list);
375+
ite = list_first_entry_or_null(&dev->itt_head,
376+
struct its_ite,
377+
ite_list);
378+
}
379+
} else {
380+
ite = list_next_entry(ite, ite_list);
381+
}
382+
383+
iter->dev = dev;
384+
iter->ite = ite;
385+
}
386+
387+
/**
388+
* vgic_its_debug_start - Start function for the seq_file interface.
389+
* @s: The seq_file structure.
390+
* @pos: The starting position (offset).
391+
*
392+
* This function initializes the iterator to the beginning of the ITS tables
393+
* and advances it to the specified position. It acquires the its_lock mutex
394+
* to protect shared data.
395+
*
396+
* Return: An iterator pointer on success, NULL if no devices are found or
397+
* the end of the list is reached, or ERR_PTR(-ENOMEM) on memory
398+
* allocation failure.
399+
*/
400+
static void *vgic_its_debug_start(struct seq_file *s, loff_t *pos)
401+
{
402+
struct vgic_its *its = s->private;
403+
struct vgic_its_iter *iter;
404+
struct its_device *dev;
405+
loff_t offset = *pos;
406+
407+
mutex_lock(&its->its_lock);
408+
409+
dev = list_first_entry_or_null(&its->device_list,
410+
struct its_device, dev_list);
411+
if (!dev)
412+
return NULL;
413+
414+
iter = kmalloc(sizeof(*iter), GFP_KERNEL);
415+
if (!iter)
416+
return ERR_PTR(-ENOMEM);
417+
418+
iter->dev = dev;
419+
iter->ite = list_first_entry_or_null(&dev->itt_head,
420+
struct its_ite, ite_list);
421+
422+
while (!end_of_iter(iter) && offset--)
423+
vgic_its_iter_next(its, iter);
424+
425+
if (end_of_iter(iter)) {
426+
kfree(iter);
427+
return NULL;
428+
}
429+
430+
return iter;
431+
}
432+
433+
/**
434+
* vgic_its_debug_next - Next function for the seq_file interface.
435+
* @s: The seq_file structure.
436+
* @v: The current iterator.
437+
* @pos: The current position (offset).
438+
*
439+
* This function advances the iterator to the next entry and increments the
440+
* position.
441+
*
442+
* Return: An iterator pointer on success, or NULL if the end of the list is
443+
* reached.
444+
*/
445+
static void *vgic_its_debug_next(struct seq_file *s, void *v, loff_t *pos)
446+
{
447+
struct vgic_its *its = s->private;
448+
struct vgic_its_iter *iter = v;
449+
450+
++*pos;
451+
vgic_its_iter_next(its, iter);
452+
453+
if (end_of_iter(iter)) {
454+
kfree(iter);
455+
return NULL;
456+
}
457+
return iter;
458+
}
459+
460+
/**
461+
* vgic_its_debug_stop - Stop function for the seq_file interface.
462+
* @s: The seq_file structure.
463+
* @v: The current iterator.
464+
*
465+
* This function frees the iterator and releases the its_lock mutex.
466+
*/
467+
static void vgic_its_debug_stop(struct seq_file *s, void *v)
468+
{
469+
struct vgic_its *its = s->private;
470+
struct vgic_its_iter *iter = v;
471+
472+
if (!IS_ERR_OR_NULL(iter))
473+
kfree(iter);
474+
mutex_unlock(&its->its_lock);
475+
}
476+
477+
/**
478+
* vgic_its_debug_show - Show function for the seq_file interface.
479+
* @s: The seq_file structure.
480+
* @v: The current iterator.
481+
*
482+
* This function formats and prints the ITS table entry information to the
483+
* seq_file output.
484+
*
485+
* Return: 0 on success.
486+
*/
487+
static int vgic_its_debug_show(struct seq_file *s, void *v)
488+
{
489+
struct vgic_its_iter *iter = v;
490+
struct its_device *dev = iter->dev;
491+
struct its_ite *ite = iter->ite;
492+
493+
if (list_is_first(&ite->ite_list, &dev->itt_head)) {
494+
seq_printf(s, "\n");
495+
seq_printf(s, "Device ID: 0x%x, Event ID Range: [0 - %llu]\n",
496+
dev->device_id, BIT_ULL(dev->num_eventid_bits) - 1);
497+
seq_printf(s, "EVENT_ID INTID HWINTID TARGET COL_ID HW\n");
498+
seq_printf(s, "-----------------------------------------------\n");
499+
}
500+
501+
if (ite && ite->irq && ite->collection) {
502+
seq_printf(s, "%8u %8u %8u %8u %8u %2d\n",
503+
ite->event_id, ite->irq->intid, ite->irq->hwintid,
504+
ite->collection->target_addr,
505+
ite->collection->collection_id, ite->irq->hw);
506+
}
507+
508+
return 0;
509+
}
510+
511+
static const struct seq_operations vgic_its_debug_sops = {
512+
.start = vgic_its_debug_start,
513+
.next = vgic_its_debug_next,
514+
.stop = vgic_its_debug_stop,
515+
.show = vgic_its_debug_show
516+
};
517+
518+
DEFINE_SEQ_ATTRIBUTE(vgic_its_debug);
519+
520+
/**
521+
* vgic_its_debug_init - Initializes the debugfs interface for VGIC ITS.
522+
* @dev: The KVM device structure.
523+
*
524+
* This function creates a debugfs file named "vgic-its-state@%its_base"
525+
* to expose the ITS table information.
526+
*
527+
* Return: 0 on success.
528+
*/
529+
int vgic_its_debug_init(struct kvm_device *dev)
530+
{
531+
struct vgic_its *its = dev->private;
532+
char *name;
533+
534+
name = kasprintf(GFP_KERNEL, "vgic-its-state@%llx", (u64)its->vgic_its_base);
535+
if (!name)
536+
return -ENOMEM;
537+
538+
debugfs_create_file(name, 0444, dev->kvm->debugfs_dentry, its, &vgic_its_debug_fops);
539+
540+
kfree(name);
541+
return 0;
542+
}
543+
544+
void vgic_its_debug_destroy(struct kvm_device *dev)
545+
{
546+
}

arch/arm64/kvm/vgic/vgic-its.c

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -154,36 +154,6 @@ static struct vgic_irq *vgic_add_lpi(struct kvm *kvm, u32 intid,
154154
return irq;
155155
}
156156

157-
struct its_device {
158-
struct list_head dev_list;
159-
160-
/* the head for the list of ITTEs */
161-
struct list_head itt_head;
162-
u32 num_eventid_bits;
163-
gpa_t itt_addr;
164-
u32 device_id;
165-
};
166-
167-
#define COLLECTION_NOT_MAPPED ((u32)~0)
168-
169-
struct its_collection {
170-
struct list_head coll_list;
171-
172-
u32 collection_id;
173-
u32 target_addr;
174-
};
175-
176-
#define its_is_collection_mapped(coll) ((coll) && \
177-
((coll)->target_addr != COLLECTION_NOT_MAPPED))
178-
179-
struct its_ite {
180-
struct list_head ite_list;
181-
182-
struct vgic_irq *irq;
183-
struct its_collection *collection;
184-
u32 event_id;
185-
};
186-
187157
/**
188158
* struct vgic_its_abi - ITS abi ops and settings
189159
* @cte_esz: collection table entry size
@@ -1938,6 +1908,8 @@ static void vgic_its_destroy(struct kvm_device *kvm_dev)
19381908

19391909
mutex_lock(&its->its_lock);
19401910

1911+
vgic_its_debug_destroy(kvm_dev);
1912+
19411913
vgic_its_free_device_list(kvm, its);
19421914
vgic_its_free_collection_list(kvm, its);
19431915
vgic_its_invalidate_cache(its);
@@ -2771,7 +2743,12 @@ static int vgic_its_set_attr(struct kvm_device *dev,
27712743
if (ret)
27722744
return ret;
27732745

2774-
return vgic_register_its_iodev(dev->kvm, its, addr);
2746+
ret = vgic_register_its_iodev(dev->kvm, its, addr);
2747+
if (ret)
2748+
return ret;
2749+
2750+
return vgic_its_debug_init(dev);
2751+
27752752
}
27762753
case KVM_DEV_ARM_VGIC_GRP_CTRL:
27772754
return vgic_its_ctrl(dev->kvm, its, attr->attr);

arch/arm64/kvm/vgic/vgic.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,36 @@ struct vgic_reg_attr {
172172
gpa_t addr;
173173
};
174174

175+
struct its_device {
176+
struct list_head dev_list;
177+
178+
/* the head for the list of ITTEs */
179+
struct list_head itt_head;
180+
u32 num_eventid_bits;
181+
gpa_t itt_addr;
182+
u32 device_id;
183+
};
184+
185+
#define COLLECTION_NOT_MAPPED ((u32)~0)
186+
187+
struct its_collection {
188+
struct list_head coll_list;
189+
190+
u32 collection_id;
191+
u32 target_addr;
192+
};
193+
194+
#define its_is_collection_mapped(coll) ((coll) && \
195+
((coll)->target_addr != COLLECTION_NOT_MAPPED))
196+
197+
struct its_ite {
198+
struct list_head ite_list;
199+
200+
struct vgic_irq *irq;
201+
struct its_collection *collection;
202+
u32 event_id;
203+
};
204+
175205
int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
176206
struct vgic_reg_attr *reg_attr);
177207
int vgic_v2_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
@@ -359,4 +389,7 @@ void vgic_v3_put_nested(struct kvm_vcpu *vcpu);
359389
void vgic_v3_handle_nested_maint_irq(struct kvm_vcpu *vcpu);
360390
void vgic_v3_nested_update_mi(struct kvm_vcpu *vcpu);
361391

392+
int vgic_its_debug_init(struct kvm_device *dev);
393+
void vgic_its_debug_destroy(struct kvm_device *dev);
394+
362395
#endif

0 commit comments

Comments
 (0)