Version 0.2.0.
[virt-dmesg.git] / src / c_utils.c
1 /* virt-dmesg
2  * (C) Copyright 2008-2011 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* This contains functions written in C.  We can drop into C wherever
20  * we need to do something that would be much faster than OCaml or
21  * where it's just awkward to do something in OCaml or much easier in
22  * C.
23  */
24
25 #include <config.h>
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <string.h>
31 #include <inttypes.h>
32 #include <endian.h>
33
34 #include <zlib.h>
35
36 #include <caml/alloc.h>
37 #include <caml/fail.h>
38 #include <caml/memory.h>
39 #include <caml/mlvalues.h>
40
41 /* This vital function returns the offset of the virtual address
42  * relative to the base address.  If the virtual address is outside
43  * the kernel, then it returns -1.
44  *
45  * Note that the function is declared "noalloc" for speed.  It must
46  * not allocate anything on the OCaml heap.
47  *
48  * str_mapping : int64 -> int64 -> int -> int
49  */
50 value
51 virt_dmesg_str_mapping (value base_addrv, value addrv, value str_lenv)
52 {
53   uint64_t base_addr = Int64_val (base_addrv);
54   uint64_t addr = Int64_val (addrv);
55   int str_len = Int_val (str_lenv);
56   uint64_t off;
57
58   off = addr - base_addr;
59   if (off < str_len)
60     return Val_int (off);
61   else
62     return Val_int (-1);
63 }
64
65 /* NOTE: Declared as a "noalloc" function for speed. */
66 value
67 virt_dmesg_addr_compare (value a1v, value a2v)
68 {
69   uint64_t a1 = Int64_val (a1v);
70   uint64_t a2 = Int64_val (a2v);
71
72   if (a1 < a2)
73     return (Val_int (-1));
74   else if (a1 > a2)
75     return (Val_int (1));
76   else
77     return (Val_int (0));
78 }
79
80 /* Find 'needle' in 'haystack', but start searching haystack from 'off'.
81  *
82  * strstr_from : string -> string -> int -> int
83  */
84 value
85 virt_dmesg_strstr_from (value haystackv, value needlev, value offv)
86 {
87   CAMLparam3 (haystackv, needlev, offv);
88   const char *haystack = String_val (haystackv);
89   int off = Int_val (offv);
90   const char *r;
91   ptrdiff_t i;
92
93   /* Because OCaml strings can contain '\0' characters, use memmem
94    * not strstr.
95    */
96   r = memmem (haystack + off, caml_string_length (haystackv) - off,
97               String_val (needlev), caml_string_length (needlev));
98   if (r == 0)
99     caml_raise_not_found ();
100
101   i = r - haystack;
102   CAMLreturn (Val_int (i));
103 }
104
105 /* This is like 'strstr_from' but it only tests for aligned 'needle',
106  * eg. if 'needle' was 4 bytes long then it would only look every 4
107  * bytes.
108  */
109 value
110 virt_dmesg_strstr_from_aligned (value haystackv, value needlev, value offv)
111 {
112   CAMLparam3 (haystackv, needlev, offv);
113   const char *haystack = String_val (haystackv);
114   size_t haystack_len = caml_string_length (haystackv);
115   const char *needle = String_val (needlev);
116   size_t needle_len = caml_string_length (needlev);
117   int off = Int_val (offv);
118   int r;
119
120   for (r = off; r < haystack_len; r += needle_len) {
121     if (memcmp (haystack + r, needle, needle_len) == 0)
122       goto found;
123   }
124   caml_raise_not_found ();
125
126  found:
127   CAMLreturn (Val_int (r));
128 }
129
130 /* Extract endian 32 and 64 bit integers (or pointers) from strings. */
131 value
132 virt_dmesg_str_get_le32 (value strv, value offv)
133 {
134   CAMLparam2 (strv, offv);
135   CAMLlocal1 (rv);
136   const char *str = String_val (strv);
137   int off = Int_val (offv);
138   uint64_t r;
139
140   r = le32toh (*(const uint32_t *)(str + off));
141   rv = caml_copy_int64 (r);
142
143   CAMLreturn (rv);
144 }
145
146 value
147 virt_dmesg_str_get_be32 (value strv, value offv)
148 {
149   CAMLparam2 (strv, offv);
150   CAMLlocal1 (rv);
151   const char *str = String_val (strv);
152   int off = Int_val (offv);
153   uint64_t r;
154
155   r = be32toh (*(const uint32_t *)(str + off));
156   rv = caml_copy_int64 (r);
157
158   CAMLreturn (rv);
159 }
160
161 value
162 virt_dmesg_str_get_le64 (value strv, value offv)
163 {
164   CAMLparam2 (strv, offv);
165   CAMLlocal1 (rv);
166   const char *str = String_val (strv);
167   int off = Int_val (offv);
168   uint64_t r;
169
170   r = le64toh (*(const uint64_t *)(str + off));
171   rv = caml_copy_int64 (r);
172
173   CAMLreturn (rv);
174 }
175
176 value
177 virt_dmesg_str_get_be64 (value strv, value offv)
178 {
179   CAMLparam2 (strv, offv);
180   CAMLlocal1 (rv);
181   const char *str = String_val (strv);
182   int off = Int_val (offv);
183   uint64_t r;
184
185   r = be64toh (*(const uint64_t *)(str + off));
186   rv = caml_copy_int64 (r);
187
188   CAMLreturn (rv);
189 }
190
191 /* Convert integers of various endianness/word size to strings of bytes.
192  *
193  * str_of_*32 returns a 4 byte string
194  * str_of_*64 returns an 8 byte string
195  */
196 value
197 virt_dmesg_str_of_le32 (value addrv)
198 {
199   CAMLparam1 (addrv);
200   CAMLlocal1 (rv);
201   const char *str;
202
203   rv = caml_alloc_string (4);
204   str = String_val (rv);
205   *(uint32_t *)str = htole32 (Int64_val (addrv));
206
207   CAMLreturn (rv);
208 }
209
210 value
211 virt_dmesg_str_of_be32 (value addrv)
212 {
213   CAMLparam1 (addrv);
214   CAMLlocal1 (rv);
215   const char *str;
216
217   rv = caml_alloc_string (4);
218   str = String_val (rv);
219   *(uint32_t *)str = htobe32 (Int64_val (addrv));
220
221   CAMLreturn (rv);
222 }
223
224 value
225 virt_dmesg_str_of_le64 (value addrv)
226 {
227   CAMLparam1 (addrv);
228   CAMLlocal1 (rv);
229   const char *str;
230
231   rv = caml_alloc_string (8);
232   str = String_val (rv);
233   *(uint64_t *)str = htole64 (Int64_val (addrv));
234
235   CAMLreturn (rv);
236 }
237
238 value
239 virt_dmesg_str_of_be64 (value addrv)
240 {
241   CAMLparam1 (addrv);
242   CAMLlocal1 (rv);
243   const char *str;
244
245   rv = caml_alloc_string (8);
246   str = String_val (rv);
247   *(uint64_t *)str = htobe64 (Int64_val (addrv));
248
249   CAMLreturn (rv);
250 }
251
252 /* Get NUL-terminated (ASCIIZ) string at the given offset.
253  *
254  * get_asciiz : string -> int -> string
255  */
256 value
257 virt_dmesg_get_asciiz (value strv, value offv)
258 {
259   CAMLparam2 (strv, offv);
260   CAMLlocal1 (rv);
261   const char *str = String_val (strv);
262   size_t len = caml_string_length (strv);
263   int off = Int_val (offv);
264   size_t rlen;
265
266   /* Work out the length of the return value. */
267   for (rlen = 0; off+rlen < len; ++rlen)
268     if (str[off+rlen] == '\0')
269       break;
270
271   rv = caml_alloc_string (rlen);
272   /* Previous allocation could have moved the input string, so ...  */
273   str = String_val (strv);
274
275   memcpy (String_val (rv), &str[off], rlen);
276
277   CAMLreturn (rv);
278 }
279
280 /* Return true iff what is at the offset is plausibly a C programming
281  * language identifier.
282  *
283  * is_C_ident : string -> int -> bool
284  */
285 value
286 virt_dmesg_is_C_ident (value strv, value offv)
287 {
288   CAMLparam2 (strv, offv);
289   const char *str = String_val (strv);
290   size_t len = caml_string_length (strv);
291   int off = Int_val (offv);
292   size_t i;
293   int r = 1 /* true */;
294
295   if (str[off] != '_' && !(str[off] >= 'A' && str[off] <= 'Z' ||
296                            str[off] >= 'a' && str[off] <= 'z')) {
297     r = 0 /* false */;
298     goto out;
299   }
300
301   for (i = 1; off+i < len; ++i) {
302     if (str[off+i] == '\0')
303       break;
304
305     if (str[off+i] != '_' && !(str[off+i] >= 'A' && str[off+i] <= 'Z' ||
306                                str[off+i] >= 'a' && str[off+i] <= 'z' ||
307                                str[off+i] >= '0' && str[off+i] <= '9')) {
308       r = 0 /* false */;
309       goto out;
310     }
311   }
312
313  out:
314   CAMLreturn (Val_int (r));
315 }
316
317 /* external crc32_of_string : string -> int32 */
318 value
319 virt_dmesg_crc32_of_string (value strv)
320 {
321   CAMLparam1 (strv);
322   CAMLlocal1 (rv);
323   uLong r;
324
325   r = crc32 (0, Z_NULL, 0);
326   r = crc32 (r, String_val (strv), caml_string_length (strv));
327
328   rv = caml_copy_int32 (r);
329   CAMLreturn (rv);
330 }