forked from vasilevnikitad/memrw
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.cpp
More file actions
208 lines (170 loc) · 6.46 KB
/
main.cpp
File metadata and controls
208 lines (170 loc) · 6.46 KB
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
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <cstdlib>
#include <iostream>
#include <limits>
#include <stdexcept>
#include <string>
using namespace std::literals::string_literals;
struct parameters {
public:
using value_type = std::uint32_t;
enum class action {
read,
write,
show_help,
};
static std::string get_help() {
static std::string const help{
program_name + " - reads/writes to /dev/mem\n"s
"Usage:\n"
+ " " + program_name + " <ADDRESS> \t\tread unsigned 32-bits value from address and print output in hex base.\n"
+ " " + program_name + " <ADDRESS> <VALUE>\t\twrite unsigned 32-bits value to address.\n"
+ "<ADDRESS> and <VALUE> can be in hex (0x42), octet (042) or decimal (42) base"
};
return help;
}
value_type* get_address() const {
return address;
}
value_type get_value() const {
return value;
}
action get_action() const {
return action;
}
parameters(int argc, char const* argv[]) {
using address_t = std::decay_t<decltype(address)>;
using value_t = std::decay_t<decltype(value)>;
constexpr static int address_pos{1};
constexpr static int value_pos{2};
switch (argc) {
case 2:
action = action::read;
address = reinterpret_cast<address_t>(get_int_type_from_str<std::size_t>(argv[address_pos]));
break;
case 3:
action = action::write;
address = reinterpret_cast<address_t>(get_int_type_from_str<std::size_t>(argv[address_pos]));
value = get_int_type_from_str<value_t>(argv[value_pos]);
break;
default:
action = action::show_help;
break;
}
}
private:
template<typename T = unsigned long long>
static T get_int_type_from_str(char const* line)
requires (std::is_convertible_v<decltype(std::stoull(line, nullptr, 0)), T>) {
try {
// std::stoull returns not a pretty message in case of exception.
// that's why there is a try{} catch{} block.
std::size_t pos;
T out{static_cast<T>(std::stoull(line, &pos, 0))};
if (auto c{line[pos]}; c && !std::isspace(c)) {
throw std::invalid_argument{"Invalid argument"};
}
if (std::numeric_limits<T>::max() < out ||
std::numeric_limits<T>::min() > out) {
throw std::invalid_argument{"Invalid argument"};
}
return out;
} catch (std::invalid_argument const&) {
throw std::invalid_argument{"Invalid argument"};
} catch (std::out_of_range const&) {
throw std::out_of_range{"Value is out of range"};
}
}
value_type *address{nullptr};
value_type value;
action action;
static constexpr auto program_name{"memrw"};
};
class mem {
public:
mem() {
if ((fd = open(dev_mem_file, O_RDWR | O_SYNC)) < 0)
throw std::system_error{errno, std::system_category(), "Failed to open"s + dev_mem_file};
}
template<typename T>
T read(T const* const addr) {
std::size_t const page_aligned_off(reinterpret_cast<std::size_t>(addr) & ~page_mask);
T const* const offset_addr{reinterpret_cast<T const*>(reinterpret_cast<std::size_t>(addr) & page_mask)};
return mem_map{fd, page_aligned_off, sizeof(T)}.read(offset_addr);
}
template<typename T>
void write(T* const addr, T const& value) {
std::size_t const page_aligned_off(reinterpret_cast<std::size_t>(addr) & ~page_mask);
T* const offset_addr{reinterpret_cast<T*>(reinterpret_cast<std::size_t>(addr) & page_mask)};
mem_map{fd, page_aligned_off, sizeof(T)}.write(offset_addr, value);
}
~mem() try {
if (close(fd) < 0)
throw std::system_error{errno, std::system_category(), "Failed to close"s + dev_mem_file};
} catch(std::exception const& e) {
std::cerr << "Failed to destroy mem class :" << e.what() << std::endl;
}
private:
int fd;
static constexpr auto dev_mem_file{"/dev/mem"};
std::size_t const page_sz{static_cast<std::size_t>(sysconf(_SC_PAGESIZE))};
std::size_t const page_mask{page_sz - 1};
struct mem_map {
mem_map(int const fd, std::size_t const offset_page_aligned, std::size_t const sz) : map_size{sz} {
mmap_addr = mmap(nullptr, sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset_page_aligned);
if (mmap_addr == MAP_FAILED)
throw std::system_error{errno, std::system_category(), "Failed to map memory"};
}
template<typename T>
T read(T const* const addr) {
return *get_offset(addr);
}
template<typename T>
void write(T* const addr, T const& value) {
*get_offset(addr) = value;
}
~mem_map() noexcept(false) {
if (munmap(mmap_addr, map_size) < 0)
throw std::system_error{errno, std::system_category(), "Failed to unmap memory"};
}
void* mmap_addr;
std::size_t const map_size;
private:
template<typename T>
T* get_offset(T* const addr) {
auto const mmap_addr_byte{reinterpret_cast<std::byte*>(mmap_addr)};
auto const addr_byte{reinterpret_cast<off_t>(addr)};
return reinterpret_cast<T*>(mmap_addr_byte + addr_byte);
}
};
};
int main(int argc, char const* argv[]) try {
parameters params{argc, argv};
switch (params.get_action()) {
case parameters::action::read: {
std::cout << "0x" << std::hex << mem{}.read(params.get_address()) << std::endl;
break;
}
case parameters::action::write: {
mem{}.write(params.get_address(), params.get_value());
break;
}
case parameters::action::show_help: {
std::cout << parameters::get_help() << std::endl;
break;
}
default:
throw std::logic_error{"unsupported parameter action"};
}
return EXIT_SUCCESS;
} catch (std::exception const& e) {
std::cerr << e.what() << std::endl;
std::cout << parameters::get_help() << std::endl;
return EXIT_FAILURE;
} catch (...) {
std::cerr << "Unknown exception" << std::endl;
std::cout << parameters::get_help() << std::endl;
return EXIT_FAILURE;
}