ひまわり

はやく人間になりたい

RISC-V KVMの初期化

RISC-V ISA manual Vol.2 Privileged Architectureを片手に、kvmtoolとkvm.koのコードを追っていく

ちょっとその前に、現状で把握している情報を
あっちこっち見ている間に, kvmのpatchがv7まで来ている

lkml.org

あと、昨日から開催されている、 www.linuxplumbersconf.orgRISC-V Microconference で, パッチを投げているWD達が以下の内容で発表を行うので、資料公開されたら見ていきたい公開されていたのでリンクを追加した

本題にもどります

RISC-Vでの、HWの仮想化支援について

上の発表資料を見るに、AArch64よりは、ARM-VHEの仮想化支援機構と似た形になっているとのこと。

M, S, U modeに加えて、HS, VS, VU modeをhypervisor extensionでは追加している

  • HS mode
    Hypervisor extended supervisor modeの略称で、既存のOSレイヤー相当がうごくS(supervisor) modeの互換をもち、guest physical memoryとsupervisor physical memoryへの変換や、MMIOのメモリマップの仮想化の機能を持つ

  • VS, US mode
    virtual supervisor(user) modeの略称で、guest環境でのS mode, U modeである

これらを実現するために、新たに, hart(Hardware thread)の仮想化についての内部状態であるVirtualization Mode (Vで資料では略される)と、いくつかのCSR (hstatus, hedeleg, hideleg, bsstatus, bsip, etc)が追加されている.

background CSR(bから始まるbsstatus等)については, kvmの実装ではどうも、vから始まる名前(vsstatus等)を使っているっぽい、なぜ差異があるのかはわからない、策定途中だから、まぁこういうこともあるとは思うが

これらを用いて、kvmは仮想化を提供している [3]

guestが起動するまでの流れ

  1. kvm.koがloadされる
  2. kvmtoolによって、kvmと連携してguest用のregister, メモリ空間の初期化をおこなう
  3. 実行

上から、順にコードを追っていく

kvm.koによる、HWの初期化

大体読むところは、アーキテクチャ依存に関する
arch/riscv/kvm/*

main.cのriscv_kvm_init関数から一度、アーキテクチャ共通ルーチンのkvm_init(virt/kvm/kvm_main.c) へ飛ぶ
プロセッサの初期化に関してはここらへん

  1. kvm_arch_init
  2. kvm_arch_hardware_setup
  3. check_processor_compat
  4. kvm_starting_cpu, kvm_dyning_cpu
  5. register_syscore_ops
    3と4に関しては、関数ポインタを渡した関数の中で呼ばれている
int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align,
          struct module *module)
{
    int r;
    int cpu;

    r = kvm_arch_init(opaque); // (1)
...
    r = kvm_arch_hardware_setup();  // (2)
    if (r < 0)
        goto out_free_0a;

    for_each_online_cpu(cpu) {
        smp_call_function_single(cpu, check_processor_compat, &r, 1);  // (3)
        if (r < 0)
            goto out_free_1;
    }

    r = cpuhp_setup_state_nocalls(CPUHP_AP_KVM_STARTING, "kvm/cpu:starting",
                      kvm_starting_cpu, kvm_dying_cpu); // (4)

    register_reboot_notifier(&kvm_reboot_notifier); // (5)
...
    r = misc_register(&kvm_dev); // /dev/kvmの準備
    if (r) {
        pr_err("kvm: misc device register failed\n");
        goto out_unreg;
    }

    register_syscore_ops(&kvm_syscore_ops); // (6)
...
}
1. kvm_arch_init
  • (1.1) hardwareがhypervisor拡張に対応しているかどうかのチェック
  • (1.2) プロセッサ毎のvsipを保持する領域の確保
    guest用のinterrupt pending register. 割り込みの通知に使われる.
  • (1.3) a virtual machine identifier (VMID)の取得
    fence命令に用いられる, fence命令はよくあるメモリバリア用の命令
    Hypervisor Guest Address Translation and Protection Register (hgatp)からとってくる
int kvm_arch_init(void *opaque)
{
    int ret;

    if (!riscv_isa_extension_available(NULL, h)) { // (1.1)
        kvm_info("hypervisor extension not available\n");
        return -ENODEV;
    }

    ret = kvm_riscv_setup_vsip(); // (1.2)
    if (ret)
        return ret;

    kvm_riscv_stage2_vmid_detect(); // (1.3)

    kvm_info("hypervisor extension available\n"); // log

    kvm_info("host has %ld VMID bits\n", kvm_riscv_stage2_vmid_bits());

    return 0;
}
2. kvm_arch_hardware_setup

ここでは、hypervisor trap delegation registers (hedeleg, hideleg)の初期化を行っている. hedelegは、exceptionが起きたときに, そのexceptionを下位のモードへ”委譲”するために使われ、それぞれのビットはexception code番号に対応していて、委譲したいexceptionのbitを立てて使う. exception のリストはISA manual Vol. 2, Table 3.6にあり、下に乗せる

hidelegには、interruptに関する委譲を扱い、番号は同じTableのinterrupt bitが立っている場合のところを参照するとわかる.

わかりやすい記事を, msyksphinzさんが上げているので、リンク[1]を乗せる

今回は, guest でOSを動かすので、instruction address misaligned, breakpoint, syscall, page faultが設定してある.

割り込みは、supervisor modeでの委譲に関する話なので、supervisor {software, timer, external} interruptが設定でき、ここではすべて設定している.

それぞれの略称は、

  • SSIE (supervisor software interrupt enable) bit
  • STIE (supervisor timer interrupt enable) bit
  • SEIE (supervisor external interrupt enable) bit

最後のVSIPは、推定だが、v1.11のdraftでのbackground supervisor interrupt pending registers同等だと思われ、割り込みの通知を知らせるレジスタであり、割り込みがあると対応したbitがたつ。その初期値で0だと思われる

int kvm_arch_hardware_enable(void)
{
    unsigned long hideleg, hedeleg;

    // hypervisor trap delegation registers (hedeleg, hideleg)の初期化
    hedeleg = 0;
    hedeleg |= (1UL << EXC_INST_MISALIGNED);
    hedeleg |= (1UL << EXC_BREAKPOINT);
    hedeleg |= (1UL << EXC_SYSCALL);
    hedeleg |= (1UL << EXC_INST_PAGE_FAULT);
    hedeleg |= (1UL << EXC_LOAD_PAGE_FAULT);
    hedeleg |= (1UL << EXC_STORE_PAGE_FAULT);
    csr_write(CSR_HEDELEG, hedeleg); // exception delegation

    hideleg = 0;
    hideleg |= SIE_SSIE;
    hideleg |= SIE_STIE;
    hideleg |= SIE_SEIE;
    csr_write(CSR_HIDELEG, hideleg);  // interrupt delegation

    csr_write(CSR_VSIP, 0); // interrupt pending

    return 0;
}

f:id:himaaaatti:20190910225059j:plain [2]

3. check_processor_compat

プロセッサ毎に, 機能があるかどうかの判定に使うはずだが、現状はまだ実装されておらず空

int kvm_arch_check_processor_compat(void)
{
    return 0;
}
4. kvm_starting_cpu, kvm_dyning_cpu

hotplugでの、始まりと終わりに呼び出される関数の登録で、 アーキテクチャ依存で呼び出されるのは、 それぞれ、kvm_arch_hardware_enableとkvm_arch_hardware_disable.

kvm_arch_hardware_enableは、一つ上の項目で説明したとおりで、もう一つは 以下で、exceptionとinterruptの委譲レジスタの初期化である j

void kvm_arch_hardware_disable(void)
{
    csr_write(CSR_HEDELEG, 0);
    csr_write(CSR_HIDELEG, 0);
}
5. register_reboot_notifier

ここは、rebootが起きる直前に呼ばれる関数の登録で、実際に呼ばれる関数は、 kvm_rebootで、この中でも、hardware_disable_nolockの先で、kvm_arch_hardware_disableが呼ばれる

static int kvm_reboot(struct notifier_block *notifier, unsigned long val,
              void *v)
{
    /*
    * Some (well, at least mine) BIOSes hang on reboot if
    * in vmx root mode.
    *
    * And Intel TXT required VMX off for all cpu when system shutdown.
    */
    pr_info("kvm: exiting hardware virtualization\n");
    kvm_rebooting = true;
    on_each_cpu(hardware_disable_nolock, NULL, 1);
    return NOTIFY_OK;
}
6. register_syscore_ops

systemがsuspend, resumeするときに、呼ばれる関数を登録している。 実際に、suspend時に、呼ばれる関数は、kvm_suspend、resume時はkvm_resumeで以下の通り

static int kvm_suspend(void)
{
    if (kvm_usage_count)
        hardware_disable_nolock(NULL);
    return 0;
}

static void kvm_resume(void)
{
    if (kvm_usage_count) {
#ifdef CONFIG_LOCKDEP
        WARN_ON(lockdep_is_held(&kvm_count_lock));
#endif
        hardware_enable_nolock(NULL);
    }
}

これらも、kvm_arch_hardware_enable, kvm_arch_hardware_disableが呼ばれる

初期化にまつわる話は以上

実際に、HS-modeからVS-modeへ、行くには、hsstatusのSPVとSPPを立ててsretをしているのだが、それは, vcpuが始まるタイミングで、KVM_VCPU_RUNが呼ばれた後なので、
次の記事にまわす


参考文献

Linux on RISC-V Linux KVM on RISC-V QEMU

先週,

QEMU: RISC-V 64のhypervisor extensionを有効にするほうほう - ひまわり

QEMU上のHost LinuxKVMが無事動いているところは先週確かめた
今回はその上で実際にGuest Linuxをboot させる

前回つかったv5のパッチの次にv6が出ていたが、とりあえずは前のままでいく KVM RISC-V Support [LWN.net]

KVMのクライアントはQEMUを筆頭にいくつかあるが、既にRISC-V patchが用意されているkvmtoolを使う

kvmtoolは, Linuxが動かせるlight weightな, KVM clientらしく、最低限のdevice emulationであり、hostと異なるアーキテクチャに対応いなかったりといった、特徴をもつ
昔は, Linuxに取り込まれていたらしいが、今は離れている

www.phoronix.com

IntelやARMがパッチを投げているらしく、ARMが出している説明の資料はすぐ見つかった https://elinux.org/images/4/44/Przywara.pdf

気になるのであれば, 参照してほしい.

Build kvmtool

これを Host のLinuxで動かすための準備をする

Hostは, RISC-V Linuxなので、gcc, ld, binutils, libc, etcのtoolchainが必要だが、riscv-gnu-toolchainがあるので、準備は簡単。 ビルド方法等は前回のに簡単あり、自分もそれをそのまま使った

kvmtoolは, Linuxのメーリスにあったのをそのまま使おうとしたが、うまく行かなかったので、forkしてちょっと手を加えたものを使う

GitHub - sux2mfgj/kvmtool at riscv_v1

これのビルドには、FDT(Flattened device tree)や、libyamlが必要なので、それもビルドする ビルド前に、CROSS_COMPILEとPATHは適切に変更する 依存的には, kvmtool -> libfdt -> libyaml

まずlibyamlから

$ git clone  git@github.com:yaml/libyaml.git 
$ ./bootstrap
$ ./configure --prefix=${tool installed root} --host=x86_64-pc-linux-gnu
$ make && make install

つぎに、fdt(libfdt), dtc内部にあるので、dtcを持ってくる

$ git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git   
$ cd dtc
$ export CC=${CROSS_COMPILE}gcc
$ export PKG_CONFIG_LIBDIR=${tool installed root}/lib/pkgconfig
$ make DESTDIR=${tool installed root} PREFIX=${tool installed root} LIBDIR=${tool installed root}/lib install-lib install-includes

そして、kvmtool

$ git clone git@github.com:sux2mfgj/kvmtool.git
$ git checkout -b riscv_v1 remotes/sux2mfgj/riscv_v1 
$ make ARCH=riscv prefix=${tool installed root} lkvm-static

これで生成される、lkvm-staticを initramfsへ入れる

$ cp lkvm-static ${initramfs root}/apps/

あと、boot時に devtmpfsをmountされてほしいので、 busyboxのinitを有効にしてビルドした後、

$ cat ${initramfs root}/etc/init.d/rcS
#!/bin/ash

mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev

となるよう、追加する

次に、これらを内包するinitramfsをつくる

$ cd ${initramfs root}
$ find . | cpio -o -H newc | gzip > ../initrd.img
Boot guest!!

これで準備は完成

後は起動するだけだが、initrdが少し大きくなったのと、Guest Linuxを動かす為, QEMUに与えるメモリはある程度大きくしないと、 HostかGuestの起動時にinitramfsを展開するメモリがなくて死んでしまうので気をつける

Hostの起動は前回と同じ。 うまくHostとなるLinuxが起動できたら、 Guestを実行する

$ ./apps/lkvm-static run -m 1024 -c 2 --console serial -p "console=ttyS0 earlycon=uart8250,mmio,0x3f8" -k ./apps/Image
  # lkvm run -k ./apps/Image -m 1024 -c 2 --name guest-78
  # Warning: The maximum recommended amount of VCPUs is 1
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 5.3.0-rc5-00771-g1b24712aa35b (hima@arch) (gcc version 8.3.0 (GCC)) #3 SMP Sat Aug 24 22:32:46 JST 2019
[    0.000000] earlycon: uart8250 at MMIO 0x00000000000003f8 (options '')
[    0.000000] printk: bootconsole [uart8250] enabled
[    0.000000] initrd not found or empty - disabling initrd
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x00000000bfffffff]
[    0.000000] software IO TLB: mapped [mem 0xbb1fc000-0xbf1fc000] (64MB)
[    0.000000] riscv: ISA extensions acdfimsu
[    0.000000] riscv: ELF capabilities acdfim
[    0.000000] percpu: Embedded 18 pages/cpu s34776 r8192 d30760 u73728
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 258055
[    0.000000] Kernel command line:  console=ttyS0 rw rootflags=trans=virtio,version=9p2000.L,cache=loose rootfstype=9p init=/virt/init  ip=dhcp console=ttyS0 earlycon=uart8250,mmio,0x3f8
[    0.000000] Dentry cache hash table entries: 131072 (order: 8, 1048576 bytes, linear)
[    0.000000] Inode-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 955716K/1046528K available (6053K kernel code, 381K rwdata, 1886K rodata, 213K init, 305K bss, 90812K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=2, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=2.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] plic: mapped 1024 interrupts with 2 handlers for 4 contexts.
[    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000234] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns
[    0.086763] Console: colour dummy device 80x25
[    0.126351] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=40000)
[    0.206320] pid_max: default: 32768 minimum: 301
[    0.247120] Mount-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.307492] Mountpoint-cache hash table entries: 2048 (order: 2, 16384 bytes, linear)
[    0.511313] rcu: Hierarchical SRCU implementation.
[    0.566149] smp: Bringing up secondary CPUs ...
[    0.641731] smp: Brought up 1 node, 2 CPUs
[    0.812298] devtmpfs: initialized
[    0.873259] random: get_random_u32 called from bucket_table_alloc.isra.10+0x4e/0x160 with crng_init=0
[    0.955936] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    1.034756] futex hash table entries: 512 (order: 3, 32768 bytes, linear)
[    1.108563] NET: Registered protocol family 16
[    1.515961] vgaarb: loaded
[    1.546718] SCSI subsystem initialized
[    1.593928] usbcore: registered new interface driver usbfs
[    1.636677] usbcore: registered new interface driver hub
[    1.686459] usbcore: registered new device driver usb
[    1.774402] clocksource: Switched to clocksource riscv_clocksource
[    1.988600] NET: Registered protocol family 2
[    2.048295] tcp_listen_portaddr_hash hash table entries: 512 (order: 1, 8192 bytes, linear)
[    2.118335] TCP established hash table entries: 8192 (order: 4, 65536 bytes, linear)
[    2.182753] TCP bind hash table entries: 8192 (order: 5, 131072 bytes, linear)
[    2.246221] TCP: Hash tables configured (established 8192 bind 8192)
[    2.310814] UDP hash table entries: 512 (order: 2, 16384 bytes, linear)
[    2.366717] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes, linear)
[    2.432442] NET: Registered protocol family 1
[    2.503368] RPC: Registered named UNIX socket transport module.
[    2.551473] RPC: Registered udp transport module.
[    2.589999] RPC: Registered tcp transport module.
[    2.624481] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    2.678811] PCI: CLS 0 bytes, default 64
[    2.731055] kvm [1]: hypervisor extension not available
[    2.802988] workingset: timestamp_bits=62 max_order=18 bucket_order=0
[    2.970825] NFS: Registering the id_resolver key type
[    3.012073] Key type id_resolver registered
[    3.050028] Key type id_legacy registered
[    3.082409] nfs4filelayout_init: NFSv4 File Layout Driver Registering...
[    3.146508] 9p: Installing v9fs 9p2000 file system support
[    3.196277] NET: Registered protocol family 38
[    3.234913] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
[    3.291897] io scheduler mq-deadline registered
[    3.330202] io scheduler kyber registered
[    7.582375] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    7.687378] printk: console [ttyS0] disabled
[    7.727071] 3f8.U6_16550A: ttyS0 at MMIO 0x3f8 (irq = 3, base_baud = 115200) is a 16550A
[    7.799061] printk: console [ttyS0] enabled
[    7.799061] printk: console [ttyS0] enabled
[    7.870531] printk: bootconsole [uart8250] disabled
[    7.870531] printk: bootconsole [uart8250] disabled
[    7.978343] 2f8.U6_16550A: ttyS1 at MMIO 0x2f8 (irq = 4, base_baud = 115200) is a 16550A
[    8.066577] 3e8.U6_16550A: ttyS2 at MMIO 0x3e8 (irq = 6, base_baud = 115200) is a 16550A
[    8.146497] 2e8.U6_16550A: ttyS3 at MMIO 0x2e8 (irq = 7, base_baud = 115200) is a 16550A
[    8.239570] [drm] radeon kernel modesetting enabled.
[    8.498927] loop: module loaded
[    8.539095] libphy: Fixed MDIO Bus: probed
[    8.643404] e1000e: Intel(R) PRO/1000 Network Driver - 3.2.6-k
[    8.698051] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[    8.746714] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    8.796702] ehci-pci: EHCI PCI platform driver
[    8.836471] ehci-platform: EHCI generic platform driver
[    8.886504] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    8.942023] ohci-pci: OHCI PCI platform driver
[    8.975609] ohci-platform: OHCI generic platform driver
[    9.023401] usbcore: registered new interface driver uas
[    9.070282] usbcore: registered new interface driver usb-storage
[    9.126332] mousedev: PS/2 mouse device common for all mice
[    9.190198] usbcore: registered new interface driver usbhid
[    9.232204] usbhid: USB HID core driver
[    9.279822] NET: Registered protocol family 10
[    9.359466] Segment Routing with IPv6
[    9.391165] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    9.462311] NET: Registered protocol family 17
[    9.504346] 9pnet: Installing 9P2000 support
[    9.566966] Key type dns_resolver registered
[    9.711043] Sending DHCP requests ., OK
[    9.795801] IP-Config: Got DHCP answer from 192.168.33.1, my address is 192.168.33.15
[    9.859806] IP-Config: Complete:
[    9.884633]      device=eth0, hwaddr=02:15:15:15:15:15, ipaddr=192.168.33.15, mask=255.255.255.0, gw=192.168.33.1
[    9.972286]      host=192.168.33.15, domain=, nis-domain=(none)
[   10.018618]      bootserver=192.168.33.1, rootserver=0.0.0.0, rootpath=
[   10.018726]      nameserver0=192.168.33.1
[   10.339242] VFS: Mounted root (9p filesystem) on device 0:13.
[   10.444184] devtmpfs: mounted
[   10.540462] Freeing unused kernel memory: 212K
[   10.578479] This architecture does not have kernel memory protection.
[   10.628057] Run /virt/init as init process
Mounting...
[   13.522093] random: fast init done
#
[    0.000000] riscv: ISA extensions acdfimsu
[    0.000000] riscv: ELF capabilities acdfim

となっていることから、これが、Privileged ISA Specificationにある
hypervisor-extended supervisor mode (HS-mode)上で動く、virtual S-mode (VS-mode)ということなのだろう
firmwareを設定していないことや、initramfsを設定していないくても動くのだが、どうなっているかまだ分かっていない

この動いているKVMとkvmtoolのコードは今追っているところだが、
このKVMに関しては、流石後発なRISC-Vだけあって、x86等の仮想化で問題となる、PopekとGoldbergの仮想化要件を満たさない命令が存在しないため(CSR等が隠されるようになっている)、
sysenter等の特殊な命令ではなく、hypervisorのために追加されたレジスタを適切に設定したうえで、sretでguestに入ることができる

実装まで行かないが、概要説明は

を見ると良さそう、2つ目のものがWestern Digitalの資料で、今回patchを投げているのもWestern Digitalの人

PLIC( 割り込みコントローラー)の振る舞いとが、RISC-V特有でちょっとまだ追っているところ.

kvmtoolはlightweightと言うだけあって、小さくできているのでこちらも読みやすい
現状のメモはscrapboxにあるので、気になる人ように

kvmtool code reading - risc-v-vmm

recursive(nested vmm)もできるとあるけど、どうするのかまだいまいちわかっていないので、それもおいおい
今回も、コマンドを乗っけているが、雑なので、動かしたいけど、だめだったみたいなのがあれば、一報をください

QEMU: RISC-V 64のhypervisor extensionを有効にするほうほう

もくじ

2019/09/15追記: githubにrepositoryができて,ツールのビルド手順がwikiにできた github.com

ことの始まり


今進行形でLinux KVMRISC-Vのpatchを入れようとしている人たちがいる

lkml.org

現状 under reviewだけど、このパッチはv5とある通り、改定が繰り返されている KVMに関することの、初出は, 2019/07/29で lkml.org

これを最初見たときに, RISC-V Privileged ISA Specificationには、確かに hypervisorに関する記述(それでも、まだdraft (2019/08/25日現在))はあるが、
それを実装したエミュレータやプロセッサは存在するのだろうかという疑問があった

どうもpatchのコメントを見るに、QEMU RISC-Vに、hypervisorの機能を実装し、それを用いて動かすということらしい
OpenSBI(firmware), xvisor(hypervisor), kvmtool(kvm client)についても、軽く調べたが、それは別に記事にまとめようと思う
上のpatchも気になるが、まずQEMUの実装を手元で動かしてみようと思う

QEMU


先のLinuxへのpatchのコメントに、手元で動かす方法が簡単にあった
QEMUに手を加えたものをGithubで開発しており、今本家のQEMUにpatchを送ってunder reviewとのことらしい

GitHub - alistair23/qemu at mainline/alistair/riscv-hyp-ext-v0.4.next

[Qemu-devel] [RFC v1 00/23] Add RISC-V Hypervisor Extension

つい先日QEMUのv4.1.0が出ていたので、もしやと思ってCHANGELOGを見に行ってしまったが、流石にまだだった

ゴール


動かしていろいろしたいが、この記事でのゴールは, Guest LinuxQEMU RISC-V64上で動かし、
そのGuestからRISC-V extension (M, F, D, Qとか)の識別子にH(Hypervisor)があること確認する事とする

要は、guest linuxのkernel logに下のようなメッセージが見えるということである

[    0.000000] riscv: ISA extensions acdfhimsu

一番右の文字列が拡張を表していて、hypervisorに関する拡張がプロセッサに実装されていない場合は、以下のようになる

[    0.000000] riscv: ISA extensions acdfimsu

わかりずらいが、"h"が居ない

準備


自分の環境は、Arch Linuxなので、試す人は、異なるところが環境であるかも

  1. QEMUを手元に持ってくる
$ git clone git@github.com:alistair23/qemu.git
$ git checkout -b rv64_hyv remotes/origin/mainline/alistair/riscv-hyp-ext-v0.4.next
$ ./configure --list-target=riscv64-softmmu 
$ make -j `nproc`

qemu/riscv64-softmmu/qemu-system-riscv64 ができる

  1. cross compileの環境の用意
$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
$ ./configure --prefix=${install path} --enable-linux
$ make -j `nproc`
$ make install
  1. Firmware(OpenSBI)の用意
$ git clone https://github.com/riscv/opensbi.git
$ git checkout -b hyp_ext_v1 remotes/origin/hyp_ext_changes_v1
$ # setup the PATH environment variable in order to use RISC-V gcc, etc
$ export CROSS_COMPILE=riscv64-unknwon-linux-gnu-
$ make PLATFORM=qemu/virt -I=${install dir} install
  1. Rootfsの用意 KVMをkernelに内蔵してしまえばいらないが、せっかくなので起動した後に、
    dmesgを詳しく眺めたり, sysとかの下を見たりするためにbusybox使って用意する

  2. roofsとなるディレクトリの用意

      $ mkdir ${initramfs_dir}
      $ mkdir -p ${initramfs_dir}/{bin, lib, sbin, dev}
      

  3. busybox

    $ curl -L http://busybox.net/downloads/busybox-1.21.1.tar.bz2 | tar -xj
    $ make allnoconfig
    $ make menuconfig # enable static link build and some applets which are init, ash, dmesg, less, etc
    $ make -j nproc
    $ make CONFIG_PREFIX=${initramfs_dir} install
    

  4. Guest Linuxの用意

$ git clone https://github.com/avpatel/linux.git
$ make ARCH=riscv defconfig
$ make ARCH=riscv -j `nproc` CROSS_COMPILE=riscv64-unknown-linux-gnu- Image
$ make ARCH=riscv -j `nproc` CROSS_COMPILE=riscv64-unknown-linux-gnu- INSTALL_MOD_PATH=${initramfs_dir} modules_install
  1. initramfsの生成
$ cd ${initramfs_dir}
$ find . | cpio -o -H newc | gzip > ../initrd.img

起動


qemu-system-riscv64 \
        -monitor null \
        -cpu rv64,x-h=true \
        -m 512M \
        -display none \
        -serial mon:stdio \
        -M virt \
        -kernel local/platform/qemu/virt/firmware/fw_jump.elf \
        -device loader,file=linux/arch/riscv/boot/Image,addr=0x80200000 \
        -append "console=ttyS0 earlycon=sbi" \
        -initrd ./initrd.img \
  • boot log
qemu-system-riscv64: warning: No -bios option specified. Not loading a firmware.
qemu-system-riscv64: warning: This default will change in a future QEMU release. Please use the -bios option to avoid breakages when this happens.
qemu-system-riscv64: warning: See QEMU's deprecation documentation for details.

OpenSBI v0.4-28-g07f78e0 (Aug 24 2019 23:00:12)
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name          : QEMU Virt Machine
Platform HART Features : RV64ACDFHIMSU
Platform Max HARTs     : 8
Current Hart           : 0
Firmware Base          : 0x80000000
Firmware Size          : 112 KB
Runtime SBI Version    : 0.1

PMP0: 0x0000000080000000-0x000000008001ffff (A)
PMP1: 0x0000000000000000-0xffffffffffffffff (A,R,W,X)
[    0.000000] OF: fdt: Ignoring memory range 0x80000000 - 0x80200000
[    0.000000] Linux version 5.3.0-rc5-00771-g1b24712aa35b (hima@arch) (gcc version 8.3.0 (GCC)) #3 SMP Sat Aug 24 22:32:46 JST 2019
[    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
[    0.000000] printk: bootconsole [sbi0] enabled
[    0.000000] Initial ramdisk at: 0x(____ptrval____) (2186569 bytes)
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000080200000-0x000000009fffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000080200000-0x000000009fffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000080200000-0x000000009fffffff]
[    0.000000] software IO TLB: mapped [mem 0x9b8fb000-0x9f8fb000] (64MB)
[    0.000000] riscv: ISA extensions acdfhimsu
[    0.000000] riscv: ELF capabilities acdfim
[    0.000000] percpu: Embedded 18 pages/cpu s34776 r8192 d30760 u73728
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 128775
[    0.000000] Kernel command line: root=/dev/ram rw console=ttyS0 earlycon=sbi
[    0.000000] Dentry cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.000000] Inode-cache hash table entries: 32768 (order: 6, 262144 bytes, linear)
[    0.000000] Sorting __ex_table...
[    0.000000] mem auto-init: stack:off, heap alloc:off, heap free:off
[    0.000000] Memory: 437296K/522240K available (6053K kernel code, 381K rwdata, 1886K rodata, 213K init, 305K bss, 84944K reserved, 0K cma-reserved)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] rcu: Hierarchical RCU implementation.
[    0.000000] rcu:     RCU restricting CPUs from NR_CPUS=8 to nr_cpu_ids=1.
[    0.000000] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.000000] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=1
[    0.000000] NR_IRQS: 0, nr_irqs: 0, preallocated irqs: 0
[    0.000000] plic: mapped 53 interrupts with 1 handlers for 2 contexts.
[    0.000000] riscv_timer_init_dt: Registering clocksource cpuid [0] hartid [0]
[    0.000000] clocksource: riscv_clocksource: mask: 0xffffffffffffffff max_cycles: 0x24e6a1710, max_idle_ns: 440795202120 ns
[    0.000330] sched_clock: 64 bits at 10MHz, resolution 100ns, wraps every 4398046511100ns
[    0.005481] Console: colour dummy device 80x25
[    0.009865] Calibrating delay loop (skipped), value calculated using timer frequency.. 20.00 BogoMIPS (lpj=40000)
[    0.010840] pid_max: default: 32768 minimum: 301
[    0.013178] Mount-cache hash table entries: 1024 (order: 1, 8192 bytes, linear)
[    0.013802] Mountpoint-cache hash table entries: 1024 (order: 1, 8192 bytes, linear)
[    0.047420] rcu: Hierarchical SRCU implementation.
[    0.050989] smp: Bringing up secondary CPUs ...
[    0.051561] smp: Brought up 1 node, 1 CPU
[    0.092081] devtmpfs: initialized
[    0.103312] random: get_random_u32 called from bucket_table_alloc.isra.10+0x4e/0x160 with crng_init=0
[    0.106640] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.107616] futex hash table entries: 256 (order: 2, 16384 bytes, linear)
[    0.111381] NET: Registered protocol family 16
[    0.161110] vgaarb: loaded
[    0.164297] SCSI subsystem initialized
[    0.167398] usbcore: registered new interface driver usbfs
[    0.168074] usbcore: registered new interface driver hub
[    0.168649] usbcore: registered new device driver usb
[    0.177815] clocksource: Switched to clocksource riscv_clocksource
[    0.205067] NET: Registered protocol family 2
[    0.209833] tcp_listen_portaddr_hash hash table entries: 256 (order: 0, 4096 bytes, linear)
[    0.210514] TCP established hash table entries: 4096 (order: 3, 32768 bytes, linear)
[    0.211209] TCP bind hash table entries: 4096 (order: 4, 65536 bytes, linear)
[    0.211819] TCP: Hash tables configured (established 4096 bind 4096)
[    0.214047] UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
[    0.214701] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
[    0.216495] NET: Registered protocol family 1
[    0.220686] RPC: Registered named UNIX socket transport module.
[    0.221157] RPC: Registered udp transport module.
[    0.221789] RPC: Registered tcp transport module.
[    0.222131] RPC: Registered tcp NFSv4.1 backchannel transport module.
[    0.222662] PCI: CLS 0 bytes, default 64
[    0.227504] Unpacking initramfs...
[    0.427787] Freeing initrd memory: 2132K
[    0.428605] kvm [1]: hypervisor extension available
[    0.428989] kvm [1]: host has 14 VMID bits
[    0.433104] workingset: timestamp_bits=62 max_order=17 bucket_order=0
[    0.446603] NFS: Registering the id_resolver key type
[    0.447776] Key type id_resolver registered
[    0.448119] Key type id_legacy registered
[    0.448512] nfs4filelayout_init: NFSv4 File Layout Driver Registering...
[    0.449980] 9p: Installing v9fs 9p2000 file system support
[    0.451533] NET: Registered protocol family 38
[    0.452109] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
[    0.452692] io scheduler mq-deadline registered
[    0.453100] io scheduler kyber registered
[    0.455802] pci-host-generic 30000000.pci: host bridge /soc/pci@30000000 ranges:
[    0.456835] pci-host-generic 30000000.pci:    IO 0x03000000..0x0300ffff -> 0x00000000
[    0.458022] pci-host-generic 30000000.pci:   MEM 0x40000000..0x7fffffff -> 0x40000000
[    0.460116] pci-host-generic 30000000.pci: ECAM at [mem 0x30000000-0x3fffffff] for [bus 00-ff]
[    0.461752] pci-host-generic 30000000.pci: PCI host bridge to bus 0000:00
[    0.462381] pci_bus 0000:00: root bus resource [bus 00-ff]
[    0.462849] pci_bus 0000:00: root bus resource [io  0x0000-0xffff]
[    0.463291] pci_bus 0000:00: root bus resource [mem 0x40000000-0x7fffffff]
[    0.464618] pci 0000:00:00.0: [1b36:0008] type 00 class 0x060000
[    0.609131] Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
[    0.616741] printk: console [ttyS0] disabled
[    0.618205] 10000000.uart: ttyS0 at MMIO 0x10000000 (irq = 10, base_baud = 230400) is a 16550A
[    0.619862] printk: console [ttyS0] enabled
[    0.619862] printk: console [ttyS0] enabled
[    0.620593] printk: bootconsole [sbi0] disabled
[    0.620593] printk: bootconsole [sbi0] disabled
[    0.623483] [drm] radeon kernel modesetting enabled.
[    0.639863] loop: module loaded
[    0.642528] libphy: Fixed MDIO Bus: probed
[    0.643506] e1000e: Intel(R) PRO/1000 Network Driver - 3.2.6-k
[    0.643814] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[    0.644391] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    0.644761] ehci-pci: EHCI PCI platform driver
[    0.645194] ehci-platform: EHCI generic platform driver
[    0.645853] ohci_hcd: USB 1.1 'Open' Host Controller (OHCI) Driver
[    0.646235] ohci-pci: OHCI PCI platform driver
[    0.646685] ohci-platform: OHCI generic platform driver
[    0.647488] usbcore: registered new interface driver uas
[    0.647976] usbcore: registered new interface driver usb-storage
[    0.649509] mousedev: PS/2 mouse device common for all mice
[    0.651177] usbcore: registered new interface driver usbhid
[    0.651469] usbhid: USB HID core driver
[    0.653237] NET: Registered protocol family 10
[    0.659911] Segment Routing with IPv6
[    0.660491] sit: IPv6, IPv4 and MPLS over IPv4 tunneling driver
[    0.663586] NET: Registered protocol family 17
[    0.665134] 9pnet: Installing 9P2000 support
[    0.665889] Key type dns_resolver registered
[    0.699386] Freeing unused kernel memory: 212K
[    0.699673] This architecture does not have kernel memory protection.
[    0.700041] Run /init as init process
# 
  • ISA extensionの確認
$ dmesg | grep "riscv: ISA"
[    0.000000] riscv: ISA extensions acdfhimsu
$ dmesg | grep kvm 
[    0.337890] kvm [1]: hypervisor extension available
[    0.338435] kvm [1]: host has 14 VMID bits

これで、 hypervisor extensionが有効なqemuが用意できた

TODO

コマンド等が雑にかいてあるので、気をつけて
OpenSBI, kvmtool, QEMUのパッチの内容、RISC-V KVMについて等について、今後まとめたい

これで、あそべそうだ

2019/09/02: 実際にゲストうごかした

Google Summer of Code 応募について

2017 Aizu Advent Calendar 2017 - Adventar 3日目です。
前は Yamashouさんで Goでserverlessの夢を見る(Go言語serverlessの紹介) - 田舎っぺ大学生録
後ろは misoton665さんで 軽い気持ちでScalazを使う - えびぞり。


今年のGSoCについて

参加した*1ので、簡単に書きます。

この記事の概要を話すと、GSoC応募の流れと、応募の際に学生であると証明できないと、その時点で詰むという話でうす。

ここでは、応募までの話を。実装した*2ものについてはまた別に書こうかなと。
それでは始めます。

Google Summer of Codeが何なのかは以下を
- Google Summer of Code - Wikipedia
- Google Summer of Code  |  Google Developers

要するに、夏休み暇そうにしてる学生にお金を配ってOSSに参加してもらおうと言うものです。
今年の日本は$6000だったので、欲しいモノが買えたり、焼肉に行けたりします。

応募するまでに、やるべき事をまず並べると。

  1. OSSを決める
  2. 決めたプロジェクトの中の人に相談する(重要)
  3. Proposalを書く
  4. そのProposalに加え必要書類を送る
  5. わくわくしながら待つ

この順番でつらつらと書いていきます。


1. OSS を決める

プロジェクトを選びましょう、しかし、何でもいいわけではなく、
そのプロジェクトがGSoCで学生募集すると申請をして、Googleに受け入れられていないといけません。 *3
もし、やりたいプロジェクトが今までにGSoCに参加していないのであれば、連絡を取り申請について相談しましょう。
来年のGSoC 2018のタイムラインはすでに公開されているので、参考にすると良いかもです.

2. 決めたプロジェクトの中の人に相談する  
  • 中の人に連絡を取りましょう。
    プロジェクトによっては、ここに連絡をくれ等あるのですが、なければメーリスやIRCでコンタクトを取るのが良さげです。
    自分の場合は英語ができないことや、受け入れられるのか心配でしたが、
    連絡を取ってみると、歓迎されて、英語に関しては、ネイティブじゃない人も多く開発に参加しているので、
    大体伝わる英語がかけているので平気だと言われて安心したことを覚えています。  

  • 連絡が取れたら、内容について詰めます。
    やりたいことがあるのなら、自分の考えを伝えればいいのですが、
    ない場合は、興味のあるところを伝えたりし、提案してもらうのも良いでしょう。
    あとは、何度かやり取りして、何をして、どう勧めていくかを決めていきましょう。
    わからないところを残すと死ぬと思うので(僕の場合は)、このステップで全体を掴み大体実装できるかなと思えるくらいまで詰めました。

3. Proposalを書く

まえの段階で詰められていれば、内容で困ることはないと思いますが、英語がつらつらとかけない人(自分を含め) はちょっと気合を入れて書きましょう。

4. そのProposalに加え必要書類を送る

作業ゲーに見えてとても危険なステップです。
Proposalに加えて学生証明を送らなければいけません。
通常は、学生証の写真で問題ないのですが、そのタイミングで学生を証明できない人達が存在します。
その一つは進学をした人たちです、僕は修士に進むところだったのですが、提出するタイミング(4/4ぐらい)で学生証がまだ受け取れておらず。自分がGSoCの期間中、学生であることを証明できません。
そこで、考えられるのが、入学許可証です。
しかし、GSoCの運営の方たちに認められるには決まった情報*4が乗っていなければならないのですが、うちの大学院のものにはありませんでした。
そこで、急遽学生課の方にお願いして、書類を作成してもらい難なきを得ました。
お願いしたのが締め切り3日前とかで、本当に申し訳ありませんでした。 ありがとうございました。

5. わくわくしながら待つ

できるのであれば、そのプロジェクトにpatchやPR送ってみたりすると良いと思います。


こんな感じで、応募の流れを書いてみました。
正直すべてGSoCのページにあるのですが、英語を斜め読みして、詰みかけたりするので、気をつけてもらえればと思います。
おくれてすみませんでした。
つらい*5

*1:https://wiki.freebsd.org/SummerOfCode2017/NVMeControllerEmulatorForBhyve

*2:実は、まだ終わっていない

*3:僕は好きなプロジェクトでいいと思っていたので危なかった

*4:https://developers.google.com/open-source/gsoc/help/proof-of-enrollment

*5:就職活動が面倒すぎるのでだれか助けて

Acepted proposal

GSoC 2017に通ったので、来年以降の参加者の参考になればと、
このぐらい英語がだめでもどうにか、通りました。
詳細については、また今度まとめようかと
一応個人情報のところは消しました。

Google Summer of Code 2017 Proposal 公開用

C/RTL simulationをwave formで見たい

環境

Vivado_HLS 2016.2

解決

デフォルトだとC/RTL simulationしてもOpen Wave Viewerが出来ないので、 見たかったり、使いたい場合は、C/RTL simulationの実行前の設定でDump Traceを有効(portかallは好きに選ぶ)してから走らせる。

微妙にハマるので