1 /*
2  * Copyright (C) 2000, Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
3  * Copyright (C) 2008, BusyBox Team. -solar 4/26/08
4  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
5  */
6 //config:config DEVMEM
7 //config:	bool "devmem (2.5 kb)"
8 //config:	default y
9 //config:	help
10 //config:	devmem is a small program that reads and writes from physical
11 //config:	memory using /dev/mem.
12 
13 //applet:IF_DEVMEM(APPLET(devmem, BB_DIR_SBIN, BB_SUID_DROP))
14 
15 //kbuild:lib-$(CONFIG_DEVMEM) += devmem.o
16 
17 //usage:#define devmem_trivial_usage
18 //usage:	"ADDRESS [WIDTH [VALUE]]"
19 //usage:#define devmem_full_usage "\n\n"
20 //usage:       "Read/write from physical address\n"
21 //usage:     "\n	ADDRESS	Address to act upon"
22 //usage:     "\n	WIDTH	Width (8/16/...)"
23 //usage:     "\n	VALUE	Data to be written"
24 
25 #include "libbb.h"
26 
27 int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
devmem_main(int argc UNUSED_PARAM,char ** argv)28 int devmem_main(int argc UNUSED_PARAM, char **argv)
29 {
30 	void *map_base, *virt_addr;
31 	uint64_t read_result;
32 	uint64_t writeval = writeval; /* for compiler */
33 	off_t target;
34 	unsigned page_size, mapped_size, offset_in_page;
35 	int fd;
36 	unsigned width = 8 * sizeof(int);
37 
38 	/* devmem ADDRESS [WIDTH [VALUE]] */
39 // TODO: options?
40 // -r: read and output only the value in hex, with 0x prefix
41 // -w: write only, no reads before or after, and no output
42 // or make this behavior default?
43 // Let's try this and see how users react.
44 
45 	/* ADDRESS */
46 	if (!argv[1])
47 		bb_show_usage();
48 	errno = 0;
49 	target = bb_strtoull(argv[1], NULL, 0); /* allows hex, oct etc */
50 
51 	/* WIDTH */
52 	if (argv[2]) {
53 		if (isdigit(argv[2][0]) || argv[2][1])
54 			width = xatou(argv[2]);
55 		else {
56 			static const char bhwl[] ALIGN1 = "bhwl";
57 			static const uint8_t sizes[] ALIGN1 = {
58 				8 * sizeof(char),
59 				8 * sizeof(short),
60 				8 * sizeof(int),
61 				8 * sizeof(long),
62 				0 /* bad */
63 			};
64 			width = strchrnul(bhwl, (argv[2][0] | 0x20)) - bhwl;
65 			width = sizes[width];
66 		}
67 		/* VALUE */
68 		if (argv[3])
69 			writeval = bb_strtoull(argv[3], NULL, 0);
70 	} else { /* argv[2] == NULL */
71 		/* make argv[3] to be a valid thing to fetch */
72 		argv--;
73 	}
74 	if (errno)
75 		bb_show_usage(); /* one of bb_strtouXX failed */
76 
77 	fd = xopen("/dev/mem", argv[3] ? (O_RDWR | O_SYNC) : (O_RDONLY | O_SYNC));
78 	mapped_size = page_size = bb_getpagesize();
79 	offset_in_page = (unsigned)target & (page_size - 1);
80 	if (offset_in_page + width > page_size) {
81 		/* This access spans pages.
82 		 * Must map two pages to make it possible: */
83 		mapped_size *= 2;
84 	}
85 	map_base = mmap(NULL,
86 			mapped_size,
87 			argv[3] ? (PROT_READ | PROT_WRITE) : PROT_READ,
88 			MAP_SHARED,
89 			fd,
90 			target & ~(off_t)(page_size - 1));
91 	if (map_base == MAP_FAILED)
92 		bb_simple_perror_msg_and_die("mmap");
93 
94 //	printf("Memory mapped at address %p.\n", map_base);
95 
96 	virt_addr = (char*)map_base + offset_in_page;
97 
98 	if (!argv[3]) {
99 		switch (width) {
100 		case 8:
101 			read_result = *(volatile uint8_t*)virt_addr;
102 			break;
103 		case 16:
104 			read_result = *(volatile uint16_t*)virt_addr;
105 			break;
106 		case 32:
107 			read_result = *(volatile uint32_t*)virt_addr;
108 			break;
109 		case 64:
110 			read_result = *(volatile uint64_t*)virt_addr;
111 			break;
112 		default:
113 			bb_simple_error_msg_and_die("bad width");
114 		}
115 //		printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n",
116 //			target, virt_addr,
117 //			(unsigned long long)read_result);
118 		/* Zero-padded output shows the width of access just done */
119 		printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result);
120 	} else {
121 		switch (width) {
122 		case 8:
123 			*(volatile uint8_t*)virt_addr = writeval;
124 //			read_result = *(volatile uint8_t*)virt_addr;
125 			break;
126 		case 16:
127 			*(volatile uint16_t*)virt_addr = writeval;
128 //			read_result = *(volatile uint16_t*)virt_addr;
129 			break;
130 		case 32:
131 			*(volatile uint32_t*)virt_addr = writeval;
132 //			read_result = *(volatile uint32_t*)virt_addr;
133 			break;
134 		case 64:
135 			*(volatile uint64_t*)virt_addr = writeval;
136 //			read_result = *(volatile uint64_t*)virt_addr;
137 			break;
138 		default:
139 			bb_simple_error_msg_and_die("bad width");
140 		}
141 //		printf("Written 0x%llX; readback 0x%llX\n",
142 //				(unsigned long long)writeval,
143 //				(unsigned long long)read_result);
144 	}
145 
146 	if (ENABLE_FEATURE_CLEAN_UP) {
147 		if (munmap(map_base, mapped_size) == -1)
148 			bb_simple_perror_msg_and_die("munmap");
149 		close(fd);
150 	}
151 
152 	return EXIT_SUCCESS;
153 }
154