Qemu 2: parameter resolution

Source: Internet
Author: User
I. Use GDB to analyze qemu code

Using GDB not only can debug the code well, but also can be used to dynamically analyze the code. To debug qemu using GDB, you need to make some preparations:

1. During qemu compilation, add-enable-Debug to the parameters when the configure script is executed.

2, download a streamlined image-linux-0.2.img from the qemu official website. The linux-0.2.img is only 8 Mb in size and contains some common shell commands after startup for qemu testing.

$wget http://wiki.qemu.org/download/linux-0.2.img.bz2$bzip2 -d ./linux-0.2.img.bz2

3. Start GDB to debug qemu:

gdb --args qemu-system-x86_64 -enable-kvm -m 4096 -smp 4 linux-0.2.img

-SMP indicates the number of processors.

Ii. Data Structure Used for Parameter Parsing

The main function simulated by the qemu system is located in the Vl. c file, whether it is a qemu-system-x86_64 or a qemu-system-ppc64, it is executed from the main function in Vl. C. The following describes some data structures involved in the main function.

Qemu linked list

The linked list of qemu is defined in the include/qemu/queue. h file. There are four types:

  • Singly-linked list.
  • List: A two-way linked list. In addition to the header node, each node simultaneously points to the forward node and the next node.
  • Simple queue: A simple queue is similar to a single-chain table, but a header pointing to the end of the linked list is added, when you insert a node, you can not only insert it into the header or a node like a single-chain table, but also insert it to the end of the linked list.
  • Tail queue: similar to a simple queue, but the nodes direct to each other in two directions.

The usage of various linked lists is not described here. The usage of qemu linked lists (lists) is described only through the definition of notifierlist. The displaystate struct defined at the beginning of the main function uses notifierelist, And the notifierlist uses the linked list.

A. Table header and node Definition

Qlist_head is required to define the header. The definition is as follows:

86 #define QLIST_HEAD(name, type)                                           87 struct name {                                                            88         struct type *lh_first;  /* first element */                      89 }

Notifierlist uses qlist_head to define the header:

27 typedef struct NotifierList 28 { 29     QLIST_HEAD(, Notifier) notifiers; 30 } NotifierList;

To define a node, you must use qlist_entry, which is defined as follows:

94 #define QLIST_ENTRY(type)                                                95 struct {                                                                 96         struct type *le_next;   /* next element */                       97         struct type **le_prev;  /* address of previous next element */   98 }

Notifier nodes are defined as follows:

21 struct Notifier 22 { 23     void (*notify)(Notifier *notifier, void *data); 24     QLIST_ENTRY(Notifier) node; 25 };

B. initialize the header.

Qlist_init is used to initialize the header:

103 #define QLIST_INIT(head) do {                                           104         (head)->lh_first = NULL;                                        105 } while (/*CONSTCOND*/0)

Initialize notifierlist as follows:

19 void notifier_list_init(NotifierList *list) 20 { 21     QLIST_INIT(&list->notifiers); 22 }

C. Insert a node into the header

Insert the node to the header using qlist_insert_head:

122 #define QLIST_INSERT_HEAD(head, elm, field) do {                        123         if (((elm)->field.le_next = (head)->lh_first) != NULL)          124                 (head)->lh_first->field.le_prev = &(elm)->field.le_next;125         (head)->lh_first = (elm);                                       126         (elm)->field.le_prev = &(head)->lh_first;                       127 } while (/*CONSTCOND*/0)

Insert notifier to notifierlist:

24 void notifier_list_add(NotifierList *list, Notifier *notifier) 25 { 26     QLIST_INSERT_HEAD(&list->notifiers, notifier, node); 27 }

D. Traverse nodes

The traversal node uses qlist_foreach or qlist_foreach_safe. qlist_foreach_safe is used to prevent the node from being deleted during the traversal process. As a result, le_next is released and the traversal is interrupted.

147 #define QLIST_FOREACH(var, head, field)                                 148         for ((var) = ((head)->lh_first);                                149                 (var);                                                  150                 (var) = ((var)->field.le_next))151 152 #define QLIST_FOREACH_SAFE(var, head, field, next_var)                  153         for ((var) = ((head)->lh_first);                                154                 (var) && ((next_var) = ((var)->field.le_next), 1);      155                 (var) = (next_var))

Notifierlist uses qlist_foreach_safe when executing all callback functions:

34 void notifier_list_notify(NotifierList *list, void *data) 35 { 36     Notifier *notifier, *next; 37  38     QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { 39         notifier->notify(notifier, data); 40     } 41 }
Error and qerror

To facilitate error processing, qemu defines two data structures: Error and qerror. Error is defined in qobject/qerror. C:

101 struct Error102 {103     char *msg;104     ErrorClass err_class;105 };

Contains the error message string and enumeration type error categories. There are several error categories:

139 typedef enum ErrorClass 140 { 141     ERROR_CLASS_GENERIC_ERROR = 0, 142     ERROR_CLASS_COMMAND_NOT_FOUND = 1, 143     ERROR_CLASS_DEVICE_ENCRYPTED = 2, 144     ERROR_CLASS_DEVICE_NOT_ACTIVE = 3, 145     ERROR_CLASS_DEVICE_NOT_FOUND = 4, 146     ERROR_CLASS_K_V_M_MISSING_CAP = 5, 147     ERROR_CLASS_MAX = 6, 148 } ErrorClass;

Qemu defines several functions in util/error. C to Operate Error:

Error_set // allocate space for Error Based on the given errorclass and formatted string and assign error_set_errno // in addition to the error_set function, append the error message of the specified errno to error_copy after the formatted string // copy error error_is_set // judge whether error has been assigned and set error_get_class // obtain error errorclass error_get_pretty // obtain Error msgerror_free // release space for error and MSG

In addition, qemu defines qerror to handle more detailed error information:

22 typedef struct QError {  23     QObject_HEAD; 24     Location loc; 25     char *err_msg; 26     ErrorClass err_class; 27 } QError;

Qerror can assign values to err_msg and err_class through a series of macros:

39 #define QERR_ADD_CLIENT_FAILED  40     ERROR_CLASS_GENERIC_ERROR, "Could not add client" 41  42 #define QERR_AMBIGUOUS_PATH  43     ERROR_CLASS_GENERIC_ERROR, "Path ‘%s‘ does not uniquely identify an object" 44  45 #define QERR_BAD_BUS_FOR_DEVICE  46     ERROR_CLASS_GENERIC_ERROR, "Device ‘%s‘ can‘t go on a %s bus" 47  48 #define QERR_BASE_NOT_FOUND  49     ERROR_CLASS_GENERIC_ERROR, "Base ‘%s‘ not found"...

Location records the error location, which is defined as follows:

20 typedef struct Location { 21     /* all members are private to qemu-error.c */ 22     enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind; 23     int num; 24     const void *ptr; 25     struct Location *prev; 26 } Location;
Gmainloop

Qemu uses gmainloop in glib to achieve Io multiplexing. For details about gmainloop, refer to the implementation principle and code model of gmainloop in the blog. Since gmainloop is not the code of qemu, this article will not repeat it.

Iii. qemuoption, qemuopt, and qemu Parameter Parsing

Qemu defines qemuoption to indicate the options used to execute commands such as qemu-system-x86_64. The definition in Vl. C is as follows:

2123 typedef struct qemuoption {2124 const char * Name; // option name, for example,-device. The value of name is device2125 int flags; // flag, indicating whether the option contains a parameter, it can be 0 or has_arg (value: 0x0001) 2126 int index; // an enumerated value, such as-device, which is qemu_option_device2127 uint32_t arch_mask; // The option supports the architecture mask 2128} qemuoption;

A qemuoption array qemu_options is maintained in Vl. C to store all available options and assign values to the Array Using qemu-options-wrapper.h and qemu-options.def. The assignment statement is as follows:

2130 static const QEMUOption qemu_options[] = {2131     { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },2132 #define QEMU_OPTIONS_GENERATE_OPTIONS2133 #include "qemu-options-wrapper.h"2134     { NULL },2135 };

# Define qemu_options_generate_options select the qemu-options-wrapper.h operation, the qemu-options-wrapper.h can perform three operations:

Outputs: generate a list of enumerated values using the qemu-options.def, that is, the qemu_option_device qemu_options_generate_help: Generate help information using the qemu-options.def and output it to the standard output qemu_options_generate_options

You can expand the qemu-options-wrapper.h through the following method to view the results of the above operation, taking generating options as an example.

  1. Write # define qemu_options_generate_options In the first line of the qemu-options-wrapper.h.
  2. Execute Commandgcc -E -o options.txt qemu-options-wrapper.h
  3. Upload File options.txt.

After assigning values to the qemu_options array, qemu has a set of all available options. In Vl. C, A for loop of the main function starts parsing the command line according to this set. The for loop framework is roughly as follows:

1     for(;;) {  2         if (optind >= argc)  3             break;  4         if (argv[optind][0] != ‘-‘) {  5         hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);  6         } else {  7             const QEMUOption *popt;  8   9             popt = lookup_opt(argc, argv, &optarg, &optind); 10             if (!(popt->arch_mask & arch_type)) { 11                 printf("Option %s not supported for this target\n", popt->name); 12                 exit(1); 13             } 14             switch(popt->index) { 15             case QEMU_OPTION_M: 16             ...... 17             case QEMU_OPTION_hda: 18             ......   19             case QEMU_OPTION_watchdog: 20             ...... 21             default: 22                 os_parse_cmd_args(popt->index, optarg); 23             }    24         } 25     }

Qemu regards the string starting with '-' in argv as an option, and then calls the lookup_opt function to search for this option in the qemu_options array. If the value of flags in the found option is has_arg, lookup_opt also assigns the parameter string to optarg. After the options and parameters are found, qemu executes different branches based on the index enumerated values in the options.

For some switch-related options, the branch operation only assigns values to the corresponding flag bits, for example:

3712             case QEMU_OPTION_old_param:3713                 old_param = 1;3714                 break;

Some options do not have sub-options. When the branch is executed, the optarg value is directly handed over to the related variable:

3822             case QEMU_OPTION_qtest:3823                 qtest_chrdev = optarg;3824                 break;

Qemu processing is more complex for options with sub-options, such as "-drive if = none, id = drive-ID. It will call qemu_opts_parse to parse sub-options, such as Realtime options:

3852             case QEMU_OPTION_realtime:3853                 opts = qemu_opts_parse(qemu_find_opts("realtime"), optarg, 0);3854                 if (!opts) {3855                     exit(1);3856                 }3857                 configure_realtime(opts);3858                 break;

The parsing of sub-options involves four data structures: qemuopt, qemudesc, qemuopts, and qemuoptslist. Their relationships are shown in:

Qemuopt stores sub-options. Each qemuopt has a qemuoptdesc to describe the sub-options name, type, and help information. The two struct types are defined as follows:

32 struct qemuopt {33 const char * Name; // suboption name 34 const char * STR; // string value 35 36 const qemuoptdesc * DESC; 37 Union {38 bool Boolean; // Boolean value 39 uint64_t uint; // number or size 40} value; 41 42 qemuopts * opts; 43 qtailq_entry (qemuopt) next; 44 }; 95 typedef struct qemuoptdesc {96 const char * Name; 97 Enum qemuopttype; 98 const char * help; 99} qemuoptdesc;

The sub-options can be of the following types:

88 Enum qemuopttype {89 qemu_opt_string = 0, // string 90 bytes, // The value can be on or off 91 qemu_opt_number, // number 92 qemu_opt_size, // The size can be K, m, G, T, and other suffixes 93 };

Qemu maintains an array of qemuoptslist *, which is defined in util/qemu-config.c:

10 static QemuOptsList *vm_config_groups[32];

In the main function, qemu_add_opts writes various qemuoptslist into the array:

2944     qemu_add_opts(&qemu_drive_opts);2945     qemu_add_opts(&qemu_chardev_opts);2946     qemu_add_opts(&qemu_device_opts); 2947     qemu_add_opts(&qemu_netdev_opts);2948     qemu_add_opts(&qemu_net_opts);2949     qemu_add_opts(&qemu_rtc_opts);2950     qemu_add_opts(&qemu_global_opts);2951     qemu_add_opts(&qemu_mon_opts);2952     qemu_add_opts(&qemu_trace_opts);2953     qemu_add_opts(&qemu_option_rom_opts);2954     qemu_add_opts(&qemu_machine_opts);2955     qemu_add_opts(&qemu_smp_opts);2956     qemu_add_opts(&qemu_boot_opts);2957     qemu_add_opts(&qemu_sandbox_opts);2958     qemu_add_opts(&qemu_add_fd_opts);2959     qemu_add_opts(&qemu_object_opts);2960     qemu_add_opts(&qemu_tpmdev_opts);2961     qemu_add_opts(&qemu_realtime_opts);2962     qemu_add_opts(&qemu_msg_opts);

Each qemuoptslist stores all the small options supported by the large options, such as the definition of the-Realtime large option qemuoptslist:

507 static QemuOptsList qemu_realtime_opts = { 508     .name = "realtime", 509     .head = QTAILQ_HEAD_INITIALIZER(qemu_realtime_opts.head), 510     .desc = { 511         { 512             .name = "mlock", 513             .type = QEMU_OPT_BOOL, 514         }, 515         { /* end of list */ } 516     }, 517 };

-Realtime only supports one sub-option and the value is of the bool type, that is, it can only be on or off.

Before calling the parse resolution sub-option, qemu will call qemu_find_opts ("realtime"), find qemuoptslist * From qemu_add_opts, and pass it together with optarg to qemu_opts_parse for resolution. Qemu may use the same large option multiple times to specify multiple identical devices. In this case, you need to use IDs to differentiate them. The qemuopts struct represents all the sub-options under the same ID. The definition is as follows:

46 struct QemuOpts { 47     char *id; 48     QemuOptsList *list; 49     Location loc; 50     QTAILQ_HEAD(QemuOptHead, QemuOpt) head; 51     QTAILQ_ENTRY(QemuOpts) next; 52 };

List is the qemuopts linked list with different IDS under the same big option.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.