forked from rarias/nixos-riscv
		
	
		
			
				
	
	
		
			188 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			4.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* 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.
 | |
|  *   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.
 | |
|  */
 | |
| 
 | |
| #define VERSION "v0.0.3"
 | |
| 
 | |
| #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>
 | |
| 
 | |
| const char *plic_address_str = "0x40800000";
 | |
| long ncontexts = 15872L;
 | |
| long maxsources = 1024L;
 | |
| long nsources = 1024L;
 | |
| 
 | |
| static void
 | |
| usage(void)
 | |
| {
 | |
| 	printf(
 | |
| "NAME\n"
 | |
| "    plictool - dump PLIC information\n"
 | |
| "\n"
 | |
| "SYNOPSIS\n"
 | |
| "    plictool [-a addr] [-s nsources] [-c ncontexts]\n"
 | |
| "\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"
 | |
| "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"
 | |
| );
 | |
| 	exit(1);
 | |
| }
 | |
| 
 | |
| void dump_sources(void *base)
 | |
| {
 | |
| 	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 =
 | |
| 			"src=%ld pend=%u prio=%u";
 | |
| 
 | |
| 		if (pending || priority) {
 | |
| 			printf(fmt, s, pending, priority);
 | |
| 			printed_source = 1;
 | |
| 		}
 | |
| 
 | |
| 		int first_context = 1;
 | |
| 		int enabled_contexts = 0;
 | |
| 		int unmasked = 0;
 | |
| 		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;
 | |
| 			}
 | |
| 
 | |
| 
 | |
| 			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++;
 | |
| 		}
 | |
| 
 | |
| 		if (printed_source)
 | |
| 			printf("\n");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int main(int argc, char *argv[])
 | |
| {
 | |
| 	int opt;
 | |
| 
 | |
| 	while ((opt = getopt(argc, argv, "a:hs:c:")) != -1) {
 | |
| 		switch (opt) {
 | |
| 			case 'a':
 | |
| 				plic_address_str = optarg;
 | |
| 				break;
 | |
| 			case 's':
 | |
| 				nsources = atol(optarg);
 | |
| 				break;
 | |
| 			case 'c':
 | |
| 				ncontexts = atol(optarg);
 | |
| 				break;
 | |
| 			case 'h':
 | |
| 			default: /* '?' */
 | |
| 				usage();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	unsigned long long plic_address = strtoull(plic_address_str, NULL, 16);
 | |
| 
 | |
| 	printf("plictool "VERSION" addr=0x%08llx nsrc=%ld nctx=%ld\n",
 | |
| 			plic_address, nsources, ncontexts);
 | |
| 
 | |
| 	//int fd = open("test.bin", O_RDWR | O_SYNC);
 | |
| 	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);
 | |
| 	}
 | |
| 
 | |
| 	dump_sources(map_base);
 | |
| 
 | |
| 	munmap(map_base, map_size);
 | |
| 
 | |
| 	close(fd);
 | |
| 
 | |
| 	return 0;
 | |
| }
 |