2024-09-03 13:06:33 +02:00
|
|
|
/* Copyright (c) 2024 Barcelona Supercomputing Center (BSC)
|
|
|
|
* SPDX-License-Identifier: MIT
|
|
|
|
* Author: Rodrigo Arias Mallo <rodrigo.arias@bsc.es> */
|
|
|
|
|
|
|
|
/* Small utility to dump the state of the PLIC. */
|
|
|
|
|
|
|
|
/* Changelog:
|
|
|
|
* v0.0.1 (2024-09-03): Initial version.
|
2024-09-04 16:17:21 +02:00
|
|
|
* v0.0.2 (2024-09-04): Print contexts in another line and masked information.
|
|
|
|
* v0.0.3 (2024-09-04): Make output format more clear and add manual.
|
2024-09-30 11:55:57 +02:00
|
|
|
* v0.0.4 (2024-09-30): Implement support for claiming an interrupt.
|
2024-09-03 13:06:33 +02:00
|
|
|
*/
|
|
|
|
|
2024-09-30 11:55:57 +02:00
|
|
|
#define VERSION "v0.0.4"
|
2024-09-04 16:17:21 +02:00
|
|
|
|
2024-09-03 13:06:33 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <errno.h>
|
|
|
|
|
2024-09-30 11:55:57 +02:00
|
|
|
enum op { DUMP, CLAIM } operation = DUMP;
|
2024-09-03 13:06:33 +02:00
|
|
|
const char *plic_address_str = "0x40800000";
|
2024-09-30 12:03:09 +02:00
|
|
|
long ncontexts = 2L;
|
2024-09-03 13:06:33 +02:00
|
|
|
long maxsources = 1024L;
|
|
|
|
long nsources = 1024L;
|
2024-09-30 11:55:57 +02:00
|
|
|
long claim_ctx = 0;
|
2024-09-03 13:06:33 +02:00
|
|
|
|
|
|
|
static void
|
|
|
|
usage(void)
|
|
|
|
{
|
|
|
|
printf(
|
2024-09-04 16:17:21 +02:00
|
|
|
"NAME\n"
|
|
|
|
" plictool - dump PLIC information\n"
|
|
|
|
"\n"
|
|
|
|
"SYNOPSIS\n"
|
2024-09-30 11:55:57 +02:00
|
|
|
" plictool [-a addr] [-s nsources] [-c ncontexts] [-C ctx]\n"
|
2024-09-04 16:17:21 +02:00
|
|
|
"\n"
|
|
|
|
"DESCRIPTION\n"
|
|
|
|
" Dumps information about the RISC-V PLIC interrupt controller.\n"
|
|
|
|
" Optional arguments:\n"
|
|
|
|
"\n"
|
|
|
|
" -a addr The starting address of the PLIC.\n"
|
|
|
|
" The default is 0x40800000.\n"
|
|
|
|
"\n"
|
|
|
|
" -s nsources Limit the number of sources to print.\n"
|
|
|
|
" The default is 1024.\n"
|
|
|
|
"\n"
|
|
|
|
" -c ncontexts Limit the number of contexts to print.\n"
|
|
|
|
" The default is 15872.\n"
|
|
|
|
"\n"
|
2024-09-30 11:55:57 +02:00
|
|
|
" -C ctx Claim the interrupt on the given context.\n"
|
|
|
|
"\n"
|
2024-09-04 16:17:21 +02:00
|
|
|
"OUTPUT FORMAT\n"
|
|
|
|
" Information is printed for each source that has some bit set\n"
|
|
|
|
" in the pending or priority registers, or it has some context\n"
|
|
|
|
" enabled. The the following attributes are printed:\n"
|
|
|
|
"\n"
|
|
|
|
" src source number\n"
|
|
|
|
" pend pending bit\n"
|
|
|
|
" prio priority of the source\n"
|
|
|
|
"\n"
|
|
|
|
" Additionally, for each enabled context of the source the\n"
|
|
|
|
" following attributes are printed for that context:\n"
|
|
|
|
"\n"
|
|
|
|
" ctx context number\n"
|
|
|
|
" thre threshold value\n"
|
|
|
|
" masked the source is masked (prio <= thre)\n"
|
|
|
|
"\n"
|
|
|
|
"AUTHOR\n"
|
|
|
|
" Rodrigo Arias Mallo <rodrigo.arias@bsc.es>\n"
|
|
|
|
"\n"
|
|
|
|
"SEE ALSO\n"
|
|
|
|
" See https://github.com/riscv/riscv-plic-spec/\n"
|
|
|
|
"\n"
|
|
|
|
);
|
2024-09-03 13:06:33 +02:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2024-09-30 11:55:57 +02:00
|
|
|
static uint32_t
|
|
|
|
read_reg(void *base, size_t offset)
|
|
|
|
{
|
|
|
|
volatile uint32_t *p = base + offset;
|
|
|
|
return *p;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
write_reg(void *base, size_t offset, uint32_t value)
|
|
|
|
{
|
|
|
|
volatile uint32_t *p = base + offset;
|
|
|
|
*p = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
claim_interrupt(void *base, long ctx)
|
|
|
|
{
|
|
|
|
size_t offset = 0x200004L + ctx * 0x1000;
|
|
|
|
uint32_t value = read_reg(base, offset);
|
|
|
|
printf("ctx=%ld claim=%u\n", ctx, value);
|
|
|
|
write_reg(base, offset, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
dump_sources(void *base)
|
2024-09-03 13:06:33 +02:00
|
|
|
{
|
|
|
|
for (long s = 0; s < nsources; s++) {
|
|
|
|
int printed_source = 0;
|
|
|
|
|
|
|
|
uint32_t *pending_reg = base + 0x1000L + (s / 32L) * 4L;
|
|
|
|
long shift = s % 32L;
|
|
|
|
uint32_t pending = ((*pending_reg) >> shift) & 1;
|
|
|
|
uint32_t *priority_reg = base + 0x0000L + (s * 4L);
|
|
|
|
uint32_t priority = *priority_reg;
|
|
|
|
|
|
|
|
const char *fmt =
|
2024-09-04 16:17:21 +02:00
|
|
|
"src=%ld pend=%u prio=%u";
|
2024-09-03 13:06:33 +02:00
|
|
|
|
|
|
|
if (pending || priority) {
|
|
|
|
printf(fmt, s, pending, priority);
|
|
|
|
printed_source = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int first_context = 1;
|
2024-09-04 16:17:21 +02:00
|
|
|
int enabled_contexts = 0;
|
|
|
|
int unmasked = 0;
|
2024-09-03 13:06:33 +02:00
|
|
|
for (long c = 0; c < ncontexts; c++) {
|
|
|
|
uint32_t *enable_reg = base + 0x2000L + 0x80L * c + (s / 32L) * 4L;
|
|
|
|
uint32_t enabled = ((*enable_reg) >> shift) & 1;
|
|
|
|
uint32_t *threshold_reg = base + 0x200000L + (c * 0x1000L);
|
|
|
|
uint32_t threshold = *threshold_reg;
|
|
|
|
|
|
|
|
if (!enabled)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!printed_source) {
|
|
|
|
printf(fmt, s, pending, priority);
|
|
|
|
printed_source = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-09-04 16:17:21 +02:00
|
|
|
int is_masked = priority <= threshold;
|
|
|
|
|
|
|
|
if (!is_masked)
|
|
|
|
unmasked++;
|
|
|
|
|
|
|
|
const char *masked = is_masked ? "masked" : "unmasked";
|
|
|
|
|
|
|
|
printf("\n ctx=%ld thre=%u %s",
|
|
|
|
c, threshold, masked);
|
|
|
|
enabled_contexts++;
|
2024-09-04 12:44:53 +02:00
|
|
|
}
|
2024-09-04 16:17:21 +02:00
|
|
|
|
|
|
|
if (printed_source)
|
|
|
|
printf("\n");
|
2024-09-03 13:06:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
int opt;
|
|
|
|
|
2024-09-30 11:55:57 +02:00
|
|
|
while ((opt = getopt(argc, argv, "a:hs:c:C:")) != -1) {
|
2024-09-03 13:06:33 +02:00
|
|
|
switch (opt) {
|
|
|
|
case 'a':
|
|
|
|
plic_address_str = optarg;
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
nsources = atol(optarg);
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
ncontexts = atol(optarg);
|
|
|
|
break;
|
2024-09-30 11:55:57 +02:00
|
|
|
case 'C':
|
|
|
|
operation = CLAIM;
|
|
|
|
claim_ctx = atol(optarg);
|
|
|
|
break;
|
2024-09-03 13:06:33 +02:00
|
|
|
case 'h':
|
|
|
|
default: /* '?' */
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-04 16:17:21 +02:00
|
|
|
unsigned long long plic_address = strtoull(plic_address_str, NULL, 16);
|
2024-09-03 13:06:33 +02:00
|
|
|
|
2024-09-04 16:17:21 +02:00
|
|
|
printf("plictool "VERSION" addr=0x%08llx nsrc=%ld nctx=%ld\n",
|
2024-09-03 13:06:33 +02:00
|
|
|
plic_address, nsources, ncontexts);
|
|
|
|
|
2024-09-04 16:17:21 +02:00
|
|
|
//int fd = open("test.bin", O_RDWR | O_SYNC);
|
2024-09-03 13:06:33 +02:00
|
|
|
int fd = open("/dev/mem", O_RDWR | O_SYNC);
|
|
|
|
|
|
|
|
if (fd == -1) {
|
|
|
|
perror("cannot open /dev/mem");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t map_size = 0x4000000UL;
|
|
|
|
void *map_base = mmap(0, map_size, PROT_READ | PROT_WRITE, MAP_SHARED,
|
|
|
|
fd, plic_address);
|
|
|
|
|
|
|
|
if (map_base == MAP_FAILED) {
|
|
|
|
perror("mmap failed");
|
|
|
|
if (errno == EPERM) {
|
|
|
|
fprintf(stderr, "Have you disabled 'CONFIG_STRICT_DEVMEM' and "
|
|
|
|
"'CONFIG_IO_STRICT_DEVMEM' in the kernel config?\n"
|
|
|
|
"Hint: zgrep STRICT_DEVMEM /proc/config.gz\n");
|
|
|
|
}
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2024-09-30 11:55:57 +02:00
|
|
|
if (operation == CLAIM)
|
|
|
|
claim_interrupt(map_base, claim_ctx);
|
|
|
|
else if (operation == DUMP)
|
|
|
|
dump_sources(map_base);
|
2024-09-03 13:06:33 +02:00
|
|
|
|
|
|
|
munmap(map_base, map_size);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|