devmem.c 4.1 KB

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