reghack.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. * reghack - Utility to binary-patch the embedded mac80211 regulatory rules.
  3. *
  4. * Copyright (C) 2012-2014 Jo-Philipp Wich <xm@subsignal.org>
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. #include <stdio.h>
  19. #include <stdint.h>
  20. #include <stdlib.h>
  21. #include <unistd.h>
  22. #include <fcntl.h>
  23. #include <string.h>
  24. #include <byteswap.h>
  25. #include <limits.h>
  26. #include <arpa/inet.h>
  27. #include <sys/stat.h>
  28. #include <sys/mman.h>
  29. static int need_byteswap = 0;
  30. enum nl80211_dfs_regions {
  31. NL80211_DFS_UNSET = 0,
  32. NL80211_DFS_FCC = 1
  33. };
  34. struct ieee80211_freq_range {
  35. uint32_t start_freq_khz;
  36. uint32_t end_freq_khz;
  37. uint32_t max_bandwidth_khz;
  38. };
  39. struct ieee80211_power_rule {
  40. uint32_t max_antenna_gain;
  41. uint32_t max_eirp;
  42. };
  43. struct ieee80211_reg_rule {
  44. struct ieee80211_freq_range freq_range;
  45. struct ieee80211_power_rule power_rule;
  46. uint32_t flags;
  47. uint32_t dfs_cac_ms;
  48. };
  49. struct ieee80211_regdomain {
  50. uint32_t n_reg_rules;
  51. char alpha2[2];
  52. enum nl80211_dfs_regions dfs_region;
  53. struct ieee80211_reg_rule reg_rules[1];
  54. };
  55. #define MHZ_TO_KHZ(freq) ((freq) * 1000)
  56. #define KHZ_TO_MHZ(freq) ((freq) / 1000)
  57. #define DBI_TO_MBI(gain) ((gain) * 100)
  58. #define MBI_TO_DBI(gain) ((gain) / 100)
  59. #define DBM_TO_MBM(gain) ((gain) * 100)
  60. #define MBM_TO_DBM(gain) ((gain) / 100)
  61. #define REG_RULE(start, end, bw, gain, eirp, reg_flags) \
  62. { \
  63. .freq_range.start_freq_khz = MHZ_TO_KHZ(start), \
  64. .freq_range.end_freq_khz = MHZ_TO_KHZ(end), \
  65. .freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw), \
  66. .power_rule.max_antenna_gain = DBI_TO_MBI(gain),\
  67. .power_rule.max_eirp = DBM_TO_MBM(eirp), \
  68. .flags = reg_flags, \
  69. .dfs_cac_ms = 0, \
  70. }
  71. #define REG_MATCH(code, num, dfs, rule) \
  72. { \
  73. .alpha2 = code, \
  74. .dfs_region = dfs, \
  75. .n_reg_rules = num, \
  76. .reg_rules = { \
  77. rule \
  78. } \
  79. }
  80. struct search_regdomain {
  81. const char *desc;
  82. struct ieee80211_regdomain reg;
  83. };
  84. static const struct search_regdomain search_regdomains[] = {
  85. /* cfg80211.ko matches */
  86. {
  87. .desc = "core world5 regdomain in cfg80211/reg.o",
  88. .reg = REG_MATCH("00", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 6, 20, 0))
  89. }, {
  90. .desc = "core world6 regdomain in cfg80211/reg.o",
  91. .reg = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 6, 20, 0))
  92. }, {
  93. .desc = "embedded 00 regdomain in cfg80211/regdb.o",
  94. .reg = REG_MATCH("00", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 20, 0))
  95. }, {
  96. .desc = "embedded 00 regdomain in cfg80211/regdb.o",
  97. .reg = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 20, 0))
  98. }, {
  99. .desc = "embedded US regdomain in cfg80211/regdb.o",
  100. .reg = REG_MATCH("US", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 27, 0))
  101. }, {
  102. .desc = "embedded US regdomain in cfg80211/regdb.o",
  103. .reg = REG_MATCH("US", 7, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 3, 27, 0))
  104. }, {
  105. .desc = "embedded US regdomain in cfg80211/regdb.o",
  106. .reg = REG_MATCH("US", 7, NL80211_DFS_FCC, REG_RULE(2402, 2472, 40, 3, 27, 0))
  107. },
  108. /* regdb.txt matches (new) */
  109. {
  110. .desc = "embedded 00 regdomain in cfg80211/regdb.o",
  111. .reg = REG_MATCH("00", 6, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
  112. }, {
  113. .desc = "embedded US regdomain in cfg80211/regdb.o",
  114. .reg = REG_MATCH("US", 5, NL80211_DFS_FCC, REG_RULE(2402, 2472, 40, 0, 30, 0))
  115. },
  116. /* ath.ko matches */
  117. {
  118. .desc = "ath world regdomain with 3 rules in ath/regd.o",
  119. .reg = REG_MATCH("99", 3, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
  120. }, {
  121. .desc = "ath world regdomain with 4 rules in ath/regd.o",
  122. .reg = REG_MATCH("99", 4, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
  123. }, {
  124. .desc = "ath world regdomain with 5 rules in ath/regd.o",
  125. .reg = REG_MATCH("99", 5, NL80211_DFS_UNSET, REG_RULE(2402, 2472, 40, 0, 20, 0))
  126. }
  127. };
  128. struct search_insn {
  129. const char *desc;
  130. const uint16_t machine;
  131. const uint32_t search;
  132. const uint32_t replace;
  133. const uint32_t mask;
  134. };
  135. static const struct search_insn search_insns[] = {
  136. /* radar frequency check */
  137. {
  138. .desc = "ath_is_radar_freq() MIPS opcode in ath/regd.o",
  139. .machine = 0x0008, /* MIPS */
  140. .search = 0x2400eb74, /* addiu rX, rY, -5260 */
  141. .replace = 0x24000000, /* addiu rX, rY, 0 */
  142. .mask = 0xfc00ffff
  143. },
  144. {
  145. .desc = "ath_is_radar_freq() PPC opcode in ath/regd.o",
  146. .machine = 0x0014, /* PPC */
  147. .search = 0x3800eb74, /* addi rX, rY, -5260 */
  148. .replace = 0x38000000, /* addi rX, rY, 0 */
  149. .mask = 0xfc00ffff
  150. },
  151. };
  152. static void check_endianess(unsigned char *elf_hdr)
  153. {
  154. int self_is_be = (htonl(42) == 42);
  155. int elf_is_be = (elf_hdr[5] == 2);
  156. if (self_is_be != elf_is_be)
  157. {
  158. need_byteswap = 1;
  159. printf("Byte swapping needed (utility %s endian, module %s endian)\n",
  160. self_is_be ? "big" : "low",
  161. elf_is_be ? "big" : "low");
  162. }
  163. }
  164. static void bswap_rule(struct ieee80211_reg_rule *r)
  165. {
  166. r->freq_range.start_freq_khz = bswap_32(r->freq_range.start_freq_khz);
  167. r->freq_range.end_freq_khz = bswap_32(r->freq_range.end_freq_khz);
  168. r->freq_range.max_bandwidth_khz = bswap_32(r->freq_range.max_bandwidth_khz);
  169. r->power_rule.max_antenna_gain = bswap_32(r->power_rule.max_antenna_gain);
  170. r->power_rule.max_eirp = bswap_32(r->power_rule.max_eirp);
  171. r->flags = bswap_32(r->flags);
  172. }
  173. static int patch_regdomain(struct ieee80211_regdomain *pos,
  174. const struct ieee80211_regdomain *comp)
  175. {
  176. struct ieee80211_reg_rule r2 = REG_RULE(2400, 2483, 40, 0, 30, 0);
  177. struct ieee80211_reg_rule r5 = REG_RULE(5140, 5860, 40, 0, 30, 0);
  178. struct ieee80211_regdomain pattern = *comp;
  179. if (need_byteswap)
  180. {
  181. bswap_rule(&pattern.reg_rules[0]);
  182. pattern.dfs_region = bswap_32(pattern.dfs_region);
  183. pattern.n_reg_rules = bswap_32(pattern.n_reg_rules);
  184. }
  185. if (!memcmp(pos, &pattern, sizeof(pattern)))
  186. {
  187. pos->reg_rules[0] = r2;
  188. pos->reg_rules[1] = r5;
  189. pos->n_reg_rules = 2;
  190. pos->dfs_region = 0;
  191. if (need_byteswap)
  192. {
  193. bswap_rule(&pos->reg_rules[0]);
  194. bswap_rule(&pos->reg_rules[1]);
  195. pos->n_reg_rules = bswap_32(pos->n_reg_rules);
  196. }
  197. return 0;
  198. }
  199. return 1;
  200. }
  201. static uint16_t check_ath_ko(unsigned char *elf_hdr, const char *filename)
  202. {
  203. uint16_t type = *(uint16_t *)(elf_hdr + 18);
  204. const char *file = strrchr(filename, '/');
  205. if (!file)
  206. file = filename;
  207. else
  208. file++;
  209. if (need_byteswap)
  210. type = bswap_16(type);
  211. if (!strcmp(file, "ath.ko"))
  212. return type;
  213. return 0;
  214. }
  215. static int patch_insn(uint32_t *pos, const struct search_insn *insn)
  216. {
  217. uint32_t cmp = need_byteswap ? bswap_32(*pos) : *pos;
  218. if ((cmp & insn->mask) == insn->search)
  219. {
  220. *pos = need_byteswap ? bswap_32(insn->replace | (cmp & ~insn->mask))
  221. : insn->replace | (cmp & ~insn->mask);
  222. return 0;
  223. }
  224. return 1;
  225. }
  226. static int tryopen(const char *path, int *size, void **map)
  227. {
  228. int fd;
  229. struct stat s;
  230. if (stat(path, &s))
  231. {
  232. perror("stat()");
  233. return -1;
  234. }
  235. if ((fd = open(path, O_RDWR)) == -1)
  236. {
  237. perror("open()");
  238. return -2;
  239. }
  240. *size = s.st_size;
  241. *map = mmap(NULL, *size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  242. if (*map == MAP_FAILED)
  243. {
  244. close(fd);
  245. perror("mmap()");
  246. return -3;
  247. }
  248. return fd;
  249. }
  250. int main(int argc, char **argv)
  251. {
  252. int i, j, fd, sz;
  253. int found = 0;
  254. uint16_t ath_ko_machine = 0;
  255. void *map;
  256. char *tmp = NULL, cmd[PATH_MAX * 2 + 4];
  257. if (argc < 2)
  258. {
  259. printf("Usage: %s module.ko\n", argv[0]);
  260. exit(1);
  261. }
  262. fd = tryopen(argv[1], &sz, &map);
  263. if (fd == -3)
  264. {
  265. printf("Memory mapping failed (missing fs support?), retrying from tmpfs\n");
  266. tmp = tmpnam(NULL);
  267. sprintf(cmd, "cp %s %s", argv[1], tmp);
  268. system(cmd);
  269. fd = tryopen(tmp, &sz, &map);
  270. }
  271. if (fd < 0)
  272. {
  273. if (tmp)
  274. unlink(tmp);
  275. exit(1);
  276. }
  277. check_endianess(map);
  278. ath_ko_machine = check_ath_ko(map, argv[1]);
  279. for (i = 0; i < (sz - sizeof(search_regdomains[0].reg)); i += sizeof(uint32_t))
  280. {
  281. if (ath_ko_machine)
  282. {
  283. for (j = 0; j < sizeof(search_insns)/sizeof(search_insns[0]); j++)
  284. {
  285. if (search_insns[j].machine != ath_ko_machine)
  286. continue;
  287. if (!patch_insn(map + i, &search_insns[j]))
  288. {
  289. printf("Patching @ 0x%08x: %s\n", i, search_insns[j].desc);
  290. found = 1;
  291. }
  292. }
  293. }
  294. for (j = 0; j < (sizeof(search_regdomains)/sizeof(search_regdomains[0])); j++)
  295. {
  296. if (!patch_regdomain(map + i, &search_regdomains[j].reg))
  297. {
  298. printf("Patching @ 0x%08x: %s\n", i, search_regdomains[j].desc);
  299. found = 1;
  300. }
  301. }
  302. }
  303. if (munmap(map, sz))
  304. {
  305. perror("munmap()");
  306. exit(1);
  307. }
  308. if (tmp)
  309. {
  310. if (found)
  311. {
  312. sprintf(cmd, "cp %s %s", tmp, argv[1]);
  313. system(cmd);
  314. }
  315. unlink(tmp);
  316. }
  317. close(fd);
  318. if (!found)
  319. {
  320. printf("Unable to find regulatory rules (already patched?)\n");
  321. exit(1);
  322. }
  323. return 0;
  324. }