routerboot.c 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Driver for MikroTik RouterBoot flash data. Common routines.
  4. *
  5. * Copyright (C) 2020 Thibaut VARÈNE <[email protected]>
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License version 2 as published
  9. * by the Free Software Foundation.
  10. */
  11. #include <linux/types.h>
  12. #include <linux/module.h>
  13. #include <linux/kernel.h>
  14. #include <linux/sysfs.h>
  15. #include "routerboot.h"
  16. static struct kobject *rb_kobj;
  17. /**
  18. * routerboot_tag_find() - Locate a given tag in routerboot config data.
  19. * @bufhead: the buffer to look into. Must start with a tag node.
  20. * @buflen: size of bufhead
  21. * @tag_id: the tag identifier to look for
  22. * @pld_ofs: will be updated with tag payload offset in bufhead, if tag found
  23. * @pld_len: will be updated with tag payload size, if tag found
  24. *
  25. * This incarnation of tag_find() does only that: it finds a specific routerboot
  26. * tag node in the input buffer. Routerboot tag nodes are u32 values:
  27. * - The low nibble is the tag identification number,
  28. * - The high nibble is the tag payload length (node excluded) in bytes.
  29. * The payload immediately follows the tag node. Tag nodes are 32bit-aligned.
  30. * The returned pld_ofs will always be aligned. pld_len may not end on 32bit
  31. * boundary (the only known case is when parsing ERD data).
  32. * The nodes are cpu-endian on the flash media. The payload is cpu-endian when
  33. * applicable. Tag nodes are not ordered (by ID) on flash.
  34. *
  35. * Return: 0 on success (tag found) or errno
  36. */
  37. int routerboot_tag_find(const u8 *bufhead, const size_t buflen, const u16 tag_id,
  38. u16 *pld_ofs, u16 *pld_len)
  39. {
  40. const u32 *datum, *bufend;
  41. u32 node;
  42. u16 id, len;
  43. int ret;
  44. if (!bufhead || !tag_id)
  45. return -EINVAL;
  46. ret = -ENOENT;
  47. datum = (const u32 *)bufhead;
  48. bufend = (const u32 *)(bufhead + buflen);
  49. while (datum < bufend) {
  50. node = *datum++;
  51. /* Tag list ends with null node */
  52. if (!node)
  53. break;
  54. id = node & 0xFFFF;
  55. len = node >> 16;
  56. if (tag_id == id) {
  57. if (datum >= bufend)
  58. break;
  59. if (pld_ofs)
  60. *pld_ofs = (u16)((u8 *)datum - bufhead);
  61. if (pld_len)
  62. *pld_len = len;
  63. ret = 0;
  64. break;
  65. }
  66. /*
  67. * The only known situation where len may not end on 32bit
  68. * boundary is within ERD data. Since we're only extracting
  69. * one tag (the first and only one) from that data, we should
  70. * never need to forcefully ALIGN(). Do it anyway, this is not a
  71. * performance path.
  72. */
  73. len = ALIGN(len, sizeof(*datum));
  74. datum += len / sizeof(*datum);
  75. }
  76. return ret;
  77. }
  78. /**
  79. * routerboot_rle_decode() - Simple RLE (MikroTik variant) decoding routine.
  80. * @in: input buffer to decode
  81. * @inlen: size of in
  82. * @out: output buffer to write decoded data to
  83. * @outlen: pointer to out size when function is called, will be updated with
  84. * size of decoded output on return
  85. *
  86. * MikroTik's variant of RLE operates as follows, considering a signed run byte:
  87. * - positive run => classic RLE
  88. * - negative run => the next -<run> bytes must be copied verbatim
  89. * The API is matched to the lzo1x routines for convenience.
  90. *
  91. * NB: The output buffer cannot overlap with the input buffer.
  92. *
  93. * Return: 0 on success or errno
  94. */
  95. int routerboot_rle_decode(const u8 *in, size_t inlen, u8 *out, size_t *outlen)
  96. {
  97. int ret, run, nbytes; // use native types for speed
  98. u8 byte;
  99. if (!in || (inlen < 2) || !out)
  100. return -EINVAL;
  101. ret = -ENOSPC;
  102. nbytes = 0;
  103. while (inlen >= 2) {
  104. run = *in++;
  105. inlen--;
  106. /* Verbatim copies */
  107. if (run & 0x80) {
  108. /* Invert run byte sign */
  109. run = ~run & 0xFF;
  110. run++;
  111. if (run > inlen)
  112. goto fail;
  113. inlen -= run;
  114. nbytes += run;
  115. if (nbytes > *outlen)
  116. goto fail;
  117. /* Basic memcpy */
  118. while (run-- > 0)
  119. *out++ = *in++;
  120. }
  121. /* Stream of half-words RLE: <run><byte>. run == 0 is ignored */
  122. else {
  123. byte = *in++;
  124. inlen--;
  125. nbytes += run;
  126. if (nbytes > *outlen)
  127. goto fail;
  128. while (run-- > 0)
  129. *out++ = byte;
  130. }
  131. }
  132. ret = 0;
  133. fail:
  134. *outlen = nbytes;
  135. return ret;
  136. }
  137. static int __init routerboot_init(void)
  138. {
  139. rb_kobj = kobject_create_and_add("mikrotik", firmware_kobj);
  140. if (!rb_kobj)
  141. return -ENOMEM;
  142. /*
  143. * We ignore the following return values and always register.
  144. * These init() routines are designed so that their failed state is
  145. * always manageable by the corresponding exit() calls.
  146. */
  147. rb_hardconfig_init(rb_kobj);
  148. rb_softconfig_init(rb_kobj);
  149. return 0;
  150. }
  151. static void __exit routerboot_exit(void)
  152. {
  153. rb_softconfig_exit();
  154. rb_hardconfig_exit();
  155. kobject_put(rb_kobj); // recursive afaict
  156. }
  157. /* Common routines */
  158. ssize_t routerboot_tag_show_string(const u8 *pld, u16 pld_len, char *buf)
  159. {
  160. return scnprintf(buf, pld_len+1, "%s\n", pld);
  161. }
  162. module_init(routerboot_init);
  163. module_exit(routerboot_exit);
  164. MODULE_LICENSE("GPL v2");
  165. MODULE_DESCRIPTION("MikroTik RouterBoot sysfs support");
  166. MODULE_AUTHOR("Thibaut VARENE");