aboutsummaryrefslogtreecommitdiff
path: root/toys/other/devmem.c
blob: a6f9f606d68fca3a6689b971053e238cdb9e1e6d (plain)
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
/* devmem.c - Access physical addresses
 *
 * Copyright 2019 The Android Open Source Project

USE_DEVMEM(NEWTOY(devmem, "<1>3", TOYFLAG_USR|TOYFLAG_BIN))

config DEVMEM
  bool "devmem"
  default y
  help
    usage: devmem ADDR [WIDTH [DATA]]

    Read/write physical address via /dev/mem.

    WIDTH is 1, 2, 4, or 8 bytes (default 4).
*/

#define FOR_devmem
#include "toys.h"

void devmem_main(void)
{
  int writing = toys.optc == 3, page_size = getpagesize(), bytes = 4, fd;
  unsigned long long addr = atolx(toys.optargs[0]), data = 0, map_off, map_len;
  void *map, *p;

  // WIDTH?
  if (toys.optc>1) {
    int i;

    if (strlen(toys.optargs[1])!=1 || (i=stridx("1248", *toys.optargs[1]))==-1)
      error_exit("bad width: %s", toys.optargs[1]);
    bytes = 1<<i;
  }

  // DATA? Report out of range values as errors rather than truncating.
  if (writing) data = atolx_range(toys.optargs[2], 0, (1ULL<<(8*bytes))-1);

  // Map in just enough.
  fd = xopen("/dev/mem", (writing ? O_RDWR : O_RDONLY) | O_SYNC);
  map_off = addr & ~(page_size - 1);
  map_len = (addr+bytes-map_off);
  map = xmmap(NULL, map_len, writing ? PROT_WRITE : PROT_READ, MAP_SHARED, fd,
      map_off);
  p = map + (addr & (page_size - 1));
  close(fd);

  // Not using peek()/poke() because registers care about size of read/write
  if (writing) {
    if (bytes == 1) *(char *)p = data;
    else if (bytes == 2) *(short *)p = data;
    else if (bytes == 4) *(int *)p = data;
    else if (bytes == 8) *(long long *)p = data;
  } else {
    if (bytes == 1) data = *(char *)p;
    else if (bytes == 2) data = *(short *)p;
    else if (bytes == 4) data = *(int *)p;
    else if (bytes == 8) data = *(long long *)p;
    printf("%#0*llx\n", bytes*2, data);
  }

  munmap(map, map_len);
}