(* Memory info command for virtual domains. (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc. http://libvirt.org/ 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. *) open Printf open Virt_mem_gettext.Gettext open Virt_mem_utils open Virt_mem_types open Kernel_net_device open Kernel_net let max_net_devices = 10000 let rec find_net_devices debug image ksymmap kernel_version = if not (net_device_known kernel_version) then ( eprintf (f_"%s: %s: unknown kernel version Try a newer version of virt-mem, or if the guest is not from a supported Linux distribution, see this page about adding support: http://et.redhat.com/~rjones/virt-mem/faq.html\n") image.domname kernel_version; image, None ) else ( let size = net_device_size kernel_version in (* In kernels < ~ 2.6.22, this is a simple linked list: * dev_base -> next -> next * In kernels >= 2.6.23, this is a list_head: * dev_base_head -> list_head dev_list -> ... *) let map = let { field_available = available } = field_signature_of_net_device_next kernel_version in if available then Some map_next else ( let { field_available = available; field_offset = offset } = field_signature_of_net_device_dev_list'next kernel_version in if available then Some (map_dev_list offset) else ( eprintf (f_"%s: kernel net_device table is not linked through either next pointer or dev_list list_head. Cannot read net devices.\n") image.domname; None ) ) in match map with | None -> image, None | Some map -> (* What is the starting point for iteration? In older kernels * it was the symbol 'dev_base'. Then briefly (2.6.22-2.6.24) * it became 'sruct list_head dev_base_head'. Then when net * namespaces were introduced (>= 2.6.25) it became 'struct * list_head init_net.dev_base_head'. *) let addr = try Some (Ksymmap.find "dev_base" ksymmap) with Not_found -> try let addr = Ksymmap.find "dev_base_head" ksymmap in let addr = Virt_mem_mmap.follow_pointer image.mem addr in Some addr with Not_found -> try let addr = Ksymmap.find "init_net" ksymmap in if not (net_known kernel_version) then ( eprintf (f_"%s: struct net not available in this kernel version.\n") image.domname; raise Not_found ); let init_net = get_net kernel_version image.mem addr in let addr = init_net.net_dev_base_head'next in Some addr with Not_found -> eprintf (f_"%s: cannot find dev_base, dev_base_head or init_net symbols in kernel image.\n") image.domname; None in match addr with | None -> image, None | Some addr -> (* Map over the structure using previously defined map function. *) let image, netdevs = map image kernel_version addr size ( fun netdev -> { netdev_name = truncate_c_string netdev.net_device_name; netdev_dev_addr = netdev.net_device_dev_addr } ) in image, Some netdevs ) (* Map dev_base_head -> list_head dev_list -> ... *) and map_dev_list offset image kernel_version first_addr size f = eprintf "map_dev_list: first_addr is %Lx\n" first_addr; (* The list_head points into the middle of the structure. * Adjust this address to point to the start of the * structure. *) let addr = Int64.sub first_addr (Int64.of_int offset) in eprintf "map_dev_list: after subtracting, addr is %Lx\n" addr; let rec loop i image acc addr = if i <= max_net_devices then ( eprintf "map_dev_list: called at %Lx\n" addr; let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in let image = if not mapped then Virt_mem_types.load_memory image addr size else image in let dev = get_net_device kernel_version image.mem addr in eprintf "map_dev_list: %Lx %S\n" addr dev.net_device_name; let acc = f dev :: acc in let addr = Option.get dev.net_device_dev_list'next in if addr <> first_addr then loop (i+1) image acc addr else image, acc ) else failwith (sprintf (f_"%s: too many network devices") image.domname); in loop 0 image [] addr (* Iterate dev_base -> next -> next ... *) and map_next image kernel_version addr size f = let rec loop i image acc addr = if i <= max_net_devices then ( if addr <> 0L then ( let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in let image = if not mapped then Virt_mem_types.load_memory image addr size else image in let dev = get_net_device kernel_version image.mem addr in eprintf "map_next: %S\n" dev.net_device_name; let acc = f dev :: acc in let addr = match dev.net_device_next with | None -> assert false | Some addr -> addr in loop (i+1) image acc addr ) else image, acc ) else failwith (sprintf (f_"%s: too many network devices") image.domname); in loop 0 image [] addr