/* virt-dmesg * (C) Copyright 2008-2011 Red Hat Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* This contains functions written in C. We can drop into C wherever * we need to do something that would be much faster than OCaml or * where it's just awkward to do something in OCaml or much easier in * C. */ #include #include #include #include #include #include #include #include #include #include #include #include /* This vital function returns the offset of the virtual address * relative to the base address. If the virtual address is outside * the kernel, then it returns -1. * * Note that the function is declared "noalloc" for speed. It must * not allocate anything on the OCaml heap. * * str_mapping : int64 -> int64 -> int -> int */ value virt_dmesg_str_mapping (value base_addrv, value addrv, value str_lenv) { uint64_t base_addr = Int64_val (base_addrv); uint64_t addr = Int64_val (addrv); int str_len = Int_val (str_lenv); uint64_t off; off = addr - base_addr; if (off < str_len) return Val_int (off); else return Val_int (-1); } /* NOTE: Declared as a "noalloc" function for speed. */ value virt_dmesg_addr_compare (value a1v, value a2v) { uint64_t a1 = Int64_val (a1v); uint64_t a2 = Int64_val (a2v); if (a1 < a2) return (Val_int (-1)); else if (a1 > a2) return (Val_int (1)); else return (Val_int (0)); } /* Find 'needle' in 'haystack', but start searching haystack from 'off'. * * strstr_from : string -> string -> int -> int */ value virt_dmesg_strstr_from (value haystackv, value needlev, value offv) { CAMLparam3 (haystackv, needlev, offv); const char *haystack = String_val (haystackv); int off = Int_val (offv); const char *r; ptrdiff_t i; /* Because OCaml strings can contain '\0' characters, use memmem * not strstr. */ r = memmem (haystack + off, caml_string_length (haystackv) - off, String_val (needlev), caml_string_length (needlev)); if (r == 0) caml_raise_not_found (); i = r - haystack; CAMLreturn (Val_int (i)); } /* This is like 'strstr_from' but it only tests for aligned 'needle', * eg. if 'needle' was 4 bytes long then it would only look every 4 * bytes. */ value virt_dmesg_strstr_from_aligned (value haystackv, value needlev, value offv) { CAMLparam3 (haystackv, needlev, offv); const char *haystack = String_val (haystackv); size_t haystack_len = caml_string_length (haystackv); const char *needle = String_val (needlev); size_t needle_len = caml_string_length (needlev); int off = Int_val (offv); int r; for (r = off; r < haystack_len; r += needle_len) { if (memcmp (haystack + r, needle, needle_len) == 0) goto found; } caml_raise_not_found (); found: CAMLreturn (Val_int (r)); } /* Extract endian 32 and 64 bit integers (or pointers) from strings. */ value virt_dmesg_str_get_le32 (value strv, value offv) { CAMLparam2 (strv, offv); CAMLlocal1 (rv); const char *str = String_val (strv); int off = Int_val (offv); uint64_t r; r = le32toh (*(const uint32_t *)(str + off)); rv = caml_copy_int64 (r); CAMLreturn (rv); } value virt_dmesg_str_get_be32 (value strv, value offv) { CAMLparam2 (strv, offv); CAMLlocal1 (rv); const char *str = String_val (strv); int off = Int_val (offv); uint64_t r; r = be32toh (*(const uint32_t *)(str + off)); rv = caml_copy_int64 (r); CAMLreturn (rv); } value virt_dmesg_str_get_le64 (value strv, value offv) { CAMLparam2 (strv, offv); CAMLlocal1 (rv); const char *str = String_val (strv); int off = Int_val (offv); uint64_t r; r = le64toh (*(const uint64_t *)(str + off)); rv = caml_copy_int64 (r); CAMLreturn (rv); } value virt_dmesg_str_get_be64 (value strv, value offv) { CAMLparam2 (strv, offv); CAMLlocal1 (rv); const char *str = String_val (strv); int off = Int_val (offv); uint64_t r; r = be64toh (*(const uint64_t *)(str + off)); rv = caml_copy_int64 (r); CAMLreturn (rv); } /* Convert integers of various endianness/word size to strings of bytes. * * str_of_*32 returns a 4 byte string * str_of_*64 returns an 8 byte string */ value virt_dmesg_str_of_le32 (value addrv) { CAMLparam1 (addrv); CAMLlocal1 (rv); const char *str; rv = caml_alloc_string (4); str = String_val (rv); *(uint32_t *)str = htole32 (Int64_val (addrv)); CAMLreturn (rv); } value virt_dmesg_str_of_be32 (value addrv) { CAMLparam1 (addrv); CAMLlocal1 (rv); const char *str; rv = caml_alloc_string (4); str = String_val (rv); *(uint32_t *)str = htobe32 (Int64_val (addrv)); CAMLreturn (rv); } value virt_dmesg_str_of_le64 (value addrv) { CAMLparam1 (addrv); CAMLlocal1 (rv); const char *str; rv = caml_alloc_string (8); str = String_val (rv); *(uint64_t *)str = htole64 (Int64_val (addrv)); CAMLreturn (rv); } value virt_dmesg_str_of_be64 (value addrv) { CAMLparam1 (addrv); CAMLlocal1 (rv); const char *str; rv = caml_alloc_string (8); str = String_val (rv); *(uint64_t *)str = htobe64 (Int64_val (addrv)); CAMLreturn (rv); } /* Get NUL-terminated (ASCIIZ) string at the given offset. * * get_asciiz : string -> int -> string */ value virt_dmesg_get_asciiz (value strv, value offv) { CAMLparam2 (strv, offv); CAMLlocal1 (rv); const char *str = String_val (strv); size_t len = caml_string_length (strv); int off = Int_val (offv); size_t rlen; /* Work out the length of the return value. */ for (rlen = 0; off+rlen < len; ++rlen) if (str[off+rlen] == '\0') break; rv = caml_alloc_string (rlen); /* Previous allocation could have moved the input string, so ... */ str = String_val (strv); memcpy (String_val (rv), &str[off], rlen); CAMLreturn (rv); } /* Return true iff what is at the offset is plausibly a C programming * language identifier. * * is_C_ident : string -> int -> bool */ value virt_dmesg_is_C_ident (value strv, value offv) { CAMLparam2 (strv, offv); const char *str = String_val (strv); size_t len = caml_string_length (strv); int off = Int_val (offv); size_t i; int r = 1 /* true */; if (str[off] != '_' && !(str[off] >= 'A' && str[off] <= 'Z' || str[off] >= 'a' && str[off] <= 'z')) { r = 0 /* false */; goto out; } for (i = 1; off+i < len; ++i) { if (str[off+i] == '\0') break; if (str[off+i] != '_' && !(str[off+i] >= 'A' && str[off+i] <= 'Z' || str[off+i] >= 'a' && str[off+i] <= 'z' || str[off+i] >= '0' && str[off+i] <= '9')) { r = 0 /* false */; goto out; } } out: CAMLreturn (Val_int (r)); } /* external crc32_of_string : string -> int32 */ value virt_dmesg_crc32_of_string (value strv) { CAMLparam1 (strv); CAMLlocal1 (rv); uLong r; r = crc32 (0, Z_NULL, 0); r = crc32 (r, String_val (strv), caml_string_length (strv)); rv = caml_copy_int32 (r); CAMLreturn (rv); }