qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] dtb support on x86 machines


From: João Henrique Ferreira de Freitas
Subject: [Qemu-devel] dtb support on x86 machines
Date: Sun, 30 Nov 2014 18:16:27 -0200
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.2.0

Hi,


I would like to share my work-in-progress about device-tree on qemux x86 machine. The patch is not fully functional but works as a proof of concept. It is based on qemu stable-2.1 and when I solve my questions I will do using master branch.

Besides that device-tree on x86 machines is not widespread used but works. The bootloader syslinux has support to it and I am doing the similar patches to kexec too. So I deciced to do the some with qemu. ;)

The patch uses setup_data field of linux boot protocol (https://www.kernel.org/doc/Documentation/x86/boot.txt) which is a linked list of 'struct setup_data'. Usually setup_data is used to extend boot parameters. I am using it to put a loaded dtb there.

Until now you can see the patch at https://github.com/joaohf/qemu/commit/941d68e6126b4e0908fdd8a90fa7d3f28098a49f. I will send it to qemu-devel list when I solve my biggest question that I am going to explain later.

------ begin ----

diff --git a/hw/i386/pc.c b/hw/i386/pc.c
index ef9fad8..94467ba 100644
--- a/hw/i386/pc.c
+++ b/hw/i386/pc.c
@@ -51,6 +51,7 @@
 #include "exec/address-spaces.h"
 #include "sysemu/arch_init.h"
 #include "qemu/bitmap.h"
+#include "sysemu/device_tree.h"
 #include "qemu/config-file.h"
 #include "hw/acpi/acpi.h"
 #include "hw/acpi/cpu_hotplug.h"
@@ -75,7 +76,7 @@
 /* Leave a chunk of memory at the top of RAM for the BIOS ACPI tables
* (128K) and other BIOS datastructures (less than 4K reported to be used at
  * the moment, 32K should be enough for a while).  */
-unsigned acpi_data_size = 0x20000 + 0x8000;
+unsigned acpi_data_size = 0x20000 + 0x80000;
 void pc_set_legacy_acpi_data_size(void)
 {
     acpi_data_size = 0x10000;
@@ -741,17 +742,77 @@ static long get_file_size(FILE *f)
     return size;
 }

+static int load_dtb(FWCfgState *fw_cfg,
+                    const char *dtb_filename,
+                    void **dtb_addr,
+                    int *dtb_size)
+{
+    void *fdt = NULL;
+
+    fdt = load_device_tree(dtb_filename, dtb_size);
+    if (!fdt) {
+        fprintf(stderr, "Couldn't open dtb file %s\n", dtb_filename);
+        return -1;
+    }
+
+    qemu_fdt_dumpdtb(fdt, *dtb_size);
+
+    *dtb_addr = fdt;
+
+    return 0;
+}
+
+struct setup_data {
+        uint64_t next;
+        uint32_t type;
+#define SETUP_NONE      0
+#define SETUP_E820_EXT  1
+#define SETUP_DTB       2
+#define SETUP_PCI       3
+#define SETUP_EFI       4
+        uint32_t len;
+        uint8_t data[0];
+} __attribute__((packed));
+
+static int setup_dtb_data(FWCfgState *fw_cfg,
+                          void **setup_data_addr, int *setup_data_size,
+                          void *dtb_addr, off_t dtb_size)
+{
+    struct setup_data *sd;
+    int sdsize;
+
+    sd = g_malloc(sizeof(struct setup_data) + dtb_size);
+    if (!sd) {
+        return -1;
+    }
+
+    memset(sd, 0, sizeof(struct setup_data) + dtb_size);
+    sd->next = 0;
+    sd->type = SETUP_DTB;
+    sd->len = dtb_size;
+    memcpy(sd->data, dtb_addr, dtb_size);
+
+    sdsize = sd->len + sizeof(struct setup_data);
+
+    *setup_data_addr = (void *) sd;
+    *setup_data_size = sdsize;
+
+    return 0;
+}
+
 static void load_linux(FWCfgState *fw_cfg,
                        const char *kernel_filename,
                        const char *initrd_filename,
+                       const char *dtb_filename,
                        const char *kernel_cmdline,
                        hwaddr max_ram_size)
 {
     uint16_t protocol;
-    int setup_size, kernel_size, initrd_size = 0, cmdline_size;
+ int setup_size, kernel_size, initrd_size = 0, cmdline_size, dtb_size = 0, setup_data_size = 0;;
     uint32_t initrd_max;
     uint8_t header[8192], *setup, *kernel, *initrd_data;
-    hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0;
+ hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0, setup_data_addr = 0;
+    void *dtb_addr, *setup_data;
     FILE *f;
     char *vmode;

@@ -891,6 +952,53 @@ static void load_linux(FWCfgState *fw_cfg,
         stl_p(header+0x21c, initrd_size);
     }

+    /* load dtb */
+    if (dtb_filename) {
+        int retval;
+        retval = load_dtb(fw_cfg, dtb_filename, &dtb_addr, &dtb_size);
+        if (retval < 0) {
+            fprintf(stderr, "qemu: error loading dtb %s: %s\n",
+                    dtb_filename, strerror(errno));
+            exit(1);
+        }
+
+        retval = setup_dtb_data(fw_cfg, &setup_data, &setup_data_size,
+                dtb_addr, dtb_size);
+        if (retval < 0) {
+            fprintf(stderr, "qemu: error no memory to setup_data\n");
+            exit(1);
+        }
+
+//        if (!initrd_addr) {
+// setup_data_addr = (initrd_max-initrd_size-setup_data_size) & ~4095;
+//        } else {
+ setup_data_addr = QEMU_ALIGN_UP(initrd_max-initrd_size-setup_data_size, 4096);
+//        }
+
+        stq_p(header+0x250, setup_data_addr);
+
+ cpu_physical_memory_write(setup_data_addr, setup_data, setup_data_size);
+

----------

Above you can see how a dtb are loaded and how setup_data is filled. I am put setup_data_addr at header[0x250] and tells to qemu to write
setup_data_addr to guest memory.

This approach works and I can see my device-tree at guest '/proc/device-tree'.

----------
+#if 1
+        fprintf(stderr,
+                "qemu: initrd_max      = %d\n"
+                "qemu: dtb addr        = 0x%p\n"
+                "qemu: dtb size        = %d\n"
+                "qemu: setup_data size = %d\n"
+                "qemu: setup_data addr = 0x%p\n"
+                "qemu: setup_data_addr = 0x" TARGET_FMT_plx "\n"
+                "qemu: header[0x250]   = " TARGET_FMT_plx "\n",
+                initrd_max,
+                dtb_addr,
+                dtb_size,
+                setup_data_size,
+                setup_data,
+                setup_data_addr,
+                ldq_p(header+0x250));
+#endif
+
+    }
+
     /* load kernel and setup */
     setup_size = header[0x1f1];
     if (setup_size == 0) {
@@ -911,6 +1019,11 @@ static void load_linux(FWCfgState *fw_cfg,
         exit(1);
     }
     fclose(f);
+
+    fprintf(stderr,
+            "qemu: setup_size   = %d\n",
+            setup_size);
+
     memcpy(setup, header, MIN(sizeof(header), setup_size));

     fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, prot_addr);
@@ -1298,7 +1411,7 @@ FWCfgState *pc_memory_init(MachineState *machine,

     if (linux_boot) {
load_linux(fw_cfg, machine->kernel_filename, machine->initrd_filename,
-                   machine->kernel_cmdline, below_4g_mem_size);
+ machine->dtb_filename, machine->kernel_cmdline, below_4g_mem_size);
     }

     for (i = 0; i < nb_option_roms; i++) {

------------ end -------------

So, running a qemu instance gives the following. Pay attention I am using the '-dtb' parameter to load device-tree.

Running qemu-system-i386...
/srv/yocto/build/dizzy/tmp/sysroots/x86_64-linux/usr/bin/qemu-system-i386 -kernel bzImage -net nic,vlan=0 -net tap,vlan=0,ifname=tap0,script=no,downscript=no -cpu qemu32 -hda image-lsb-qemux86.ext3 -show-cursor -usb -usbdevice wacom-tablet -vga vmware -no-reboot -dtb device_tree_lc.dtb -m 256 --append "vga=0 uvesafb.mode_option=640x480-32 root=/dev/hda rw mem=256M
ip=192.168.7.2::192.168.7.1:255.255.255.0 oprofile.timer=1 "
qemu: initrd_max      = 267780095
qemu: dtb addr        = 0x0x7f0d80beb010
qemu: dtb size        = 134848
qemu: setup_data size = 134864
qemu: setup_data addr = 0x0x7f0d80a74010
qemu: setup_data_addr = 0x000000000ff40000
qemu: header[0x250]   = 000000000ff40000
qemu: setup_size   = 15360


[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Initializing cgroup subsys cpuacct
[ 0.000000] Linux version 3.10.55-ltsi-yocto-standard (address@hidden) (gcc version 4.8.2 (GCC) ) #1 SMP PREEMPT Fri Oct 31 19:23:26 BRST 2014
[    0.000000] e820: BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009fbff] usable
[ 0.000000] BIOS-e820: [mem 0x000000000009fc00-0x000000000009ffff] reserved [ 0.000000] BIOS-e820: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000000ffdffff] usable
[ 0.000000] BIOS-e820: [mem 0x000000000ffe0000-0x000000000fffffff] reserved [ 0.000000] BIOS-e820: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
[    0.000000] e820: update [mem 0x0ff40000-0x0ff60ecf] usable ==> usable
[    0.000000] extended physical RAM map:
[ 0.000000] reserve setup_data: [mem 0x0000000000000000-0x000000000009fbff] usable [ 0.000000] reserve setup_data: [mem 0x000000000009fc00-0x000000000009ffff] reserved [ 0.000000] reserve setup_data: [mem 0x00000000000f0000-0x00000000000fffff] reserved [ 0.000000] reserve setup_data: [mem 0x0000000000100000-0x000000000ff3ffff] usable [ 0.000000] reserve setup_data: [mem 0x000000000ff40000-0x000000000ff60ecf] usable [ 0.000000] reserve setup_data: [mem 0x000000000ff60ed0-0x000000000ffdffff] usable [ 0.000000] reserve setup_data: [mem 0x000000000ffe0000-0x000000000fffffff] reserved [ 0.000000] reserve setup_data: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved
[    0.000000] e820: remove [mem 0x10000000-0xfffffffffffffffe] usable
[    0.000000] Notice: NX (Execute Disable) protection missing in CPU!
[    0.000000] e820: user-defined physical RAM map:
[    0.000000] user: [mem 0x0000000000000000-0x000000000009fbff] usable
[    0.000000] user: [mem 0x000000000009fc00-0x000000000009ffff] reserved
[    0.000000] user: [mem 0x00000000000f0000-0x00000000000fffff] reserved
[    0.000000] user: [mem 0x0000000000100000-0x000000000ff3ffff] usable
[    0.000000] user: [mem 0x000000000ff40000-0x000000000ff60ecf] usable
[    0.000000] user: [mem 0x000000000ff60ed0-0x000000000ffdffff] usable
[    0.000000] user: [mem 0x000000000ffe0000-0x000000000fffffff] reserved
[    0.000000] user: [mem 0x00000000fffc0000-0x00000000ffffffff] reserved


Then I conclude that 'setup_data_addr = 0x000000000ff40000' is the guest address that qemu put the setup_data (with dtb). In the begin of dmesg we can see:

[ 0.000000] reserve setup_data: [mem 0x000000000ff40000-0x000000000ff60ecf] usable
....
[    0.000000] user: [mem 0x000000000ff40000-0x000000000ff60ecf] usable

The size of this memory range is the same of setup_data size (134864).


So, the linux claim about 'ioremap on RAM pfn 0xff40'


[......]

[    0.685545] ------------[ cut here ]------------
[ 0.685758] WARNING: at /srv/yocto/build/daisy-padtec-otns/tmp/work/qemux86-padtec-linux/linux-yocto/3.10.55+gitAUTOINC+f79a00265e_8e055f3b66-r0/linux/arch/x86/mm/ioremap.c:63 __ioremap_check_ram+0x85/0x90()
[    0.685912] ioremap on RAM pfn 0xff40
[    0.686064] Modules linked in:
[ 0.686322] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 3.10.55-ltsi-yocto-standard #1 [ 0.686413] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.7.5-0-ge51488c-20140602_164612-nilsson.home.kraxel.org 04/01/2014 [ 0.686613] cf895c4c cf895c4c cf895c14 c16e3eaa cf895c3c c103650e c189f938 cf895c68 [ 0.686841] 0000003f c102e0e5 c102e0e5 cff3e820 0000ffe0 00000400 cf895c54 c1036563 [ 0.687134] 00000009 cf895c4c c189f938 cf895c68 cf895c78 c102e0e5 c18a9358 0000003f
[    0.687349] Call Trace:
[    0.687627]  [<c16e3eaa>] dump_stack+0x16/0x18
[    0.687715]  [<c103650e>] warn_slowpath_common+0x5e/0x80
[    0.687804]  [<c102e0e5>] ? __ioremap_check_ram+0x85/0x90
[    0.687880]  [<c102e0e5>] ? __ioremap_check_ram+0x85/0x90
[    0.688044]  [<c1036563>] warn_slowpath_fmt+0x33/0x40
[    0.688115]  [<c102e0e5>] __ioremap_check_ram+0x85/0x90
[    0.688187]  [<c103ede0>] walk_system_ram_range+0xe0/0x100
[    0.688267]  [<c102ddef>] __ioremap_caller+0x6f/0x280
[    0.688335]  [<c102e060>] ? ioremap_prot+0x20/0x20
[    0.688403]  [<c134efc4>] ? pci_bus_read_config_word+0x74/0x80
[    0.688477]  [<c135407e>] ? __pci_bus_find_cap_start+0x1e/0x50
[    0.688550]  [<c102e01b>] ioremap_nocache+0x1b/0x20
[    0.688618]  [<c15e2aca>] ? pcibios_add_device+0x3a/0xb0
[    0.688687]  [<c15e2aca>] pcibios_add_device+0x3a/0xb0
[    0.688756]  [<c1351900>] pci_device_add+0xd0/0x120
[    0.688826]  [<c16ddca1>] pci_scan_single_device+0x81/0xa0
[    0.688897]  [<c1351998>] pci_scan_slot+0x48/0x140
[    0.689042]  [<c1352584>] pci_scan_child_bus+0x24/0xa0
[    0.689114]  [<c15e1541>] pci_acpi_scan_root+0x2e1/0x420
[    0.689188]  [<c1380bba>] acpi_pci_root_add+0x185/0x392
[    0.689260]  [<c137dcba>] ? acpi_scan_match_handler+0x32/0x57
[    0.689332]  [<c137de9f>] acpi_bus_device_attach+0x6c/0xb3
[    0.689405]  [<c1394fdb>] acpi_ns_walk_namespace+0xb9/0x16b
[    0.689479]  [<c1395433>] acpi_walk_namespace+0x79/0xa0
[    0.689548]  [<c137de33>] ? acpi_bus_type_and_status+0x88/0x88
[    0.689622]  [<c137eaca>] acpi_bus_scan+0x95/0xa5
[    0.689688]  [<c137de33>] ? acpi_bus_type_and_status+0x88/0x88
[    0.689762]  [<c1a2ea27>] acpi_scan_init+0x47/0x13a
[    0.689830]  [<c1a2e86a>] acpi_init+0x233/0x276
[    0.689900]  [<c1a2e637>] ? acpi_sleep_init+0xd2/0xd2
[    0.690041]  [<c10001ca>] do_one_initcall+0xda/0x130
[    0.690117]  [<c1a1db6e>] ? buffer_init+0x46/0x46
[    0.690187]  [<c19fbb70>] kernel_init_freeable+0x130/0x1f7
[    0.690258]  [<c19fb4d2>] ? do_early_param+0x78/0x78
[    0.690329]  [<c16e8cad>] ? _raw_spin_unlock_irq+0xd/0x40
[    0.690399]  [<c16e8cbb>] ? _raw_spin_unlock_irq+0x1b/0x40
[    0.690470]  [<c1060135>] ? finish_task_switch+0x45/0xa0
[    0.690541]  [<c16dcea0>] kernel_init+0x10/0x140
[    0.690610]  [<c16ef537>] ret_from_kernel_thread+0x1b/0x28
[    0.690680]  [<c16dce90>] ? rest_init+0x80/0x80
[    0.692118] ---[ end trace c548593bf4ae83de ]---


I can't figure out why linux kernel is claims about:

 [    0.685912] ioremap on RAM pfn 0xff40


May I need to map setup_data allocation using a different approach?

How I can reserve the right pointer address and pass it to guest?


You can see the full dmesg output at https://gist.github.com/joaohf/c4132c767373cf85633c


Any help with qemu memory will be lovely.


Thanks.

--
João Henrique Ferreira de Freitas - joaohf_at_gmail.com
Campinas-SP-Brasil




reply via email to

[Prev in Thread] Current Thread [Next in Thread]