On Wed, Sep 09, 2020 at 10:18:47AM +0200, Laszlo Ersek wrote:
> On 09/08/20 18:54, Daniel P. Berrangé wrote:
> > Some applications want to pass quite large values for the OEM strings
> > entries. Rather than having huge strings on the command line, it would
> > be better to load them from a file, as supported with -fw_cfg.
> > 
> > This introduces the "valuefile" parameter allowing for:
> > 
> >   $ echo -n "thisthing" > mydata.txt
> >   $ qemu-system-x86_64 \
> >     -smbios type=11,value=something \
> >     -smbios type=11,valuefile=mydata.txt \
> >     -smbios type=11,value=somemore \
> >     ...other args...
> > 
> > Now in the guest
> > 
> > $ dmidecide -t 11
> > Getting SMBIOS data from sysfs.
> > SMBIOS 2.8 present.
> > 
> > Handle 0x0E00, DMI type 11, 5 bytes
> > OEM Strings
> >     String 1: something
> >     String 2: thisthing
> >     String 3: somemore
> > 
> > Signed-off-by: Daniel P. Berrangé <berra...@redhat.com>
> > ---
> >  hw/smbios/smbios.c | 72 +++++++++++++++++++++++++++++++++++++---------
> >  1 file changed, 59 insertions(+), 13 deletions(-)
> 
> (gearing up to test this / look into the edk2 problem, just one question
> in passing: could we / would we simplify this with g_file_get_contents()?)

BTW, to test this, I'm doing the following.

See the attached 'make-tiny-initrd.py' script. It expects "busybox" on
the host OS and builds a tiny initrd containing busybox.

It can optionally copy in arbitrary other commands, and shared libraries
they link to.  By default it will launch an interactive shell in the
guest, but you can tell it to run a specific command, after which it
will poweroff.

I want to run dmidecode, so I'm using

 $ make-tiny-image.py --run "dmidecode" dmidecode

which both copies dmidecode into the initrd, and also runs it by
default.

It creates 'tiny-initrd.img'

Then I simply boot the host OS kernel using this initrd.


  ./build/qemu-system-x86_64  \
      -kernel /boot/vmlinuz-5.7.14-200.fc32.x86_64 \
      -initrd tiny-initrd.img
      -append 'console=ttyS0'
      -m 1000
      -serial stdio
      -display none
      -blockdev 
'{"driver":"file","filename":"/usr/share/OVMF/OVMF_CODE.fd","node-name":"libvirt-pflash0-storage","auto-read-only":true,"discard":"unmap"}'
      -blockdev 
'{"node-name":"libvirt-pflash0-format","read-only":true,"driver":"raw","file":"libvirt-pflash0-storage"}'
      -blockdev 
'{"driver":"file","filename":"/home/berrange/src/virt/qemu/OVMF_VARS.fd","node-name":"libvirt-pflash1-storage","auto-read-only":true,"discard":"unmap"}'
      -blockdev 
'{"node-name":"libvirt-pflash1-format","read-only":false,"driver":"raw","file":"libvirt-pflash1-storage"}'
      -machine 
pc-q35-4.0,accel=kvm,usb=off,smm=on,dump-guest-core=off,pflash0=libvirt-pflash0-format,pflash1=libvirt-pflash1-format,smbios-ep=3_0
      -chardev file,path=firmware.log,id=firmwarelog
      -device isa-debugcon,iobase=0x402,chardev=firmwarelog
      -smbios type=11,path=smallfile.txt


I have a file 'bigfile.txt' that contains 14 MB of plain text.

I then create 'smallfile.txt' from this

  $ dd if=bigfile.txt of=littlefile.txt bs=1 count=130863

If count=130863 or smaller than EDK2 succesfully boots the guest.

If count=130864 or larger then AFAICT it gets stuck in EDK2 or
very early boot - the guest OS never runs.

If smbios-ep=2_1 (or is omitted), then the size limit is smaller of course
but QEMU validates that for you.

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|
#!/usr/bin/env python3

import re
import sys
import glob
import argparse
import os
import os.path
import stat
import subprocess
from tempfile import TemporaryDirectory
from shutil import copy

def make_busybox(tmpdir, runcmd):
    usrsbin = os.path.join(tmpdir, "usr/sbin")
    bin = os.path.join(tmpdir, "bin")
    os.makedirs(usrsbin, exist_ok=True)
    os.makedirs(bin, exist_ok=True)

    busyboxin = "/usr/sbin/busybox"
    busyboxout = os.path.join(tmpdir, usrsbin, "busybox")
    copy(busyboxin, busyboxout)
    subprocess.check_call([busyboxin, "--install", "-s", bin])

    init = os.path.join(tmpdir, "init")
    with open(init, "w") as fh:
        print("""#!/bin/sh

mkdir /proc /sys
mount -t proc none /proc
mount -t sysfs none /sys

mount -n -t tmpfs none /dev
mknod -m 622 /dev/console c 5 1
mknod -m 666 /dev/null c 1 3
mknod -m 666 /dev/zero c 1 5
mknod -m 666 /dev/ptmx c 5 2
mknod -m 666 /dev/tty c 5 0
mknod -m 666 /dev/ttyS0 c 4 64
mknod -m 444 /dev/random c 1 8
mknod -m 444 /dev/urandom c 1 9

%s
poweroff -f
""" % runcmd, file=fh)
    os.chmod(init, stat.S_IRWXU)

def get_deps(binary):
    out = subprocess.check_output(["ldd", binary]).decode("utf8")
    deps = []
    for line in out.split("\n"):
        m = re.search("=> (/[^ ]+)", line)
        if m is not None:
            deps.append(m.group(1))
        else:
            m = re.match("\s*(/[^ ]+)\s+\(.*\)\s*$", line)
            if m is not None:
                deps.append(m.group(1))
    return deps
    
def make_binaries(tmpdir, binaries):
    bindir = os.path.join(tmpdir, "bin")

    seen = {}
    libs = []
    for binary in binaries:
        if binary[0] == '/':
            src = binary
            dst = os.path.join(tmpdir, binary[1:])
        else:
            src = os.path.join("/usr/bin", binary)
            if not os.path.exists(src):
                src = os.path.join("/usr/sbin", binary)
            dst = os.path.join(bindir, binary)
        if os.path.exists(dst):
            os.unlink(dst)
        copy(src, dst)

        libs.extend(get_deps(src))

    while len(libs):
        print("Pass libs")
        todo = libs
        libs = []
        for lib in todo:
            if lib in seen:
                continue

            dir = os.path.dirname(lib)
            libdir = os.path.join(tmpdir, dir[1:])
            os.makedirs(libdir, exist_ok=True)
            dst = os.path.join(tmpdir, lib[1:])
            copy(lib, dst)
            print(lib)
            seen[lib] = True
            libs.extend(get_deps(lib))

                              

def make_image(tmpdir, output, binaries, runcmd):
    make_busybox(tmpdir, runcmd)
    make_binaries(tmpdir, binaries)

    files = glob.iglob(tmpdir + "/**", recursive=True)
    prefix=len(tmpdir) + 1
    files = [f[prefix:] for f in files]
    files = files[1:]
    filelist = "\n".join(files).encode("utf8")

    with open(output, "w") as fh:
        subprocess.run(["cpio", "--quiet", "-o", "-H", "newc"],
                       cwd=tmpdir, input=filelist, stdout=fh)

parser = argparse.ArgumentParser(description='Build a tiny initrd image')
parser.add_argument('--output', default="tiny-initrd.img",
                    help='Filename of output file')
parser.add_argument('--run', default="exec setsid cttyhack /bin/sh",
                    help='Command to execute in guest (default: "exec setsid 
cttyhack /bin/sh")')
parser.add_argument('binary', nargs="*",
                    help='List of binaries to include')

args = parser.parse_args()

print (args.output)

with TemporaryDirectory(prefix="make-tiny-image") as tmpdir:
    make_image(tmpdir, args.output, args.binary, args.run)

Reply via email to