target-arm queue:
* Fix vd == vm overlap in sve_ldff1_z * Add support for MTE with KVM guests * Add RAZ/WI handling for DBGDTR[TX|RX] * Start of conversion of A64 decoder to decodetree * Saturate L2CTLR_EL1 core count field rather than overflowing * vexpress: Avoid trivial memory leak of 'flashalias' * sbsa-ref: switch default cpu core to Neoverse-N1 * sbsa-ref: use Bochs graphics card instead of VGA * MAINTAINERS: Add Marcin Juszkiewicz to sbsa-ref reviewer list * docs: Convert u2f.txt to rST -----BEGIN PGP SIGNATURE----- iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmRmHvMZHHBldGVyLm1h eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3vqqEACFEcWq3E2gRjwnz8JAEk/0 jYuYg9jUG6Ev6xY5x31+M4DfK78eXgHYtCxhEcT6FSwpFg/ZXC+bPlZcRlM+8692 gkp+JJeBA4VRy9e7Uk6GvRWnpGzjnkHTHf4E9PZB8iIvbJY9nFTtMZydn1w0EnMW HsetnNLIxrtJaETwUa5mDWh0Bt4t6ZIEB2bJSr3O0fy7uiJ8xvpRMYxqfxvI0h+0 7xSaG7xb5Dy4LxohMK0CLdj1wy+8uWpYgD6ZneJ2hlqjknvNWa3zdR8bRLNT0aZL 8ubR1ioFvfi+uA26SNVrdRrGEhqMrTxD0XstFutz0zlOjn0wjo1Ny/ojmGYWuvcU aG09UvcecMP8hy+ygTXJ+2D04eH1VGmS1GEwRS3p+fdODsgHy0Ctln8IPK8SuG7q 67BG/F4GNdkbktHGbZlwduxh30furH8pSSlIJOeTq7d20+atqZ94MWaoW1iQ+t4B 9gDi3MsKoUKVNEhJPorHlDxvtlQppr0ziL0IVPeYUNJONlSza88hkx34ScA5Rl7+ 5vQYjLkhS1qZQqvd1fNSRNtHeGx2uBeE9eZF/ZCp7bA5rxcRn//LmG7hO7Octuii zIVaOektXeShALdJ7dMt4MZh0z1RjVVLf0ouC1HHCg9rlzvB+0I5AhXYacGkmCqW wf9S0hvNqdGmJRQhNRonGg== =ooCi -----END PGP SIGNATURE----- Merge tag 'pull-target-arm-20230518' of https://git.linaro.org/people/pmaydell/qemu-arm into staging target-arm queue: * Fix vd == vm overlap in sve_ldff1_z * Add support for MTE with KVM guests * Add RAZ/WI handling for DBGDTR[TX|RX] * Start of conversion of A64 decoder to decodetree * Saturate L2CTLR_EL1 core count field rather than overflowing * vexpress: Avoid trivial memory leak of 'flashalias' * sbsa-ref: switch default cpu core to Neoverse-N1 * sbsa-ref: use Bochs graphics card instead of VGA * MAINTAINERS: Add Marcin Juszkiewicz to sbsa-ref reviewer list * docs: Convert u2f.txt to rST # -----BEGIN PGP SIGNATURE----- # # iQJNBAABCAA3FiEE4aXFk81BneKOgxXPPCUl7RQ2DN4FAmRmHvMZHHBldGVyLm1h # eWRlbGxAbGluYXJvLm9yZwAKCRA8JSXtFDYM3vqqEACFEcWq3E2gRjwnz8JAEk/0 # jYuYg9jUG6Ev6xY5x31+M4DfK78eXgHYtCxhEcT6FSwpFg/ZXC+bPlZcRlM+8692 # gkp+JJeBA4VRy9e7Uk6GvRWnpGzjnkHTHf4E9PZB8iIvbJY9nFTtMZydn1w0EnMW # HsetnNLIxrtJaETwUa5mDWh0Bt4t6ZIEB2bJSr3O0fy7uiJ8xvpRMYxqfxvI0h+0 # 7xSaG7xb5Dy4LxohMK0CLdj1wy+8uWpYgD6ZneJ2hlqjknvNWa3zdR8bRLNT0aZL # 8ubR1ioFvfi+uA26SNVrdRrGEhqMrTxD0XstFutz0zlOjn0wjo1Ny/ojmGYWuvcU # aG09UvcecMP8hy+ygTXJ+2D04eH1VGmS1GEwRS3p+fdODsgHy0Ctln8IPK8SuG7q # 67BG/F4GNdkbktHGbZlwduxh30furH8pSSlIJOeTq7d20+atqZ94MWaoW1iQ+t4B # 9gDi3MsKoUKVNEhJPorHlDxvtlQppr0ziL0IVPeYUNJONlSza88hkx34ScA5Rl7+ # 5vQYjLkhS1qZQqvd1fNSRNtHeGx2uBeE9eZF/ZCp7bA5rxcRn//LmG7hO7Octuii # zIVaOektXeShALdJ7dMt4MZh0z1RjVVLf0ouC1HHCg9rlzvB+0I5AhXYacGkmCqW # wf9S0hvNqdGmJRQhNRonGg== # =ooCi # -----END PGP SIGNATURE----- # gpg: Signature made Thu 18 May 2023 05:49:55 AM PDT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [full] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [full] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [full] * tag 'pull-target-arm-20230518' of https://git.linaro.org/people/pmaydell/qemu-arm: (29 commits) docs: Convert u2f.txt to rST hw/arm/vexpress: Avoid trivial memory leak of 'flashalias' target/arm: Saturate L2CTLR_EL1 core count field rather than overflowing target/arm: Convert ERET, ERETAA, ERETAB to decodetree target/arm: Convert BRAA, BRAB, BLRAA, BLRAB to decodetree target/arm: Convert BRA[AB]Z, BLR[AB]Z, RETA[AB] to decodetree target/arm: Convert BR, BLR, RET to decodetree target/arm: Convert conditional branch insns to decodetree target/arm: Convert TBZ, TBNZ to decodetree target/arm: Convert CBZ, CBNZ to decodetree target/arm: Convert unconditional branch immediate to decodetree target/arm: Convert Extract instructions to decodetree target/arm: Convert Bitfield to decodetree target/arm: Convert Move wide (immediate) to decodetree target/arm: Convert Logical (immediate) to decodetree target/arm: Replace bitmask64 with MAKE_64BIT_MASK target/arm: Convert Add/subtract (immediate with tags) to decodetree target/arm: Convert Add/subtract (immediate) to decodetree target/arm: Split gen_add_CC and gen_sub_CC target/arm: Convert PC-rel addressing to decodetree ... Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
commit
266ccbb27b
@ -940,6 +940,7 @@ SBSA-REF
|
||||
M: Radoslaw Biernacki <rad@semihalf.com>
|
||||
M: Peter Maydell <peter.maydell@linaro.org>
|
||||
R: Leif Lindholm <quic_llindhol@quicinc.com>
|
||||
R: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org>
|
||||
L: qemu-arm@nongnu.org
|
||||
S: Maintained
|
||||
F: hw/arm/sbsa-ref.c
|
||||
|
@ -93,4 +93,5 @@ Emulated Devices
|
||||
devices/virtio-pmem.rst
|
||||
devices/vhost-user-rng.rst
|
||||
devices/canokey.rst
|
||||
devices/usb-u2f.rst
|
||||
devices/igb.rst
|
||||
|
93
docs/system/devices/usb-u2f.rst
Normal file
93
docs/system/devices/usb-u2f.rst
Normal file
@ -0,0 +1,93 @@
|
||||
Universal Second Factor (U2F) USB Key Device
|
||||
============================================
|
||||
|
||||
U2F is an open authentication standard that enables relying parties
|
||||
exposed to the internet to offer a strong second factor option for end
|
||||
user authentication.
|
||||
|
||||
The second factor is provided by a device implementing the U2F
|
||||
protocol. In case of a USB U2F security key, it is a USB HID device
|
||||
that implements the U2F protocol.
|
||||
|
||||
QEMU supports both pass-through of a host U2F key device to a VM,
|
||||
and software emulation of a U2F key.
|
||||
|
||||
``u2f-passthru``
|
||||
----------------
|
||||
|
||||
The ``u2f-passthru`` device allows you to connect a real hardware
|
||||
U2F key on your host to a guest VM. All requests made from the guest
|
||||
are passed through to the physical security key connected to the
|
||||
host machine and vice versa.
|
||||
|
||||
In addition, the dedicated pass-through allows you to share a single
|
||||
U2F security key with several guest VMs, which is not possible with a
|
||||
simple host device assignment pass-through.
|
||||
|
||||
You can specify the host U2F key to use with the ``hidraw``
|
||||
option, which takes the host path to a Linux ``/dev/hidrawN`` device:
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| -usb -device u2f-passthru,hidraw=/dev/hidraw0
|
||||
|
||||
If you don't specify the device, the ``u2f-passthru`` device will
|
||||
autoscan to take the first U2F device it finds on the host (this
|
||||
requires a working libudev):
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| -usb -device u2f-passthru
|
||||
|
||||
``u2f-emulated``
|
||||
----------------
|
||||
|
||||
``u2f-emulated`` is a completely software emulated U2F device.
|
||||
It uses `libu2f-emu <https://github.com/MattGorko/libu2f-emu>`__
|
||||
for the U2F key emulation. libu2f-emu
|
||||
provides a complete implementation of the U2F protocol device part for
|
||||
all specified transports given by the FIDO Alliance.
|
||||
|
||||
To work, an emulated U2F device must have four elements:
|
||||
|
||||
* ec x509 certificate
|
||||
* ec private key
|
||||
* counter (four bytes value)
|
||||
* 48 bytes of entropy (random bits)
|
||||
|
||||
To use this type of device, these have to be configured, and these
|
||||
four elements must be passed one way or another.
|
||||
|
||||
Assuming that you have a working libu2f-emu installed on the host,
|
||||
there are three possible ways to configure the ``u2f-emulated`` device:
|
||||
|
||||
* ephemeral
|
||||
* setup directory
|
||||
* manual
|
||||
|
||||
Ephemeral is the simplest way to configure; it lets the device generate
|
||||
all the elements it needs for a single use of the lifetime of the device.
|
||||
It is the default if you do not pass any other options to the device.
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| -usb -device u2f-emulated
|
||||
|
||||
You can pass the device the path of a setup directory on the host
|
||||
using the ``dir`` option; the directory must contain these four files:
|
||||
|
||||
* ``certificate.pem``: ec x509 certificate
|
||||
* ``private-key.pem``: ec private key
|
||||
* ``counter``: counter value
|
||||
* ``entropy``: 48 bytes of entropy
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| -usb -device u2f-emulated,dir=$dir
|
||||
|
||||
You can also manually pass the device the paths to each of these files,
|
||||
if you don't want them all to be in the same directory, using the options
|
||||
|
||||
* ``cert``
|
||||
* ``priv``
|
||||
* ``counter``
|
||||
* ``entropy``
|
||||
|
||||
.. parsed-literal::
|
||||
|qemu_system| -usb -device u2f-emulated,cert=$DIR1/$FILE1,priv=$DIR2/$FILE2,counter=$DIR3/$FILE3,entropy=$DIR4/$FILE4
|
@ -207,7 +207,7 @@ option or the ``device_add`` monitor command. Available devices are:
|
||||
USB audio device
|
||||
|
||||
``u2f-{emulated,passthru}``
|
||||
Universal Second Factor device
|
||||
:doc:`usb-u2f`
|
||||
|
||||
``canokey``
|
||||
An Open-source Secure Key implementing FIDO2, OpenPGP, PIV and more.
|
||||
|
110
docs/u2f.txt
110
docs/u2f.txt
@ -1,110 +0,0 @@
|
||||
QEMU U2F Key Device Documentation.
|
||||
|
||||
Contents
|
||||
1. USB U2F key device
|
||||
2. Building
|
||||
3. Using u2f-emulated
|
||||
4. Using u2f-passthru
|
||||
5. Libu2f-emu
|
||||
|
||||
1. USB U2F key device
|
||||
|
||||
U2F is an open authentication standard that enables relying parties
|
||||
exposed to the internet to offer a strong second factor option for end
|
||||
user authentication.
|
||||
|
||||
The standard brings many advantages to both parties, client and server,
|
||||
allowing to reduce over-reliance on passwords, it increases authentication
|
||||
security and simplifies passwords.
|
||||
|
||||
The second factor is materialized by a device implementing the U2F
|
||||
protocol. In case of a USB U2F security key, it is a USB HID device
|
||||
that implements the U2F protocol.
|
||||
|
||||
In QEMU, the USB U2F key device offers a dedicated support of U2F, allowing
|
||||
guest USB FIDO/U2F security keys operating in two possible modes:
|
||||
pass-through and emulated.
|
||||
|
||||
The pass-through mode consists of passing all requests made from the guest
|
||||
to the physical security key connected to the host machine and vice versa.
|
||||
In addition, the dedicated pass-through allows to have a U2F security key
|
||||
shared on several guests which is not possible with a simple host device
|
||||
assignment pass-through.
|
||||
|
||||
The emulated mode consists of completely emulating the behavior of an
|
||||
U2F device through software part. Libu2f-emu is used for that.
|
||||
|
||||
|
||||
2. Building
|
||||
|
||||
To ensure the build of the u2f-emulated device variant which depends
|
||||
on libu2f-emu: configuring and building:
|
||||
|
||||
./configure --enable-u2f && make
|
||||
|
||||
The pass-through mode is built by default on Linux. To take advantage
|
||||
of the autoscan option it provides, make sure you have a working libudev
|
||||
installed on the host.
|
||||
|
||||
|
||||
3. Using u2f-emulated
|
||||
|
||||
To work, an emulated U2F device must have four elements:
|
||||
* ec x509 certificate
|
||||
* ec private key
|
||||
* counter (four bytes value)
|
||||
* 48 bytes of entropy (random bits)
|
||||
|
||||
To use this type of device, this one has to be configured, and these
|
||||
four elements must be passed one way or another.
|
||||
|
||||
Assuming that you have a working libu2f-emu installed on the host.
|
||||
There are three possible ways of configurations:
|
||||
* ephemeral
|
||||
* setup directory
|
||||
* manual
|
||||
|
||||
Ephemeral is the simplest way to configure, it lets the device generate
|
||||
all the elements it needs for a single use of the lifetime of the device.
|
||||
|
||||
qemu -usb -device u2f-emulated
|
||||
|
||||
Setup directory allows to configure the device from a directory containing
|
||||
four files:
|
||||
* certificate.pem: ec x509 certificate
|
||||
* private-key.pem: ec private key
|
||||
* counter: counter value
|
||||
* entropy: 48 bytes of entropy
|
||||
|
||||
qemu -usb -device u2f-emulated,dir=$dir
|
||||
|
||||
Manual allows to configure the device more finely by specifying each
|
||||
of the elements necessary for the device:
|
||||
* cert
|
||||
* priv
|
||||
* counter
|
||||
* entropy
|
||||
|
||||
qemu -usb -device u2f-emulated,cert=$DIR1/$FILE1,priv=$DIR2/$FILE2,counter=$DIR3/$FILE3,entropy=$DIR4/$FILE4
|
||||
|
||||
|
||||
4. Using u2f-passthru
|
||||
|
||||
On the host specify the u2f-passthru device with a suitable hidraw:
|
||||
|
||||
qemu -usb -device u2f-passthru,hidraw=/dev/hidraw0
|
||||
|
||||
Alternately, the u2f-passthru device can autoscan to take the first
|
||||
U2F device it finds on the host (this requires a working libudev):
|
||||
|
||||
qemu -usb -device u2f-passthru
|
||||
|
||||
|
||||
5. Libu2f-emu
|
||||
|
||||
The u2f-emulated device uses libu2f-emu for the U2F key emulation. Libu2f-emu
|
||||
implements completely the U2F protocol device part for all specified
|
||||
transport given by the FIDO Alliance.
|
||||
|
||||
For more information about libu2f-emu see this page:
|
||||
https://github.com/MattGorko/libu2f-emu.
|
@ -648,7 +648,7 @@ static void create_pcie(SBSAMachineState *sms)
|
||||
}
|
||||
}
|
||||
|
||||
pci_create_simple(pci->bus, -1, "VGA");
|
||||
pci_create_simple(pci->bus, -1, "bochs-display");
|
||||
|
||||
create_smmu(sms, pci->bus);
|
||||
}
|
||||
@ -852,7 +852,7 @@ static void sbsa_ref_class_init(ObjectClass *oc, void *data)
|
||||
|
||||
mc->init = sbsa_ref_init;
|
||||
mc->desc = "QEMU 'SBSA Reference' ARM Virtual Machine";
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a57");
|
||||
mc->default_cpu_type = ARM_CPU_TYPE_NAME("neoverse-n1");
|
||||
mc->max_cpus = 512;
|
||||
mc->pci_allow_0_address = true;
|
||||
mc->minimum_page_bits = 12;
|
||||
|
@ -173,6 +173,11 @@ struct VexpressMachineClass {
|
||||
|
||||
struct VexpressMachineState {
|
||||
MachineState parent;
|
||||
MemoryRegion vram;
|
||||
MemoryRegion sram;
|
||||
MemoryRegion flashalias;
|
||||
MemoryRegion lowram;
|
||||
MemoryRegion a15sram;
|
||||
bool secure;
|
||||
bool virt;
|
||||
};
|
||||
@ -182,7 +187,7 @@ struct VexpressMachineState {
|
||||
#define TYPE_VEXPRESS_A15_MACHINE MACHINE_TYPE_NAME("vexpress-a15")
|
||||
OBJECT_DECLARE_TYPE(VexpressMachineState, VexpressMachineClass, VEXPRESS_MACHINE)
|
||||
|
||||
typedef void DBoardInitFn(const VexpressMachineState *machine,
|
||||
typedef void DBoardInitFn(VexpressMachineState *machine,
|
||||
ram_addr_t ram_size,
|
||||
const char *cpu_type,
|
||||
qemu_irq *pic);
|
||||
@ -263,14 +268,13 @@ static void init_cpus(MachineState *ms, const char *cpu_type,
|
||||
}
|
||||
}
|
||||
|
||||
static void a9_daughterboard_init(const VexpressMachineState *vms,
|
||||
static void a9_daughterboard_init(VexpressMachineState *vms,
|
||||
ram_addr_t ram_size,
|
||||
const char *cpu_type,
|
||||
qemu_irq *pic)
|
||||
{
|
||||
MachineState *machine = MACHINE(vms);
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *lowram = g_new(MemoryRegion, 1);
|
||||
ram_addr_t low_ram_size;
|
||||
|
||||
if (ram_size > 0x40000000) {
|
||||
@ -287,9 +291,9 @@ static void a9_daughterboard_init(const VexpressMachineState *vms,
|
||||
* address space should in theory be remappable to various
|
||||
* things including ROM or RAM; we always map the RAM there.
|
||||
*/
|
||||
memory_region_init_alias(lowram, NULL, "vexpress.lowmem", machine->ram,
|
||||
0, low_ram_size);
|
||||
memory_region_add_subregion(sysmem, 0x0, lowram);
|
||||
memory_region_init_alias(&vms->lowram, NULL, "vexpress.lowmem",
|
||||
machine->ram, 0, low_ram_size);
|
||||
memory_region_add_subregion(sysmem, 0x0, &vms->lowram);
|
||||
memory_region_add_subregion(sysmem, 0x60000000, machine->ram);
|
||||
|
||||
/* 0x1e000000 A9MPCore (SCU) private memory region */
|
||||
@ -348,14 +352,13 @@ static VEDBoardInfo a9_daughterboard = {
|
||||
.init = a9_daughterboard_init,
|
||||
};
|
||||
|
||||
static void a15_daughterboard_init(const VexpressMachineState *vms,
|
||||
static void a15_daughterboard_init(VexpressMachineState *vms,
|
||||
ram_addr_t ram_size,
|
||||
const char *cpu_type,
|
||||
qemu_irq *pic)
|
||||
{
|
||||
MachineState *machine = MACHINE(vms);
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
|
||||
{
|
||||
/* We have to use a separate 64 bit variable here to avoid the gcc
|
||||
@ -386,9 +389,9 @@ static void a15_daughterboard_init(const VexpressMachineState *vms,
|
||||
/* 0x2b060000: SP805 watchdog: not modelled */
|
||||
/* 0x2b0a0000: PL341 dynamic memory controller: not modelled */
|
||||
/* 0x2e000000: system SRAM */
|
||||
memory_region_init_ram(sram, NULL, "vexpress.a15sram", 0x10000,
|
||||
memory_region_init_ram(&vms->a15sram, NULL, "vexpress.a15sram", 0x10000,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, 0x2e000000, sram);
|
||||
memory_region_add_subregion(sysmem, 0x2e000000, &vms->a15sram);
|
||||
|
||||
/* 0x7ffb0000: DMA330 DMA controller: not modelled */
|
||||
/* 0x7ffd0000: PL354 static memory controller: not modelled */
|
||||
@ -547,10 +550,6 @@ static void vexpress_common_init(MachineState *machine)
|
||||
I2CBus *i2c;
|
||||
ram_addr_t vram_size, sram_size;
|
||||
MemoryRegion *sysmem = get_system_memory();
|
||||
MemoryRegion *vram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *sram = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flashalias = g_new(MemoryRegion, 1);
|
||||
MemoryRegion *flash0mem;
|
||||
const hwaddr *map = daughterboard->motherboard_map;
|
||||
int i;
|
||||
|
||||
@ -662,24 +661,25 @@ static void vexpress_common_init(MachineState *machine)
|
||||
|
||||
if (map[VE_NORFLASHALIAS] != -1) {
|
||||
/* Map flash 0 as an alias into low memory */
|
||||
MemoryRegion *flash0mem;
|
||||
flash0mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(pflash0), 0);
|
||||
memory_region_init_alias(flashalias, NULL, "vexpress.flashalias",
|
||||
memory_region_init_alias(&vms->flashalias, NULL, "vexpress.flashalias",
|
||||
flash0mem, 0, VEXPRESS_FLASH_SIZE);
|
||||
memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], flashalias);
|
||||
memory_region_add_subregion(sysmem, map[VE_NORFLASHALIAS], &vms->flashalias);
|
||||
}
|
||||
|
||||
dinfo = drive_get(IF_PFLASH, 0, 1);
|
||||
ve_pflash_cfi01_register(map[VE_NORFLASH1], "vexpress.flash1", dinfo);
|
||||
|
||||
sram_size = 0x2000000;
|
||||
memory_region_init_ram(sram, NULL, "vexpress.sram", sram_size,
|
||||
memory_region_init_ram(&vms->sram, NULL, "vexpress.sram", sram_size,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, map[VE_SRAM], sram);
|
||||
memory_region_add_subregion(sysmem, map[VE_SRAM], &vms->sram);
|
||||
|
||||
vram_size = 0x800000;
|
||||
memory_region_init_ram(vram, NULL, "vexpress.vram", vram_size,
|
||||
memory_region_init_ram(&vms->vram, NULL, "vexpress.vram", vram_size,
|
||||
&error_fatal);
|
||||
memory_region_add_subregion(sysmem, map[VE_VIDEORAM], vram);
|
||||
memory_region_add_subregion(sysmem, map[VE_VIDEORAM], &vms->vram);
|
||||
|
||||
/* 0x4e000000 LAN9118 Ethernet */
|
||||
if (nd_table[0].used) {
|
||||
|
@ -2146,7 +2146,7 @@ static void machvirt_init(MachineState *machine)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (vms->mte && (kvm_enabled() || hvf_enabled())) {
|
||||
if (vms->mte && hvf_enabled()) {
|
||||
error_report("mach-virt: %s does not support providing "
|
||||
"MTE to the guest CPU",
|
||||
current_accel_name());
|
||||
@ -2216,39 +2216,48 @@ static void machvirt_init(MachineState *machine)
|
||||
}
|
||||
|
||||
if (vms->mte) {
|
||||
/* Create the memory region only once, but link to all cpus. */
|
||||
if (!tag_sysmem) {
|
||||
/*
|
||||
* The property exists only if MemTag is supported.
|
||||
* If it is, we must allocate the ram to back that up.
|
||||
*/
|
||||
if (!object_property_find(cpuobj, "tag-memory")) {
|
||||
error_report("MTE requested, but not supported "
|
||||
"by the guest CPU");
|
||||
if (tcg_enabled()) {
|
||||
/* Create the memory region only once, but link to all cpus. */
|
||||
if (!tag_sysmem) {
|
||||
/*
|
||||
* The property exists only if MemTag is supported.
|
||||
* If it is, we must allocate the ram to back that up.
|
||||
*/
|
||||
if (!object_property_find(cpuobj, "tag-memory")) {
|
||||
error_report("MTE requested, but not supported "
|
||||
"by the guest CPU");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tag_sysmem = g_new(MemoryRegion, 1);
|
||||
memory_region_init(tag_sysmem, OBJECT(machine),
|
||||
"tag-memory", UINT64_MAX / 32);
|
||||
|
||||
if (vms->secure) {
|
||||
secure_tag_sysmem = g_new(MemoryRegion, 1);
|
||||
memory_region_init(secure_tag_sysmem, OBJECT(machine),
|
||||
"secure-tag-memory",
|
||||
UINT64_MAX / 32);
|
||||
|
||||
/* As with ram, secure-tag takes precedence over tag. */
|
||||
memory_region_add_subregion_overlap(secure_tag_sysmem,
|
||||
0, tag_sysmem, -1);
|
||||
}
|
||||
}
|
||||
|
||||
object_property_set_link(cpuobj, "tag-memory",
|
||||
OBJECT(tag_sysmem), &error_abort);
|
||||
if (vms->secure) {
|
||||
object_property_set_link(cpuobj, "secure-tag-memory",
|
||||
OBJECT(secure_tag_sysmem),
|
||||
&error_abort);
|
||||
}
|
||||
} else if (kvm_enabled()) {
|
||||
if (!kvm_arm_mte_supported()) {
|
||||
error_report("MTE requested, but not supported by KVM");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
tag_sysmem = g_new(MemoryRegion, 1);
|
||||
memory_region_init(tag_sysmem, OBJECT(machine),
|
||||
"tag-memory", UINT64_MAX / 32);
|
||||
|
||||
if (vms->secure) {
|
||||
secure_tag_sysmem = g_new(MemoryRegion, 1);
|
||||
memory_region_init(secure_tag_sysmem, OBJECT(machine),
|
||||
"secure-tag-memory", UINT64_MAX / 32);
|
||||
|
||||
/* As with ram, secure-tag takes precedence over tag. */
|
||||
memory_region_add_subregion_overlap(secure_tag_sysmem, 0,
|
||||
tag_sysmem, -1);
|
||||
}
|
||||
}
|
||||
|
||||
object_property_set_link(cpuobj, "tag-memory", OBJECT(tag_sysmem),
|
||||
&error_abort);
|
||||
if (vms->secure) {
|
||||
object_property_set_link(cpuobj, "secure-tag-memory",
|
||||
OBJECT(secure_tag_sysmem),
|
||||
&error_abort);
|
||||
kvm_arm_enable_mte(cpuobj, &error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,8 +15,15 @@ static uint64_t l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri)
|
||||
{
|
||||
ARMCPU *cpu = env_archcpu(env);
|
||||
|
||||
/* Number of cores is in [25:24]; otherwise we RAZ */
|
||||
return (cpu->core_count - 1) << 24;
|
||||
/*
|
||||
* Number of cores is in [25:24]; otherwise we RAZ.
|
||||
* If the board didn't configure the CPUs into clusters,
|
||||
* we default to "all CPUs in one cluster", which might be
|
||||
* more than the 4 that the hardware permits and which is
|
||||
* all you can report in this two-bit field. Saturate to
|
||||
* 0b11 (== 4 CPUs) rather than overflowing the field.
|
||||
*/
|
||||
return MIN(cpu->core_count - 1, 3) << 24;
|
||||
}
|
||||
|
||||
static const ARMCPRegInfo cortex_a72_a57_a53_cp_reginfo[] = {
|
||||
|
@ -1480,6 +1480,7 @@ void arm_cpu_post_init(Object *obj)
|
||||
qdev_prop_allow_set_link_before_realize,
|
||||
OBJ_PROP_LINK_STRONG);
|
||||
}
|
||||
cpu->has_mte = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -1616,7 +1617,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
}
|
||||
if (cpu->tag_memory) {
|
||||
error_setg(errp,
|
||||
"Cannot enable %s when guest CPUs has MTE enabled",
|
||||
"Cannot enable %s when guest CPUs has tag memory enabled",
|
||||
current_accel_name());
|
||||
return;
|
||||
}
|
||||
@ -1996,10 +1997,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
||||
}
|
||||
|
||||
#ifndef CONFIG_USER_ONLY
|
||||
if (cpu->tag_memory == NULL && cpu_isar_feature(aa64_mte, cpu)) {
|
||||
if (!cpu->has_mte && cpu_isar_feature(aa64_mte, cpu)) {
|
||||
/*
|
||||
* Disable the MTE feature bits if we do not have tag-memory
|
||||
* provided by the machine.
|
||||
* Disable the MTE feature bits if we do not have the feature
|
||||
* setup by the machine.
|
||||
*/
|
||||
cpu->isar.id_aa64pfr1 =
|
||||
FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0);
|
||||
|
@ -935,6 +935,9 @@ struct ArchCPU {
|
||||
*/
|
||||
uint32_t psci_conduit;
|
||||
|
||||
/* CPU has Memory Tag Extension */
|
||||
bool has_mte;
|
||||
|
||||
/* For v8M, initial value of the Secure VTOR */
|
||||
uint32_t init_svtor;
|
||||
/* For v8M, initial value of the Non-secure VTOR */
|
||||
@ -1053,6 +1056,7 @@ struct ArchCPU {
|
||||
bool prop_pauth;
|
||||
bool prop_pauth_impdef;
|
||||
bool prop_lpa2;
|
||||
OnOffAuto prop_mte;
|
||||
|
||||
/* DCZ blocksize, in log_2(words), ie low 4 bits of DCZID_EL0 */
|
||||
uint32_t dcz_blocksize;
|
||||
|
@ -949,8 +949,10 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
|
||||
.access = PL0_R, .accessfn = access_tdcc,
|
||||
.type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
/*
|
||||
* OSDTRRX_EL1/OSDTRTX_EL1 are used for save and restore of DBGDTRRX_EL0.
|
||||
* It is a component of the Debug Communications Channel, which is not implemented.
|
||||
* These registers belong to the Debug Communications Channel,
|
||||
* which is not implemented. However we implement RAZ/WI behaviour
|
||||
* with trapping to prevent spurious SIGILLs if the guest OS does
|
||||
* access them as the support cannot be probed for.
|
||||
*/
|
||||
{ .name = "OSDTRRX_EL1", .state = ARM_CP_STATE_BOTH, .cp = 14,
|
||||
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = 0, .opc2 = 2,
|
||||
@ -960,6 +962,11 @@ static const ARMCPRegInfo debug_cp_reginfo[] = {
|
||||
.opc0 = 2, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2,
|
||||
.access = PL1_RW, .accessfn = access_tdcc,
|
||||
.type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
/* DBGDTRTX_EL0/DBGDTRRX_EL0 depend on direction */
|
||||
{ .name = "DBGDTR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14,
|
||||
.opc0 = 2, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0,
|
||||
.access = PL0_RW, .accessfn = access_tdcc,
|
||||
.type = ARM_CP_CONST, .resetvalue = 0 },
|
||||
/*
|
||||
* OSECCR_EL1 provides a mechanism for an operating system
|
||||
* to access the contents of EDECCR. EDECCR is not implemented though,
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "hw/boards.h"
|
||||
#include "hw/irq.h"
|
||||
#include "qemu/log.h"
|
||||
#include "migration/blocker.h"
|
||||
|
||||
const KVMCapabilityInfo kvm_arch_required_capabilities[] = {
|
||||
KVM_CAP_LAST_INFO
|
||||
@ -1064,3 +1065,37 @@ bool kvm_arch_cpu_check_are_resettable(void)
|
||||
void kvm_arch_accel_class_init(ObjectClass *oc)
|
||||
{
|
||||
}
|
||||
|
||||
void kvm_arm_enable_mte(Object *cpuobj, Error **errp)
|
||||
{
|
||||
static bool tried_to_enable;
|
||||
static bool succeeded_to_enable;
|
||||
Error *mte_migration_blocker = NULL;
|
||||
int ret;
|
||||
|
||||
if (!tried_to_enable) {
|
||||
/*
|
||||
* MTE on KVM is enabled on a per-VM basis (and retrying doesn't make
|
||||
* sense), and we only want a single migration blocker as well.
|
||||
*/
|
||||
tried_to_enable = true;
|
||||
|
||||
ret = kvm_vm_enable_cap(kvm_state, KVM_CAP_ARM_MTE, 0);
|
||||
if (ret) {
|
||||
error_setg_errno(errp, -ret, "Failed to enable KVM_CAP_ARM_MTE");
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: add proper migration support with MTE enabled */
|
||||
error_setg(&mte_migration_blocker,
|
||||
"Live migration disabled due to MTE enabled");
|
||||
if (migrate_add_blocker(mte_migration_blocker, errp)) {
|
||||
error_free(mte_migration_blocker);
|
||||
return;
|
||||
}
|
||||
succeeded_to_enable = true;
|
||||
}
|
||||
if (succeeded_to_enable) {
|
||||
object_property_set_bool(cpuobj, "has_mte", true, NULL);
|
||||
}
|
||||
}
|
||||
|
@ -756,6 +756,11 @@ bool kvm_arm_steal_time_supported(void)
|
||||
return kvm_check_extension(kvm_state, KVM_CAP_STEAL_TIME);
|
||||
}
|
||||
|
||||
bool kvm_arm_mte_supported(void)
|
||||
{
|
||||
return kvm_check_extension(kvm_state, KVM_CAP_ARM_MTE);
|
||||
}
|
||||
|
||||
QEMU_BUILD_BUG_ON(KVM_ARM64_SVE_VQ_MIN != 1);
|
||||
|
||||
uint32_t kvm_arm_sve_get_vls(CPUState *cs)
|
||||
|
@ -313,6 +313,13 @@ bool kvm_arm_pmu_supported(void);
|
||||
*/
|
||||
bool kvm_arm_sve_supported(void);
|
||||
|
||||
/**
|
||||
* kvm_arm_mte_supported:
|
||||
*
|
||||
* Returns: true if KVM can enable MTE, and false otherwise.
|
||||
*/
|
||||
bool kvm_arm_mte_supported(void);
|
||||
|
||||
/**
|
||||
* kvm_arm_get_max_vm_ipa_size:
|
||||
* @ms: Machine state handle
|
||||
@ -377,6 +384,8 @@ void kvm_arm_pvtime_init(CPUState *cs, uint64_t ipa);
|
||||
|
||||
int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level);
|
||||
|
||||
void kvm_arm_enable_mte(Object *cpuobj, Error **errp);
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
@ -403,6 +412,11 @@ static inline bool kvm_arm_steal_time_supported(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool kvm_arm_mte_supported(void)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions should never actually be called without KVM support.
|
||||
*/
|
||||
@ -451,6 +465,11 @@ static inline uint32_t kvm_arm_sve_get_vls(CPUState *cs)
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
static inline void kvm_arm_enable_mte(Object *cpuobj, Error **errp)
|
||||
{
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline const char *gic_class_name(void)
|
||||
|
152
target/arm/tcg/a64.decode
Normal file
152
target/arm/tcg/a64.decode
Normal file
@ -0,0 +1,152 @@
|
||||
# AArch64 A64 allowed instruction decoding
|
||||
#
|
||||
# Copyright (c) 2023 Linaro, Ltd
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#
|
||||
# This file is processed by scripts/decodetree.py
|
||||
#
|
||||
|
||||
&r rn
|
||||
&ri rd imm
|
||||
&rri_sf rd rn imm sf
|
||||
&i imm
|
||||
|
||||
|
||||
### Data Processing - Immediate
|
||||
|
||||
# PC-rel addressing
|
||||
|
||||
%imm_pcrel 5:s19 29:2
|
||||
@pcrel . .. ..... ................... rd:5 &ri imm=%imm_pcrel
|
||||
|
||||
ADR 0 .. 10000 ................... ..... @pcrel
|
||||
ADRP 1 .. 10000 ................... ..... @pcrel
|
||||
|
||||
# Add/subtract (immediate)
|
||||
|
||||
%imm12_sh12 10:12 !function=shl_12
|
||||
@addsub_imm sf:1 .. ...... . imm:12 rn:5 rd:5
|
||||
@addsub_imm12 sf:1 .. ...... . ............ rn:5 rd:5 imm=%imm12_sh12
|
||||
|
||||
ADD_i . 00 100010 0 ............ ..... ..... @addsub_imm
|
||||
ADD_i . 00 100010 1 ............ ..... ..... @addsub_imm12
|
||||
ADDS_i . 01 100010 0 ............ ..... ..... @addsub_imm
|
||||
ADDS_i . 01 100010 1 ............ ..... ..... @addsub_imm12
|
||||
|
||||
SUB_i . 10 100010 0 ............ ..... ..... @addsub_imm
|
||||
SUB_i . 10 100010 1 ............ ..... ..... @addsub_imm12
|
||||
SUBS_i . 11 100010 0 ............ ..... ..... @addsub_imm
|
||||
SUBS_i . 11 100010 1 ............ ..... ..... @addsub_imm12
|
||||
|
||||
# Add/subtract (immediate with tags)
|
||||
|
||||
&rri_tag rd rn uimm6 uimm4
|
||||
@addsub_imm_tag . .. ...... . uimm6:6 .. uimm4:4 rn:5 rd:5 &rri_tag
|
||||
|
||||
ADDG_i 1 00 100011 0 ...... 00 .... ..... ..... @addsub_imm_tag
|
||||
SUBG_i 1 10 100011 0 ...... 00 .... ..... ..... @addsub_imm_tag
|
||||
|
||||
# Logical (immediate)
|
||||
|
||||
&rri_log rd rn sf dbm
|
||||
@logic_imm_64 1 .. ...... dbm:13 rn:5 rd:5 &rri_log sf=1
|
||||
@logic_imm_32 0 .. ...... 0 dbm:12 rn:5 rd:5 &rri_log sf=0
|
||||
|
||||
AND_i . 00 100100 . ...... ...... ..... ..... @logic_imm_64
|
||||
AND_i . 00 100100 . ...... ...... ..... ..... @logic_imm_32
|
||||
ORR_i . 01 100100 . ...... ...... ..... ..... @logic_imm_64
|
||||
ORR_i . 01 100100 . ...... ...... ..... ..... @logic_imm_32
|
||||
EOR_i . 10 100100 . ...... ...... ..... ..... @logic_imm_64
|
||||
EOR_i . 10 100100 . ...... ...... ..... ..... @logic_imm_32
|
||||
ANDS_i . 11 100100 . ...... ...... ..... ..... @logic_imm_64
|
||||
ANDS_i . 11 100100 . ...... ...... ..... ..... @logic_imm_32
|
||||
|
||||
# Move wide (immediate)
|
||||
|
||||
&movw rd sf imm hw
|
||||
@movw_64 1 .. ...... hw:2 imm:16 rd:5 &movw sf=1
|
||||
@movw_32 0 .. ...... 0 hw:1 imm:16 rd:5 &movw sf=0
|
||||
|
||||
MOVN . 00 100101 .. ................ ..... @movw_64
|
||||
MOVN . 00 100101 .. ................ ..... @movw_32
|
||||
MOVZ . 10 100101 .. ................ ..... @movw_64
|
||||
MOVZ . 10 100101 .. ................ ..... @movw_32
|
||||
MOVK . 11 100101 .. ................ ..... @movw_64
|
||||
MOVK . 11 100101 .. ................ ..... @movw_32
|
||||
|
||||
# Bitfield
|
||||
|
||||
&bitfield rd rn sf immr imms
|
||||
@bitfield_64 1 .. ...... 1 immr:6 imms:6 rn:5 rd:5 &bitfield sf=1
|
||||
@bitfield_32 0 .. ...... 0 0 immr:5 0 imms:5 rn:5 rd:5 &bitfield sf=0
|
||||
|
||||
SBFM . 00 100110 . ...... ...... ..... ..... @bitfield_64
|
||||
SBFM . 00 100110 . ...... ...... ..... ..... @bitfield_32
|
||||
BFM . 01 100110 . ...... ...... ..... ..... @bitfield_64
|
||||
BFM . 01 100110 . ...... ...... ..... ..... @bitfield_32
|
||||
UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_64
|
||||
UBFM . 10 100110 . ...... ...... ..... ..... @bitfield_32
|
||||
|
||||
# Extract
|
||||
|
||||
&extract rd rn rm imm sf
|
||||
|
||||
EXTR 1 00 100111 1 0 rm:5 imm:6 rn:5 rd:5 &extract sf=1
|
||||
EXTR 0 00 100111 0 0 rm:5 0 imm:5 rn:5 rd:5 &extract sf=0
|
||||
|
||||
# Branches
|
||||
|
||||
%imm26 0:s26 !function=times_4
|
||||
@branch . ..... .......................... &i imm=%imm26
|
||||
|
||||
B 0 00101 .......................... @branch
|
||||
BL 1 00101 .......................... @branch
|
||||
|
||||
%imm19 5:s19 !function=times_4
|
||||
&cbz rt imm sf nz
|
||||
|
||||
CBZ sf:1 011010 nz:1 ................... rt:5 &cbz imm=%imm19
|
||||
|
||||
%imm14 5:s14 !function=times_4
|
||||
%imm31_19 31:1 19:5
|
||||
&tbz rt imm nz bitpos
|
||||
|
||||
TBZ . 011011 nz:1 ..... .............. rt:5 &tbz imm=%imm14 bitpos=%imm31_19
|
||||
|
||||
B_cond 0101010 0 ................... 0 cond:4 imm=%imm19
|
||||
|
||||
BR 1101011 0000 11111 000000 rn:5 00000 &r
|
||||
BLR 1101011 0001 11111 000000 rn:5 00000 &r
|
||||
RET 1101011 0010 11111 000000 rn:5 00000 &r
|
||||
|
||||
&braz rn m
|
||||
BRAZ 1101011 0000 11111 00001 m:1 rn:5 11111 &braz # BRAAZ, BRABZ
|
||||
BLRAZ 1101011 0001 11111 00001 m:1 rn:5 11111 &braz # BLRAAZ, BLRABZ
|
||||
|
||||
&reta m
|
||||
RETA 1101011 0010 11111 00001 m:1 11111 11111 &reta # RETAA, RETAB
|
||||
|
||||
&bra rn rm m
|
||||
BRA 1101011 1000 11111 00001 m:1 rn:5 rm:5 &bra # BRAA, BRAB
|
||||
BLRA 1101011 1001 11111 00001 m:1 rn:5 rm:5 &bra # BLRAA, BLRAB
|
||||
|
||||
ERET 1101011 0100 11111 000000 11111 00000
|
||||
ERETA 1101011 0100 11111 00001 m:1 11111 11111 &reta # ERETAA, ERETAB
|
||||
|
||||
# We don't need to decode DRPS because it always UNDEFs except when
|
||||
# the processor is in halting debug state (which we don't implement).
|
||||
# The pattern is listed here as documentation.
|
||||
# DRPS 1101011 0101 11111 000000 11111 00000
|
@ -13,6 +13,7 @@ gen = [
|
||||
decodetree.process('a32-uncond.decode', extra_args: '--static-decode=disas_a32_uncond'),
|
||||
decodetree.process('t32.decode', extra_args: '--static-decode=disas_t32'),
|
||||
decodetree.process('t16.decode', extra_args: ['-w', '16', '--static-decode=disas_t16']),
|
||||
decodetree.process('a64.decode', extra_args: ['--static-decode=disas_a64']),
|
||||
]
|
||||
|
||||
arm_ss.add(gen)
|
||||
|
@ -6727,6 +6727,7 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
|
||||
intptr_t reg_off;
|
||||
SVEHostPage info;
|
||||
target_ulong addr, in_page;
|
||||
ARMVectorReg scratch;
|
||||
|
||||
/* Skip to the first true predicate. */
|
||||
reg_off = find_next_active(vg, 0, reg_max, esz);
|
||||
@ -6736,6 +6737,11 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Protect against overlap between vd and vm. */
|
||||
if (unlikely(vd == vm)) {
|
||||
vm = memcpy(&scratch, vm, reg_max);
|
||||
}
|
||||
|
||||
/*
|
||||
* Probe the first element, allowing faults.
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -220,6 +220,11 @@ static inline int rsub_8(DisasContext *s, int x)
|
||||
return 8 - x;
|
||||
}
|
||||
|
||||
static inline int shl_12(DisasContext *s, int x)
|
||||
{
|
||||
return x << 12;
|
||||
}
|
||||
|
||||
static inline int neon_3same_fp_size(DisasContext *s, int x)
|
||||
{
|
||||
/* Convert 0==fp32, 1==fp16 into a MO_* value */
|
||||
|
Loading…
Reference in New Issue
Block a user