2 * (C) Copyright 2008-2011 Red Hat Inc.
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.
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.
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.
23 (* C functions, see [c_utils.c] file. *)
24 external str_mapping : int64 -> int64 -> int -> int
25 = "virt_dmesg_str_mapping" "noalloc"
26 external strstr_from : string -> string -> int -> int = "virt_dmesg_strstr_from"
27 external strstr_from_aligned : string -> string -> int -> int
28 = "virt_dmesg_strstr_from_aligned"
29 external str_get_le32 : string -> int -> int64 = "virt_dmesg_str_get_le32"
30 external str_get_be32 : string -> int -> int64 = "virt_dmesg_str_get_be32"
31 external str_get_le64 : string -> int -> int64 = "virt_dmesg_str_get_le64"
32 external str_get_be64 : string -> int -> int64 = "virt_dmesg_str_get_be64"
33 external str_of_le32 : int64 -> string = "virt_dmesg_str_of_le32"
34 external str_of_be32 : int64 -> string = "virt_dmesg_str_of_be32"
35 external str_of_le64 : int64 -> string = "virt_dmesg_str_of_le64"
36 external str_of_be64 : int64 -> string = "virt_dmesg_str_of_be64"
37 external get_asciiz : string -> int -> string = "virt_dmesg_get_asciiz"
38 external is_C_ident : string -> int -> bool = "virt_dmesg_is_C_ident"
39 external crc32_of_string : string -> int32 = "virt_dmesg_crc32_of_string"
40 external addr_compare : int64 -> int64 -> int
41 = "virt_dmesg_addr_compare" "noalloc"
44 (* Note that 'base_addr' is the guest virtual address of the first
45 * byte in the 'data' string.
54 and endian = BigEndian | LittleEndian
56 and wordsize = Word32 | Word64
58 let string_of_endian = function
59 | BigEndian -> "big endian"
60 | LittleEndian -> "little endian"
62 let string_of_wordsize = function
66 let succ_word { wordsize = wordsize } addr =
67 match wordsize with Word32 -> addr +^ 4L | Word64 -> addr +^ 8L
69 let pred_word { wordsize = wordsize } addr =
70 match wordsize with Word32 -> addr -^ 4L | Word64 -> addr -^ 8L
72 let succ_align { wordsize = wordsize } addr =
73 let mask = match wordsize with Word32 -> 3L | Word64 -> 7L in
74 (addr +^ mask) &^ (Int64.lognot mask)
76 let bytes_of_wordsize = function
77 | { wordsize = Word32 } -> 4
78 | { wordsize = Word64 } -> 8
80 let create base_addr f =
81 (* XXX Make kernel size configurable. *)
82 let len = 16 * 1024 * 1024 - 65536 in
83 let top_addr = base_addr +^ Int64.of_int len in
85 (* As we're loading the kernel, keep running stats on the number of
86 * valid (or "probably valid") pointers found that are:
87 * - 32 bit, aligned, little endian
88 * - 32 bit, aligned, big endian
89 * - 64 bit, aligned, little endian
90 * - 64 bit, aligned, big endian
91 * and the total amount of data read. Use these stats to heuristically
92 * determine endianness and word size, and also reject the kernel
93 * early by raising [Not_found] if it doesn't look like a kernel.
95 * Also a characteristic of qemu is that if you ask for an invalid
96 * virtual address, it returns the same set of blocks of random data
97 * over and over. Detect this by keeping a count of the checksum
100 let update_stats, get_endian_wordsize =
101 let blocks = ref 0 in
103 let le32 = ref 0 and be32 = ref 0 and le64 = ref 0 and be64 = ref 0 in
104 let max_le32_pc = ref 0. and max_be32_pc = ref 0.
105 and max_le64_pc = ref 0. and max_be64_pc = ref 0. in
106 let checksums = Counter.create () in
108 let update_stats data =
111 let n = String.length data in
112 assert (n mod 8 = 0);
116 let p = str_get_le32 data (i*4) in
117 if p >= base_addr && p < top_addr then
119 let p = str_get_be32 data (i*4) in
120 if p >= base_addr && p < top_addr then
124 let p = str_get_le64 data (i*8) in
125 if p >= base_addr && p < top_addr then
127 let p = str_get_be64 data (i*8) in
128 if p >= base_addr && p < top_addr then
132 (* Because the kernel is likely to be smaller than the memory we
133 * are reading, keep track of the running maximum of the
134 * percentage of memory which contains pointers. The percentages
135 * will decline once we run off the end of the kernel.
137 if !size > 1_000_000 then (
138 let percent v w = float v *. 100. /. (float (!size / w)) in
139 max_le32_pc := max !max_le32_pc (percent !le32 4);
140 max_be32_pc := max !max_be32_pc (percent !be32 4);
141 max_le64_pc := max !max_le64_pc (percent !le64 8);
142 max_be64_pc := max !max_be64_pc (percent !be64 8);
144 (* If these are all < 0.1% even after a millions of bytes have
145 * been read, then we should bail. It's not a kernel.
147 if !size > 8_000_000 &&
148 !max_le32_pc < 0.1 && !max_be32_pc < 0.1 &&
149 !max_le64_pc < 0.1 && !max_be64_pc < 0.1 then (
150 debug "doesn't look like a kernel: le32 %g%% be32 %g%% le64 %g%% be64 %g%%"
151 !max_le32_pc !max_be32_pc !max_le64_pc !max_be64_pc;
156 (* Update block CRC. Bail if qemu is just giving us the same
157 * block of data over and over again. However note that most
158 * kernels are only about 10 MB in size, and because we read a
159 * fixed amount (usually 16 MB) at the end of this read we'll be
160 * reading unmapped memory which can trip this heuristic.
161 * Therefore only run this heuristic for the first 4 MB of
164 if !size > 2 * 1024 * 1024 && !size <= 4 * 1024 * 1024 then (
165 let crc = crc32_of_string data in
166 Counter.incr checksums crc;
168 if Counter.get checksums crc > !blocks/4 then (
169 debug "looks like unmapped memory";
174 and get_endian_wordsize () =
175 if !max_le32_pc > !max_be32_pc && !max_le32_pc > !max_le64_pc &&
176 !max_le32_pc > !max_be64_pc then
178 else if !max_be32_pc > !max_le64_pc && !max_be32_pc > !max_be64_pc then
180 else if !max_le64_pc > !max_be64_pc then
186 update_stats, get_endian_wordsize
189 (* Loop loading the kernel. *)
192 let str = f (base_addr +^ Int64.of_int i) in
194 loop (i + String.length str) (str :: acc)
198 let strs = loop 0 [] in
199 let data = String.concat "" strs in
200 assert (String.length data = len);
202 let endian, wordsize = get_endian_wordsize () in
204 { data = data; base_addr = base_addr; endian = endian; wordsize = wordsize }
206 let rec find_first k str =
207 find_from k k.base_addr str
210 let rec loop addr acc =
212 let a = find_from k addr str in
213 loop (a +^ 1L) (a :: acc)
214 with Not_found -> List.rev acc
218 and find_from { data = data; base_addr = base_addr } addr str =
219 let off = str_mapping base_addr addr (String.length data) in
221 let found_off = strstr_from data str off in
222 base_addr +^ Int64.of_int found_off
224 let find_all_pointers ({ endian = endian; wordsize = wordsize } as k) ptr =
226 match endian, wordsize with
227 | LittleEndian, Word32 -> str_of_le32 ptr
228 | BigEndian, Word32 -> str_of_be32 ptr
229 | LittleEndian, Word64 -> str_of_le64 ptr
230 | BigEndian, Word64 -> str_of_be64 ptr in
232 let find_from_aligned addr =
233 let off = str_mapping k.base_addr addr (String.length k.data) in
235 let found_off = strstr_from_aligned k.data str off in
236 k.base_addr +^ Int64.of_int found_off
239 let rec loop addr acc =
241 let a = find_from_aligned addr in
242 loop (succ_word k a) (a :: acc)
243 with Not_found -> List.rev acc
247 let follow_pointer k addr =
248 let off = str_mapping k.base_addr addr (String.length k.data) in
250 match k.endian, k.wordsize with
251 | LittleEndian, Word32 -> str_get_le32 k.data off
252 | BigEndian, Word32 -> str_get_be32 k.data off
253 | LittleEndian, Word64 -> str_get_le64 k.data off
254 | BigEndian, Word64 -> str_get_be64 k.data off
256 let is_mapped k addr =
257 let off = str_mapping k.base_addr addr (String.length k.data) in
260 let get_memory k addr len =
261 let off = str_mapping k.base_addr addr (String.length k.data) in
264 assert (len + off <= String.length k.data);
265 String.sub k.data off len
267 let get_byte k addr =
268 let off = str_mapping k.base_addr addr (String.length k.data) in
270 Char.code (String.unsafe_get k.data off)
272 let get_int32 k addr =
273 let off = str_mapping k.base_addr addr (String.length k.data - 4) in
276 | LittleEndian -> str_get_le32 k.data off
277 | BigEndian -> str_get_be32 k.data off
279 let get_int64 k addr =
280 let off = str_mapping k.base_addr addr (String.length k.data - 8) in
283 | LittleEndian -> str_get_le64 k.data off
284 | BigEndian -> str_get_be64 k.data off
286 let get_string k addr =
287 let off = str_mapping k.base_addr addr (String.length k.data) in
289 get_asciiz k.data off
291 let is_C_identifier k addr =
292 let off = str_mapping k.base_addr addr (String.length k.data) in
294 is_C_ident k.data off